Somewhat recursive string construction
This commit is contained in:
parent
4dabd6c3a8
commit
3c4921aa54
9 changed files with 1130 additions and 185 deletions
648
src/jvm.rs
648
src/jvm.rs
|
@ -3,14 +3,15 @@ use core::fmt::{Display, Formatter};
|
|||
use std::collections::VecDeque;
|
||||
use std::error::Error as ErrorTrait;
|
||||
|
||||
use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag };
|
||||
use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag, FieldAccessFlagMask };
|
||||
use crate::bytecode::{ Bytecode, Instruction };
|
||||
use crate::classfile;
|
||||
use crate::classfile::{ JavaClassFile, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData };
|
||||
use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData };
|
||||
use crate::classstore;
|
||||
use crate::classstore::ClassStore;
|
||||
use crate::constantpool::{ ConstantPoolInfo, ConstantClassInfo, ConstantUtf8Info, ConstantMethodRefInfo, ConstantNameAndTypeInfo};
|
||||
use crate::heap_area::{ HeapArea, FieldValue, ObjectReference };
|
||||
use crate::iterators::{ ClassMethodIterator, ClassFieldIterator, CompatibleTypesIterator };
|
||||
use crate::stackframe;
|
||||
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
||||
|
||||
|
@ -63,7 +64,124 @@ impl JVM {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[StackValue]) -> Result<(), Error> {
|
||||
fn class_native_class_data() -> JavaClassFile {
|
||||
JavaClassFile {
|
||||
minor_version: 0,
|
||||
major_version: 0,
|
||||
constant_pool: Box::new([
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::NativeClassData".to_string() }),
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 4 }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Object".to_string() }),
|
||||
]
|
||||
),
|
||||
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
||||
this_class: 1,
|
||||
super_class: 3,
|
||||
interfaces: Box::new([]),
|
||||
fields: Box::new([
|
||||
FieldInfo {
|
||||
access_flags: FieldAccessFlagMask {
|
||||
mask: FieldAccessFlag::Public.discriminant()
|
||||
},
|
||||
name: "native_class_descriptor_index".to_string(),
|
||||
descriptor: AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Int(),
|
||||
},
|
||||
attributes: Box::new([]),
|
||||
},
|
||||
]),
|
||||
methods: Box::new([]),
|
||||
attributes: Box::new([]),
|
||||
}
|
||||
}
|
||||
|
||||
fn make_class_class(&mut self, class_name: &str) {
|
||||
let class_object = self.heap_area.make_object(&self.class_store, self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap());
|
||||
let class_data_object = self.heap_area.make_object(&self.class_store, self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap());
|
||||
|
||||
self.heap_area.object_area.set_object_field(
|
||||
class_object,
|
||||
"classData",
|
||||
FieldValue::Reference(class_data_object),
|
||||
self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(),
|
||||
&self.class_store,
|
||||
).unwrap();
|
||||
// set native name index on class data
|
||||
self.heap_area.object_area.set_object_field(
|
||||
class_data_object,
|
||||
"native_class_descriptor_index",
|
||||
FieldValue::Int(self.class_store.add_native_class_name(class_name.to_string()) as i32),
|
||||
self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(),
|
||||
&self.class_store,
|
||||
).unwrap();
|
||||
|
||||
let parsed_class_name = match AbstractTypeDescription::parse_first(class_name).unwrap() {
|
||||
(_, desc) => {
|
||||
assert!(desc.array_level == 0);
|
||||
match desc.kind {
|
||||
AbstractTypeKind::Classname(name) => name,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
};
|
||||
self.class_store.set_class_objectref_by_index(self.class_store.class_idx_from_name(&parsed_class_name).unwrap(), class_object);
|
||||
}
|
||||
|
||||
fn make_array_class(&mut self, element_descriptor_string: &str) {
|
||||
let (chars_consumed, element_descriptor) = AbstractTypeDescription::parse_first(element_descriptor_string).unwrap();
|
||||
assert!(chars_consumed == element_descriptor_string.len());
|
||||
let element_class_ref = if element_descriptor.array_level == 0 {
|
||||
match element_descriptor.kind {
|
||||
AbstractTypeKind::Classname(ref name) => {
|
||||
self.class_store.get_class_objectref_from_index(
|
||||
self.class_store.class_idx_from_name(&name).unwrap()
|
||||
)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
self.class_store.get_array_class_ref(&element_descriptor).unwrap()
|
||||
};
|
||||
|
||||
let array_class_object = self.heap_area.make_object(&self.class_store, 6);
|
||||
let array_class_data_object = self.heap_area.make_object(&self.class_store, 1);
|
||||
let array_type_description = AbstractTypeDescription {
|
||||
array_level: 1 + element_descriptor.array_level,
|
||||
kind: element_descriptor.kind,
|
||||
};
|
||||
// set component type
|
||||
self.heap_area.object_area.set_object_field(
|
||||
array_class_object,
|
||||
"componentType",
|
||||
FieldValue::Reference(element_class_ref),
|
||||
self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(),
|
||||
&self.class_store,
|
||||
).unwrap();
|
||||
// set classdata object
|
||||
self.heap_area.object_area.set_object_field(
|
||||
array_class_object,
|
||||
"classData",
|
||||
FieldValue::Reference(array_class_data_object),
|
||||
self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(),
|
||||
&self.class_store,
|
||||
).unwrap();
|
||||
// set native name index on class data
|
||||
self.heap_area.object_area.set_object_field(
|
||||
array_class_data_object,
|
||||
"native_class_descriptor_index",
|
||||
FieldValue::Int(self.class_store.add_native_class_name((&array_type_description).into()) as i32),
|
||||
self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(),
|
||||
&self.class_store,
|
||||
).unwrap();
|
||||
self.class_store.put_array_class_ref(
|
||||
array_type_description,
|
||||
array_class_object,
|
||||
);
|
||||
}
|
||||
|
||||
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[&str]) -> Result<(), Error> {
|
||||
let entry_class = JavaClassFile {
|
||||
minor_version: 0,
|
||||
major_version: 0,
|
||||
|
@ -72,11 +190,17 @@ impl JVM {
|
|||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::EntryPoint".to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "Code".to_string() }),
|
||||
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 5, name_and_type_index: 6}),
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 7 }),
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 7 }), // 5
|
||||
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 8, descriptor_index: 9 }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: class_name.to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: method_name.to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "()V".to_string() }),
|
||||
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 11, name_and_type_index: 13}), // 10
|
||||
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 12 } ),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/String".to_string() }),
|
||||
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 14, descriptor_index: 15 }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "<init>".to_string() }),
|
||||
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "([B)V".to_string() }), // 15
|
||||
]
|
||||
),
|
||||
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
||||
|
@ -104,7 +228,7 @@ impl JVM {
|
|||
data: AttributeData::Code(
|
||||
CodeAttributeData {
|
||||
max_stack: 0,
|
||||
max_locals: 0,
|
||||
max_locals: 1,
|
||||
code: Bytecode {
|
||||
bytes: Box::new([
|
||||
0xB8_u8.to_be(), // invokestatic
|
||||
|
@ -118,26 +242,92 @@ impl JVM {
|
|||
)
|
||||
}
|
||||
])
|
||||
}
|
||||
},
|
||||
MethodInfo {
|
||||
access_flags: MethodAccessFlagMask {
|
||||
mask: MethodAccessFlag::Public.discriminant() | MethodAccessFlag::Static.discriminant()
|
||||
},
|
||||
name: "make_arg_string".to_string(),
|
||||
descriptor: MethodDescriptor {
|
||||
argument_types: Box::new([]),
|
||||
return_type: AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: AbstractTypeKind::Void(),
|
||||
}
|
||||
},
|
||||
code_attribute_index: 0,
|
||||
attributes: Box::new([
|
||||
AttributeInfo {
|
||||
attribute_name_index: 3,
|
||||
data: AttributeData::Code(
|
||||
CodeAttributeData {
|
||||
max_stack: 3,
|
||||
max_locals: 3,
|
||||
code: Bytecode {
|
||||
bytes: Box::new([
|
||||
0xBB_u8.to_be(), // new
|
||||
11_u16.to_be_bytes()[0], // index 11 into the constant
|
||||
11_u16.to_be_bytes()[1], // pool
|
||||
|
||||
0x59_u8.to_be(), // dup
|
||||
|
||||
0x2A_u8.to_be(), // aload 0
|
||||
|
||||
0xB7_u8.to_be(), // invokespecial
|
||||
10_u16.to_be_bytes()[0], // index 10 into the constant
|
||||
10_u16.to_be_bytes()[1], // pool
|
||||
])
|
||||
},
|
||||
exception_table: Box::new([]),
|
||||
attributes: Box::new([]),
|
||||
}
|
||||
)
|
||||
}
|
||||
])
|
||||
}
|
||||
]),
|
||||
attributes: Box::new([]),
|
||||
};
|
||||
let entry_frame = StackFrame::new(&entry_class, 0, 0, &[]);
|
||||
self.class_store.add_class(entry_class, true)?; // 0
|
||||
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
||||
self.class_store.load_class(&"java/lang/Object".to_string())?; // 2
|
||||
self.class_store.load_class(&"java/lang/Number".to_string())?; // 3
|
||||
self.class_store.load_class(&"java/lang/Byte".to_string())?; // 4
|
||||
self.class_store.load_class(&"java/lang/String".to_string())?; // 5
|
||||
self.class_store.load_class(&"java/lang/Class".to_string())?; // 6
|
||||
|
||||
self.stack_frames.push(
|
||||
StackFrame::new(&entry_class, 0, 0, arguments),
|
||||
);
|
||||
self.make_class_class("Ljava/lang/Byte;");
|
||||
self.make_class_class("Ljava/lang/String;");
|
||||
self.make_array_class("Ljava/lang/Byte;");
|
||||
self.make_array_class("Ljava/lang/String;");
|
||||
|
||||
self.class_store.add_class(entry_class, true);
|
||||
self.class_store.load_class(&"java/lang/Class".to_string())?;
|
||||
self.class_store.load_class(&"java/lang/Object".to_string())?;
|
||||
let argument_array = self.heap_area.make_empty_array(&self.class_store, AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".into()) }, arguments.len());
|
||||
for (arg_index, argument) in arguments.iter().enumerate() {
|
||||
let byte_array = self.heap_area.byte_array_from_rust_string(argument, &self.class_store);
|
||||
self.stack_frames.push(
|
||||
StackFrame::new(
|
||||
self.class_store.get_class(&"::EntryPoint".to_string()).unwrap().0,
|
||||
self.class_store.class_idx_from_name(&"::EntryPoint".to_string()).unwrap(),
|
||||
1, // method index
|
||||
&[StackValue::Reference(byte_array), StackValue::Reference(argument_array), StackValue::Int(arg_index as i32)],
|
||||
)
|
||||
);
|
||||
|
||||
self.new_object(&"java/lang/Class".to_string())?;
|
||||
self.run()?;
|
||||
}
|
||||
|
||||
|
||||
// push the entry frame which will call main
|
||||
self.stack_frames.push(entry_frame);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn run(&mut self) -> Result<(), Error> {
|
||||
while self.stack_frames.len() != 0 {
|
||||
println!("Enter bytecode loop:");
|
||||
|
||||
let jvm_op = self.bytecode_loop()?;
|
||||
match jvm_op {
|
||||
JVMCallbackOperation::PopFrame() => {
|
||||
|
@ -167,6 +357,10 @@ impl JVM {
|
|||
JVMCallbackOperation::InitClass(name) => {
|
||||
// TODO: throw exception
|
||||
self.init_class_hierarchy(&name)?;
|
||||
},
|
||||
|
||||
JVMCallbackOperation::MakeArrayClass(component_descriptor_string) => {
|
||||
self.make_array_class(&component_descriptor_string);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -174,39 +368,25 @@ impl JVM {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn new_object(&mut self, class_name: &String) -> Result<ObjectReference, Error> {
|
||||
let (_, class_idx) = self.class_store.get_class(class_name)?;
|
||||
Ok(self.heap_area.make_object(&mut self.class_store, class_idx))
|
||||
}
|
||||
|
||||
fn load_class_hierarchy(&mut self, name: &String) -> Result<(), Error> {
|
||||
self.class_store.load_class(&name)?;
|
||||
let super_class_name = {
|
||||
let (file, _) = self.class_store.get_class(&name)?;
|
||||
file.get_super_class_name()?.clone()
|
||||
};
|
||||
let mut super_classes = vec![super_class_name];
|
||||
let mut waiting_queue = VecDeque::new();
|
||||
waiting_queue.push_back(name.clone());
|
||||
|
||||
while super_classes.len() != 0 {
|
||||
let current_super = super_classes.pop().unwrap();
|
||||
let have_super_super = {
|
||||
let (super_file, _) = self.class_store.get_class(¤t_super)?;
|
||||
super_file.has_super_class()
|
||||
};
|
||||
while waiting_queue.len() != 0 {
|
||||
let class_name = waiting_queue.pop_front().unwrap();
|
||||
|
||||
if have_super_super {
|
||||
let super_super_name = {
|
||||
let (super_file, _) = self.class_store.get_class(¤t_super)?;
|
||||
super_file.get_super_class_name()?
|
||||
};
|
||||
if self.class_store.have_class(super_super_name) {
|
||||
self.class_store.load_class(¤t_super)?;
|
||||
} else {
|
||||
super_classes.push(current_super);
|
||||
super_classes.push(super_super_name.to_string());
|
||||
if ! self.class_store.have_class(&class_name) {
|
||||
println!("Loading Class {class_name}");
|
||||
self.class_store.load_class(&class_name)?;
|
||||
let (file, _) = self.class_store.get_class(&class_name).unwrap();
|
||||
if file.has_super_class() {
|
||||
waiting_queue.push_back(file.get_super_class_name()?.to_string());
|
||||
}
|
||||
|
||||
for interface_index in &file.interfaces {
|
||||
let interface_name = file.gather_class(*interface_index)?;
|
||||
waiting_queue.push_back(interface_name.to_string());
|
||||
}
|
||||
} else {
|
||||
self.class_store.load_class(¤t_super)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -245,53 +425,125 @@ impl JVM {
|
|||
}
|
||||
|
||||
fn init_class(&mut self, class_idx: usize) -> Result<(), Error> {
|
||||
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
||||
let clinit_idx = class_file.find_method_index(&"<clinit>".to_string());
|
||||
{
|
||||
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
||||
|
||||
self.heap_area.make_static(class_file, class_idx);
|
||||
self.heap_area.make_static(class_file, class_idx);
|
||||
|
||||
for field in &class_file.fields {
|
||||
if field.access_flags & FieldAccessFlag::Static {
|
||||
let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter()
|
||||
.filter(|a| match a.data { AttributeData::ConstantValue(_) => true, _ => false })
|
||||
.map(|a| match &a.data { AttributeData::ConstantValue(c) => c, _ => unreachable!() })
|
||||
.collect();
|
||||
for field in &class_file.fields {
|
||||
if field.access_flags & FieldAccessFlag::Static {
|
||||
let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter()
|
||||
.filter(|a| match a.data { AttributeData::ConstantValue(_) => true, _ => false })
|
||||
.map(|a| match &a.data { AttributeData::ConstantValue(c) => c, _ => unreachable!() })
|
||||
.collect();
|
||||
|
||||
assert!(cvalue_attrs.len() < 2);
|
||||
// TODO: Throw error
|
||||
assert!(cvalue_attrs.len() < 2);
|
||||
// TODO: Throw error
|
||||
|
||||
if cvalue_attrs.len() == 1 {
|
||||
let constant_value_info = cvalue_attrs[0];
|
||||
if cvalue_attrs.len() == 1 {
|
||||
let constant_value_info = cvalue_attrs[0];
|
||||
|
||||
assert!(field.descriptor.array_level == 0);
|
||||
// TODO: Throw Error
|
||||
assert!(field.descriptor.array_level == 0);
|
||||
// TODO: Throw Error
|
||||
|
||||
let field_value = match field.descriptor.kind {
|
||||
AbstractTypeKind::Boolean() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
let field_value = match field.descriptor.kind {
|
||||
AbstractTypeKind::Boolean() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
|
||||
FieldValue::Boolean(int_entry.value != 0)
|
||||
},
|
||||
AbstractTypeKind::Int() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
FieldValue::Boolean(int_entry.value != 0)
|
||||
},
|
||||
AbstractTypeKind::Byte() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
|
||||
FieldValue::Int(int_entry.value)
|
||||
},
|
||||
AbstractTypeKind::Short() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
FieldValue::Byte(int_entry.value as u8)
|
||||
},
|
||||
AbstractTypeKind::Char() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
|
||||
FieldValue::Short(int_entry.value as i16)
|
||||
},
|
||||
_ => todo!()
|
||||
};
|
||||
FieldValue::Char(int_entry.value as u16)
|
||||
},
|
||||
AbstractTypeKind::Int() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
|
||||
self.heap_area.static_area.set(class_file.get_classname()?, &field.name, field_value)?;
|
||||
FieldValue::Int(int_entry.value)
|
||||
},
|
||||
AbstractTypeKind::Short() => {
|
||||
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
||||
|
||||
FieldValue::Short(int_entry.value as i16)
|
||||
},
|
||||
AbstractTypeKind::Long() => {
|
||||
let long_entry = class_file.pool_long_entry(constant_value_info.constant_value_index)?;
|
||||
|
||||
FieldValue::Long(long_entry.value)
|
||||
},
|
||||
AbstractTypeKind::Classname(ref name) => {
|
||||
if name == "java/lang/String" {
|
||||
let string_entry = class_file.gather_string(constant_value_info.constant_value_index)?;
|
||||
|
||||
let (string_class_file, string_class_index) = self.class_store.get_class(name).unwrap();
|
||||
let string_object = self.heap_area.make_object(&self.class_store, string_class_index);
|
||||
// TODO: Review this if it looks like there will be stack
|
||||
// overflows in initialization
|
||||
|
||||
let string_init_function_index = match ClassMethodIterator::new(string_class_index, &self.class_store)
|
||||
.filter(|( cid, _mid, _minfo)| *cid == string_class_index)
|
||||
.filter(|(_cid, _mid, minfo)| minfo.name == "<init>")
|
||||
.filter(|(_cid, _mid, minfo)| minfo.descriptor.argument_types.len() == 1)
|
||||
.filter(|(_cid, _mid, minfo)| minfo.descriptor.argument_types[0] == AbstractTypeDescription { array_level: 1, kind: AbstractTypeKind::Byte() })
|
||||
.next() {
|
||||
Some((_, method_index, _)) => method_index,
|
||||
None => unreachable!(),
|
||||
};
|
||||
|
||||
let byte_array = self.heap_area.byte_array_from_rust_string(string_entry, &self.class_store);
|
||||
|
||||
self.stack_frames.push(
|
||||
StackFrame::new(
|
||||
string_class_file,
|
||||
string_class_index,
|
||||
string_init_function_index as u16,
|
||||
&[StackValue::Reference(byte_array)],
|
||||
)
|
||||
);
|
||||
|
||||
FieldValue::Reference(string_object)
|
||||
} else {
|
||||
todo!()
|
||||
}
|
||||
},
|
||||
|
||||
_ => {
|
||||
println!("{:?}", field.descriptor.kind);
|
||||
todo!()
|
||||
},
|
||||
};
|
||||
|
||||
self.heap_area.static_area.set(class_file.get_classname()?, &field.name, field_value)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Push clinit function
|
||||
// class object
|
||||
self.make_class_class(&("L".to_owned() + &self.class_store.class_file_from_idx(class_idx).unwrap().get_classname()?.clone() + ";"));
|
||||
|
||||
// Push clinit function
|
||||
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
||||
let clinit_idx = class_file.find_method_index(&"<clinit>".to_string());
|
||||
|
||||
if let Some(method_index) = clinit_idx {
|
||||
let clinit_frame = StackFrame::new(
|
||||
class_file,
|
||||
class_idx,
|
||||
method_index as u16,
|
||||
&[],
|
||||
);
|
||||
|
||||
self.stack_frames.push(clinit_frame);
|
||||
}
|
||||
|
||||
// finish
|
||||
self.class_store.set_init(class_idx, true);
|
||||
|
||||
Ok(())
|
||||
|
@ -312,7 +564,67 @@ impl JVM {
|
|||
let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize);
|
||||
frame.instruction_pointer += offset as u32;
|
||||
|
||||
println!("{:25}.{:15}:{:<10}{instruction:?}", class.get_classname().unwrap(), method.name, frame.instruction_pointer);
|
||||
|
||||
match instruction {
|
||||
|
||||
Instruction::Duplicate() => {
|
||||
let popped_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
||||
}
|
||||
|
||||
Instruction::InvokeSpecial(methodref_index) => {
|
||||
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;
|
||||
|
||||
if ! self.class_store.have_class(supplied_class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(supplied_class_name).unwrap() {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::InitClass(supplied_class_name.to_string()));
|
||||
}
|
||||
|
||||
let target_class_index = self.class_store.class_idx_from_name(supplied_class_name).unwrap();
|
||||
|
||||
let parsed_expected_descriptor: MethodDescriptor = MethodDescriptor::try_from(supplied_descriptor_string)?;
|
||||
let (class_index, method_index, method_info) = match ClassMethodIterator::new(target_class_index, &self.class_store)
|
||||
.filter(|(_cid, _mid, minfo)| minfo.name == *supplied_method_name)
|
||||
.filter(|(_cid, _mid, minfo)| minfo.descriptor == parsed_expected_descriptor)
|
||||
.next() {
|
||||
Some(m) => m,
|
||||
None => {
|
||||
// TODO: Throw exception
|
||||
return Err(Error::RunTimeError(format!("InvokeSpecial: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name)));
|
||||
}
|
||||
};
|
||||
|
||||
let mut arguments = VecDeque::new();
|
||||
fill_arguments(class, method, &mut arguments, &method_info.descriptor.argument_types, &mut frame.operand_stack)?;
|
||||
|
||||
// this
|
||||
arguments.push_front(
|
||||
StackValue::Reference(
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?
|
||||
)
|
||||
);
|
||||
|
||||
|
||||
let new_frame = StackFrame::new(
|
||||
self.class_store.class_file_from_idx(class_index).unwrap(),
|
||||
class_index,
|
||||
method_index as u16,
|
||||
&arguments.make_contiguous(),
|
||||
);
|
||||
|
||||
return Ok(JVMCallbackOperation::PushFrame(new_frame));
|
||||
},
|
||||
|
||||
Instruction::InvokeStatic(methodref_index) => {
|
||||
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;
|
||||
|
||||
|
@ -397,18 +709,159 @@ impl JVM {
|
|||
load_local_int(class, method, frame, 3)?;
|
||||
}
|
||||
|
||||
Instruction::LoadLocalReference0() => {
|
||||
load_local_reference(class, method, frame, 0)?;
|
||||
}
|
||||
|
||||
Instruction::MultiplyInt() => {
|
||||
let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(factor_1 * factor_2)))?;
|
||||
}
|
||||
|
||||
Instruction::PushConstInt5() => {
|
||||
let frame_result = frame.operand_stack.push(StackValue::Int(5));
|
||||
match frame_result {
|
||||
Ok(_) => (),
|
||||
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
||||
Instruction::NewArray(class_index) => {
|
||||
// construct single level array
|
||||
let class_name = class.gather_class(class_index)?;
|
||||
|
||||
if ! self.class_store.have_class(class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(class_name).unwrap() {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
return Ok(JVMCallbackOperation::InitClass(class_name.to_string()));
|
||||
}
|
||||
|
||||
let class_descriptor = AbstractTypeDescription {
|
||||
array_level: 0,
|
||||
kind: match class_name {
|
||||
_ => AbstractTypeKind::Classname(class_name.to_string())
|
||||
}
|
||||
};
|
||||
|
||||
let array_descriptor = AbstractTypeDescription {
|
||||
array_level: 1,
|
||||
kind: class_descriptor.kind.clone(),
|
||||
};
|
||||
|
||||
if let None = self.class_store.get_array_class_ref(&array_descriptor) {
|
||||
let complete_type_descriptor = format!("L{};", class_name);
|
||||
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::MakeArrayClass(complete_type_descriptor));
|
||||
}
|
||||
|
||||
let array_capacity = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
let array_ref = self.heap_area.make_empty_array(&self.class_store, class_descriptor, array_capacity as usize);
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(array_ref)))?;
|
||||
}
|
||||
|
||||
Instruction::NewObject(class_index) => {
|
||||
let class_name = class.gather_class(class_index)?;
|
||||
|
||||
if ! self.class_store.have_class(class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(class_name).unwrap() {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::InitClass(class_name.to_string()));
|
||||
}
|
||||
|
||||
let class_index = self.class_store.class_idx_from_name(class_name).unwrap();
|
||||
let new_object = self.heap_area.make_object(&self.class_store, class_index);
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(new_object)))?;
|
||||
},
|
||||
|
||||
Instruction::PushConstInt0() => {
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(0)))?;
|
||||
}
|
||||
Instruction::PushConstInt1() => {
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(1)))?;
|
||||
}
|
||||
Instruction::PushConstInt2() => {
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(2)))?;
|
||||
}
|
||||
Instruction::PushConstInt3() => {
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(3)))?;
|
||||
}
|
||||
Instruction::PushConstInt4() => {
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(4)))?;
|
||||
}
|
||||
Instruction::PushConstInt5() => {
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(5)))?;
|
||||
}
|
||||
|
||||
Instruction::PutStatic(fieldref_index) => {
|
||||
let (target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
||||
|
||||
if ! self.class_store.have_class(target_class_name) {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::LoadClass(target_class_name.to_string()));
|
||||
}
|
||||
if ! self.class_store.was_init(target_class_name).unwrap() {
|
||||
// rewind the bytecode offset, I'll need to execute this instruction again
|
||||
frame.instruction_pointer -= offset as u32;
|
||||
|
||||
return Ok(JVMCallbackOperation::InitClass(target_class_name.to_string()));
|
||||
}
|
||||
|
||||
let matched_field = match ClassFieldIterator::new(self.class_store.class_idx_from_name(target_class_name).unwrap(), &self.class_store)
|
||||
.filter(|f| f.name == *target_field_name)
|
||||
.filter(|f| f.access_flags & FieldAccessFlag::Static)
|
||||
.next() {
|
||||
Some(f) => f,
|
||||
None => return Err(Error::RunTimeError(format!("PutStatic: Trying to set field '{}' on class '{}' but there is no such static field", target_field_name, target_class_name)))
|
||||
};
|
||||
|
||||
|
||||
let (consumed_chars, expected_descriptor) = AbstractTypeDescription::parse_first(expected_field_descriptor)?;
|
||||
assert!(expected_field_descriptor.len() == consumed_chars);
|
||||
|
||||
// TODO: Throw exception on fail
|
||||
if matched_field.descriptor != expected_descriptor {
|
||||
return Err(Error::RunTimeError(format!("PutStatic: Field descriptor mismatch: '{expected_descriptor:?}' but found '{:?}'", matched_field.descriptor)))
|
||||
}
|
||||
|
||||
let set_value = match (matched_field.descriptor.array_level, &matched_field.descriptor.kind) {
|
||||
|
||||
(0, AbstractTypeKind::Boolean()) => {
|
||||
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
FieldValue::Boolean((int_value & 1) != 0)
|
||||
},
|
||||
|
||||
(0..=255, AbstractTypeKind::Classname(_field_type_name)) => {
|
||||
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
let value_native_name = self.heap_area.object_area.get_reference_native_class_name(ref_value, &self.class_store);
|
||||
let parsed_native_name = AbstractTypeDescription::parse_first(value_native_name).unwrap().1;
|
||||
|
||||
if ! self.class_store.are_types_compatible(&matched_field.descriptor, &parsed_native_name) {
|
||||
return Err(Error::RunTimeError(format!("PutStatic: Trying to set a value with type '{parsed_native_name:?}' on a field with type '{:?}'", matched_field.descriptor)));
|
||||
}
|
||||
|
||||
FieldValue::Reference(ref_value)
|
||||
}
|
||||
_ => {
|
||||
println!("{:?}", matched_field);
|
||||
todo!()
|
||||
},
|
||||
};
|
||||
|
||||
self.heap_area.static_area.set(target_class_name, target_field_name, set_value)?;
|
||||
}
|
||||
|
||||
Instruction::ReturnInt() => {
|
||||
|
@ -443,19 +896,19 @@ impl JVM {
|
|||
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?;
|
||||
},
|
||||
Instruction::StoreLocalInt1() => {
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(1))?;
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?;
|
||||
wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Int(int)))?;
|
||||
},
|
||||
Instruction::StoreLocalInt2() => {
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(2))?;
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?;
|
||||
wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Int(int)))?;
|
||||
},
|
||||
Instruction::StoreLocalInt3() => {
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(3))?;
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?;
|
||||
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Int(int)))?;
|
||||
},
|
||||
|
||||
_ => {
|
||||
|
@ -477,22 +930,23 @@ enum JVMCallbackOperation {
|
|||
PushFrame(StackFrame),
|
||||
LoadClass(String),
|
||||
InitClass(String),
|
||||
MakeArrayClass(String),
|
||||
}
|
||||
|
||||
fn load_local_reference(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> {
|
||||
let loaded_value = wrap_stackframe_error(class, method, frame.load_local_reference(index as u16))?;
|
||||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(loaded_value)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_local_int(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> {
|
||||
let frame_result = frame.load_local_int(index as u16);
|
||||
let local_int = match frame_result {
|
||||
Ok(i) => {
|
||||
i
|
||||
},
|
||||
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
||||
};
|
||||
let loaded_value = wrap_stackframe_error(class, method, frame.load_local_int(index as u16))?;
|
||||
|
||||
let frame_result = frame.operand_stack.push(StackValue::Int(local_int));
|
||||
match frame_result {
|
||||
Ok(_) => Ok(()),
|
||||
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
||||
}
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(loaded_value)))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut VecDeque<StackValue>, argument_types: &Box<[AbstractTypeDescription]>, stack: &mut OperandStack) -> Result<(), Error> {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue