use core::fmt::{Display, Formatter}; use std::collections::VecDeque; use std::error::Error as ErrorTrait; use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag, FieldAccessFlagMask }; use crate::bytecode::{ Bytecode, Instruction }; use crate::classfile; 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, CompartmentEntry }; use crate::iterators::{ ClassMethodIterator, ClassFieldIterator }; use crate::native_methods; use crate::native_methods::EntryPoint; use crate::native_registry::NativeRegistry; use crate::stackframe; use crate::stackframe::{ StackFrame, StackValue, OperandStack }; #[derive(Debug)] pub enum Error { ClassStoreError(classstore::Error), ClassFileError(classfile::Error), StackFrameError(stackframe::Error, String), BadNameError(String), RunTimeError(String), OpcodeError(String), } impl From for Error { fn from(value: classfile::Error) -> Self { return Error::ClassFileError(value); } } impl From for Error { fn from(value: classstore::Error) -> Self { return Error::ClassStoreError(value); } } impl ErrorTrait for Error {} impl Display for Error { fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> { writeln!(formatter, "{self}")?; if let Some(e) = self.source() { writeln!(formatter, "\tCaused by: {e:?}")?; } Ok(()) } } #[derive(Debug)] pub struct JVM { pub class_store: ClassStore, pub stack_frames: Vec, pub heap_area: HeapArea, pub native_registry: NativeRegistry, } impl JVM { pub fn new() -> Self { return JVM { class_store: ClassStore::new(), stack_frames: Vec::new(), heap_area: HeapArea::new(usize::MAX), native_registry: NativeRegistry::default(), } } fn load_class(&mut self, class_name: &String) -> Result { let class_index = self.class_store.load_class(class_name)?; let class_file = self.class_store.class_file_from_idx(class_index).unwrap(); for (method_name, method_implementation) in class_file.methods.iter() .filter(|m| m.access_flags & MethodAccessFlag::Native) .map(| m | (&m.name, native_methods::function_for(class_name, m))) { let method_implementation = match method_implementation { Ok(m) => m, Err(e) => return Err(e) }; self.native_registry.register(class_name, method_name, method_implementation); } Ok(class_index) } 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_descriptor(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_class_ref: ObjectReference, element_descriptor: AbstractTypeDescription) { let class_class_index = self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).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), class_class_index, &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), class_class_index, &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_descriptor((&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, ); } fn make_primitive_class(&mut self, class_name: &str, class_descriptor: &str, string_names: bool) -> ObjectReference { let class_class_index = self.class_store.class_idx_from_name(&String::from("java/lang/Class")).unwrap(); let data_class_index = self.class_store.class_idx_from_name(&String::from("::NativeClassData")).unwrap(); let primitive_class_object = self.heap_area.make_object(&self.class_store, class_class_index); let primitive_class_data_object = self.heap_area.make_object(&self.class_store, data_class_index); // set classdata object self.heap_area.object_area.set_object_field( primitive_class_object, "classData", FieldValue::Reference(primitive_class_data_object), class_class_index, &self.class_store, ).unwrap(); if string_names { let name_string_ref = self.heap_area.make_handmade_string(&class_name.into(), &self.class_store); // set name string object self.heap_area.object_area.set_object_field( primitive_class_object, "name", FieldValue::Reference(name_string_ref), class_class_index, &self.class_store, ).unwrap(); } // set native name index on class data self.heap_area.object_area.set_object_field( primitive_class_data_object, "native_class_descriptor_index", FieldValue::Int(self.class_store.add_native_class_descriptor(class_descriptor.into()) as i32), self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(), &self.class_store, ).unwrap(); // TODO: Set Static primitive_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, constant_pool: Box::new([ ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }), 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 }), // 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: "([Ljava/lang/String;)V".to_string() }), ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 1, name_and_type_index: 11}), // 10 ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 12, descriptor_index: 13 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "populateUnsafeConstants".to_string() }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "()V".to_string() }), ConstantPoolInfo::Class(ConstantClassInfo { name_index: 15 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "jdk/internal/misc/UnsafeConstants".to_string() }), // 15 ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 17, name_and_type_index: 19}), ConstantPoolInfo::Class(ConstantClassInfo { name_index: 18 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/System".to_string() }), ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 20, descriptor_index: 13 }), ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }), ] ), access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() }, this_class: 1, super_class: 0, interfaces: Box::new([]), fields: Box::new([]), methods: Box::new([ MethodInfo { access_flags: MethodAccessFlagMask { mask: MethodAccessFlag::Private.discriminant() | MethodAccessFlag::Static.discriminant() }, name: "call_main".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: 1, max_locals: 1, code: Bytecode { bytes: Box::new([ // access something from UnsafeConstants 0x12_u8.to_be(), // ldc 0x0E_u8.to_be(), // index 14 into the constant pool 0x57_u8.to_be(), // pop // Update UnsafeConstants to actual values 0xb8_u8.to_be(), // invokestatic 0x0A_u16.to_be_bytes()[0], // index 10 into the constant 0x0A_u16.to_be_bytes()[1], // pool // call initPhase1 0xb8_u8.to_be(), // invokestatic 0x10_u16.to_be_bytes()[0], // index 10 into the constant 0x10_u16.to_be_bytes()[1], // pool 0x2A_u8.to_be(), // aload_0 0xB8_u8.to_be(), // invokestatic 0x04_u16.to_be_bytes()[0], // index 4 into the constant 0x04_u16.to_be_bytes()[1], // pool ]), }, exception_table: Box::new([]), attributes: Box::new([]), } ) } ]) }, MethodInfo { access_flags: MethodAccessFlagMask { mask: MethodAccessFlag::Private.discriminant() | MethodAccessFlag::Static.discriminant() | MethodAccessFlag::Native.discriminant() }, name: "populateUnsafeConstants".to_string(), descriptor: MethodDescriptor { argument_types: Box::new([]), return_type: AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void(), } }, code_attribute_index: 0, attributes: Box::new([]) } ]), attributes: Box::new([]), }; self.native_registry.register("::EntryPoint", "populateUnsafeConstants", EntryPoint::populate_unsafe_constants); self.class_store.add_class(entry_class, true)?; // 0 self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1 self.load_class(&"java/lang/Object".to_string())?; // 2 self.load_class(&"java/lang/Number".to_string())?; // 3 let byte_class_index = self.load_class(&"java/lang/Byte".to_string())?; // 4 let string_class_index = self.load_class(&"java/lang/String".to_string())?; // 5 let class_class_index = self.load_class(&"java/lang/Class".to_string())?; // 6 let system_class_index = self.load_class(&"java/lang/System".to_string())?; // 7 self.make_class_class("Ljava/lang/Byte;"); self.make_class_class("Ljava/lang/String;"); self.make_array_class( self.class_store.get_class_objectref_from_index(byte_class_index), AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/Byte".into()), } ); self.make_array_class( self.class_store.get_class_objectref_from_index(string_class_index), AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname("java/lang/String".into()), } ); self.heap_area.fill_byte_cache(&self.class_store); self.class_store.primitive_classes.byte_class = self.make_primitive_class("byte", "B", false); self.make_array_class( self.class_store.primitive_classes.byte_class, AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Byte(), } ); { // we can only make a byte class name after the byte array is loaded let string_ref = self.heap_area.make_handmade_string(&String::from("byte"), &self.class_store); self.heap_area.object_area.set_object_field( self.class_store.primitive_classes.byte_class, "name", FieldValue::Reference(string_ref), class_class_index, &self.class_store ).unwrap(); } self.class_store.primitive_classes.int_class = self.make_primitive_class("int", "I", true); self.class_store.primitive_classes.char_class = self.make_primitive_class("char", "C", true); self.class_store.primitive_classes.long_class = self.make_primitive_class("long", "J", true); self.class_store.primitive_classes.float_class = self.make_primitive_class("float", "F", true); self.class_store.primitive_classes.short_class = self.make_primitive_class("short", "S", true); self.class_store.primitive_classes.double_class = self.make_primitive_class("double", "D", true); self.class_store.primitive_classes.boolean_class = self.make_primitive_class("boolean", "Z", true); self.make_array_class( self.class_store.primitive_classes.char_class, AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Char(), } ); self.make_array_class( self.class_store.primitive_classes.int_class, AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Int(), } ); let string_refs = arguments.iter() .map(|s| self.heap_area.make_handmade_string(&s.to_string(), &self.class_store)) .collect(); let argument_array_ref = self.heap_area.make_array(&self.class_store, string_refs); // push the entry frame which will call main let entry_frame = StackFrame::new(self.class_store.get_class(&String::from("::EntryPoint")).unwrap().0, 0, 0, &[StackValue::Reference(argument_array_ref)]); self.stack_frames.push(entry_frame); Ok(()) } pub fn run(&mut self) -> Result<(), Error> { while self.stack_frames.len() != 0 { let jvm_op = self.bytecode_loop()?; match jvm_op { JVMCallbackOperation::PopFrame() => { self.stack_frames.truncate(self.stack_frames.len() - 1) }, JVMCallbackOperation::ReturnFrame(value) => { // Pop returning frame self.stack_frames.truncate(self.stack_frames.len() - 1); let frame = { let frame_index = self.stack_frames.len() - 1; &mut self.stack_frames[frame_index] }; let class = self.class_store.class_file_from_idx(frame.class_index).unwrap(); let method = & class.methods[frame.method_index as usize]; wrap_stackframe_error(class, method, self.stack_frames.last_mut().unwrap().operand_stack.push(value))?; } JVMCallbackOperation::PushFrame(frame) => self.stack_frames.push(frame), JVMCallbackOperation::LoadClass(name) => { // TODO: throw exception self.load_class_hierarchy(&name)?; }, JVMCallbackOperation::InitClass(name) => { // TODO: throw exception self.init_class_hierarchy(&name)?; }, JVMCallbackOperation::MakeArrayClass(component_class_ref, component_descriptor) => { self.make_array_class(component_class_ref, component_descriptor); } } } Ok(()) } fn load_class_hierarchy(&mut self, name: &String) -> Result<(), Error> { let mut waiting_queue = VecDeque::new(); waiting_queue.push_back(name.clone()); while waiting_queue.len() != 0 { let class_name = waiting_queue.pop_front().unwrap(); if ! self.class_store.have_class(&class_name) { println!("Loading Class {class_name}"); self.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()); } } } Ok(()) } fn init_class_hierarchy(&mut self, name: &String) -> Result<(), Error> { let mut class_stack = vec![name.to_string()]; while class_stack.len() != 0 { let current_name = class_stack.pop().unwrap(); let was_super_init = { let (file, _) = self.class_store.get_class(¤t_name)?; if ! file.has_super_class() { true } else { let super_name = file.get_super_class_name()?; self.class_store.was_init(super_name).unwrap() } }; if was_super_init { let class_idx = self.class_store.class_idx_from_name(¤t_name).unwrap(); self.init_class(class_idx)?; } else { let super_name = { let (file, _) = self.class_store.get_class(¤t_name)?; file.get_super_class_name()? }; class_stack.push(current_name); class_stack.push(super_name.to_string()); } } Ok(()) } fn init_class(&mut self, class_idx: usize) -> Result<(), Error> { { let class_file = self.class_store.class_file_from_idx(class_idx).unwrap(); 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(); assert!(cvalue_attrs.len() < 2); // TODO: Throw error if cvalue_attrs.len() == 1 { let constant_value_info = cvalue_attrs[0]; 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)?; FieldValue::Boolean(int_entry.value != 0) }, AbstractTypeKind::Byte() => { let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; FieldValue::Byte(int_entry.value as i8) }, AbstractTypeKind::Char() => { let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?; FieldValue::Char(int_entry.value as u16) }, AbstractTypeKind::Int() => { 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::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::Float() => { let float_entry = class_file.pool_float_entry(constant_value_info.constant_value_index)?; FieldValue::Float(float_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_object = self.heap_area.make_handmade_string(string_entry, &self.class_store); 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)?; } } } } // 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(&"".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(()) } fn native_call(&mut self) -> Result { let frame = { let frame_index = self.stack_frames.len() - 1; &mut self.stack_frames[frame_index] }; let class = self.class_store.class_file_from_idx(frame.class_index).unwrap(); let method = & class.methods[frame.method_index as usize]; let class_name = class.get_classname().unwrap(); let native_method = match self.native_registry.get(class_name, &method.name) { Some(m) => m, None => { return Err(Error::RunTimeError(format!("Tried to call native method '{class_name}.{}' but there is no such method in the method registry", method.name))); } }; native_method(self) } fn bytecode_loop(&mut self) -> Result { //println!("Enter bytecode loop:"); let frame_index = self.stack_frames.len() - 1; let frame = &mut self.stack_frames[frame_index]; let class = self.class_store.class_file_from_idx(frame.class_index).unwrap(); let method = & class.methods[frame.method_index as usize]; if method.access_flags & MethodAccessFlag::Native { println!("{:25}.{:15}: (native)", class.get_classname().unwrap(), method.name); return self.native_call() } let code_attr = method.get_code_attribute().unwrap(); let bytecode = & code_attr.code; while frame.instruction_pointer as usize != bytecode.bytes.len() { let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize); frame.instruction_pointer += offset as u32; println!("{}{:25}.{:15}:{:<10}{instruction:?}", " ".repeat(frame_index), class.get_classname().unwrap(), method.name, frame.instruction_pointer); match instruction { Instruction::AddInt() => { let value_0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_0 + value_1)))?; } Instruction::ArithmeticShiftIntRight() => { let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 & 0b00011111; let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; // rust does arithmetic shift on singed values wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int >> shift)))?; }, Instruction::ArrayLength() => { let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let array_length = self.heap_area.object_area.get_array_length(array_reference); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(array_length as i32)))?; }, Instruction::ArrayElement() => { let element_index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let element = self.heap_area.object_area.get_array_element(array_reference, element_index); wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(element))?; } Instruction::AndInt() => { let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_1 & value_2)))?; } Instruction::BranchAlways(branch_offset) => { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } Instruction::BranchIntEquality(branch_offset) => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if value_1 == value_2 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchIntGreaterEquals(branch_offset) => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if value_1 >= value_2 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchIntInequality(branch_offset) => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if value_1 != value_2 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchIntLessEquals(branch_offset) => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if value_1 <= value_2 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchIntLessThan(branch_offset) => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if value_1 < value_2 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchNonNull(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; if test_value != ObjectReference::NULL { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchNonNegative(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if test_value >= 0 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchNonPositive(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if test_value <= 0 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchNonZero(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if test_value != 0 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchNull(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; if test_value == ObjectReference::NULL { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchPositive(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if test_value > 0 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchReferenceInequality(branch_offset) => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; if value_1 != value_2 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::BranchZero(branch_offset) => { let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; if test_value == 0 { frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32}; } } Instruction::CheckCast(classref_index) => { // TODO: Class loading checks let class_name = class.gather_class(classref_index)?; let object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store); let instruction_result = if class_name == native_class_name { 1 } else { let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); let class_object_ref = self.class_store.get_class_objectref_from_index(class_index); let native_class_description = self.heap_area.object_area.get_class_ref_native_class_name(class_object_ref, &self.class_store); let class_description = AbstractTypeDescription::parse_full(native_class_description)?; let object_description = AbstractTypeDescription::parse_full(native_class_name)?; if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 } }; if instruction_result == 0 { // TODO: Throw Exception return Err(Error::RunTimeError(format!("Trying to cast an object of type {native_class_name} to {class_name}"))) } wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(object)))?; } Instruction::CompareFloatG() => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let comparison_result = if value_1.is_nan() || value_2.is_nan() { 1 } else if value_1 == value_2 { 0 } else if value_1 < value_2 { -1 } else { 1 }; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?; } Instruction::CompareFloatL() => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let comparison_result = if value_1.is_nan() || value_2.is_nan() { -1 } else if value_1 == value_2 { 0 } else if value_1 < value_2 { -1 } else { 1 }; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?; } Instruction::DivideInt() => { // TODO: Obey all the rules let quotient = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let divident = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; frame.operand_stack.push(StackValue::Int(divident / quotient)).unwrap(); } 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::EnterMonitor() => { // TODO: Revisit this when doing multi-threading let _monitored_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; } Instruction::ExitMonitor() => { // TODO: Revisit this when doing multi-threading let _monitored_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; } Instruction::GetField(fieldref_index) => { let (class_name, field_name, field_descriptor) = class.gather_fieldref(fieldref_index).unwrap(); let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; // TODO: Are there any methods callable on arrays? let this_object_class_index = self.heap_area.object_area.get_object_class_index(this_object); let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap().to_string(); let this_object_descriptor = AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname(this_object_class_name.clone()) }; let object_descriptor = AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname(class_name.to_string()) }; if ! self.class_store.are_types_compatible(&this_object_descriptor, &object_descriptor) { return Err(Error::RunTimeError(format!( "GetField: Cannot fetch '{field_name}' from class '{class_name}' on instance from class '{}: Types are incompatible'", this_object_class_name ))) } let fetched_value = self.heap_area.object_area.get_object_field(this_object, field_name, frame.class_index, &self.class_store).unwrap(); // println!("{fetched_value:?} {field_descriptor}"); // TODO: Check field and value compatability wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?; } Instruction::GetStatic(fieldref_index) => { let (class_name, field_name, field_descriptor) = class.gather_fieldref(fieldref_index).unwrap(); 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())); } // TODO: Throw error let parsed_field_descriptor = AbstractTypeDescription::parse_full(field_descriptor).unwrap(); // TODO: Throw error let fetched_value = self.heap_area.static_area.get(class_name, field_name, parsed_field_descriptor).unwrap(); wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?; } Instruction::IncrementLocalInt(index, constant) => { let int = wrap_stackframe_error(class, method, frame.load_local_int(index as u16))?; wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Int(int + constant as i32)))?; } Instruction::InstanceOf(classref_index) => { // TODO: Class loading checks let class_name = class.gather_class(classref_index)?; let object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store); let instruction_result = if class_name == native_class_name { 1 } else { let class_index = self.class_store.class_idx_from_name(class_name).unwrap(); let class_object_ref = self.class_store.get_class_objectref_from_index(class_index); let native_class_description = self.heap_area.object_area.get_class_ref_native_class_name(class_object_ref, &self.class_store); let class_description = AbstractTypeDescription::parse_full(native_class_description)?; let object_description = AbstractTypeDescription::parse_full(native_class_name)?; if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 } }; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(instruction_result)))?; } Instruction::InvokeInterface(methodref_index) => { let (supplied_interface_name, supplied_method_name, supplied_descriptor_string) = class.gather_interfacemethodref(methodref_index)?; if ! self.class_store.have_class(supplied_interface_name) { frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::LoadClass(supplied_interface_name.to_string())); } if ! self.class_store.was_init(supplied_interface_name).unwrap() { frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::InitClass(supplied_interface_name.to_string())); } let target_interface_class_index = self.class_store.class_idx_from_name(supplied_interface_name).unwrap(); let parsed_expected_descriptor: MethodDescriptor = MethodDescriptor::try_from(supplied_descriptor_string)?; let (class_index, method_index, method_info) = match ClassMethodIterator::new(target_interface_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!("InvokeInterface: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_interface_name))); } }; let mut arguments = VecDeque::new(); fill_arguments(class, method, &mut arguments, &method_info.descriptor.argument_types, &mut frame.operand_stack)?; let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; arguments.push_front(StackValue::Reference(this_object)); let object_class_index = self.heap_area.object_area.get_object_class_index(this_object); let object_class_file = self.class_store.class_file_from_idx(object_class_index).unwrap(); let object_class_name = object_class_file.get_classname().unwrap(); if object_class_name != supplied_interface_name { let object_class_description = AbstractTypeDescription::for_class_name(object_class_name); let supplied_interface_description = AbstractTypeDescription::for_class_name(supplied_interface_name); if ! self.class_store.are_types_compatible(&object_class_description, &supplied_interface_description) { return Err(Error::RunTimeError(format!("Tried to invoke interface method : '{supplied_interface_name}.{supplied_method_name}' on object of type '{object_class_name}'"))); } } // TODO: I hate this, now I have to find the 'implementation' of a function let interface_class_file = self.class_store.class_file_from_idx(target_interface_class_index).unwrap(); let interface_frame = StackFrame::new( interface_class_file, target_interface_class_index, method_index as u16, arguments.make_contiguous() ); return Ok(JVMCallbackOperation::PushFrame(interface_frame)); } Instruction::InvokeSpecial(methodref_index) => { // No instance-based dispatch 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)?; 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 (callee_class_file, callee_class_index) = self.class_store.get_class(supplied_class_name)?; // TODO: Throw exception on fail let callee_method_index = callee_class_file.find_method_index(supplied_method_name).unwrap(); // TODO: Throw exception on fail let callee_method_info = &callee_class_file.methods[callee_method_index]; if ! (callee_method_info.access_flags & MethodAccessFlag::Static) { // TODO: Throw IncompatibleClassChangeError return Err(Error::RunTimeError(format!( "Invoked method '{}' in class '{}' does not have Access::Static (from invokestatic from '{}' in class '{}')", method.name, class.get_classname().unwrap(), supplied_method_name, supplied_class_name, ))); } let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?; // TODO: Throw exception on fail if supplied_descriptor != callee_method_info.descriptor { // TODO: Throw exception on fail return Err(Error::RunTimeError(format!( "Mismatched method descriptors between caller and callee: Caller ({}) wanted '{}' but found '{}' on Callee ({})", class.get_classname().unwrap(), supplied_descriptor_string, callee_method_info.descriptor.source_string(), supplied_class_name, ))); } let mut arguments = VecDeque::new(); fill_arguments(class, method, &mut arguments, &callee_method_info.descriptor.argument_types, &mut frame.operand_stack)?; // TODO: Throw errors on abstract methods etc. let new_frame = StackFrame::new( callee_class_file, callee_class_index, callee_method_index as u16, &arguments.make_contiguous(), ); return Ok(JVMCallbackOperation::PushFrame(new_frame)); }, Instruction::InvokeVirtual(methodref_index) => { let (base_class_name, base_method_name, base_descriptor_string) = class.gather_methodref(methodref_index)?; if ! self.class_store.have_class(base_class_name) { // rewind the bytecode offset, I'll need to execute this instruction again frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::LoadClass(base_class_name.to_string())); } if ! self.class_store.was_init(base_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(base_class_name.to_string())); } let base_descriptor = MethodDescriptor::try_from(base_descriptor_string).unwrap(); // extract arguments, they are on top of the stack let mut arguments = VecDeque::new(); fill_arguments(class, method, &mut arguments, &base_descriptor.argument_types, &mut frame.operand_stack)?; let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; arguments.push_front(StackValue::Reference(this_object)); // TODO: Are there any methods callable on arrays? let this_object_class_index = self.heap_area.object_area.get_object_class_index(this_object); let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap().to_string(); let this_object_descriptor = AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname(this_object_class_name.clone()) }; let base_object_descriptor = AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Classname(base_class_name.to_string()) }; // TODO: Throw error if ! self.class_store.are_types_compatible(&this_object_descriptor, &base_object_descriptor) { return Err(Error::RunTimeError(format!("InvokeVirtual: Cannot call '{base_method_name}' from class '{base_class_name}' on instance from class '{this_object_class_name}: Types are incompatible'"))) } let (invoked_class_index, invoked_method_index, _invoked_method_info) = match ClassMethodIterator::new(this_object_class_index, &self.class_store) .filter(|(_, _, method_info)| method_info.name == *base_method_name) .filter(|(_, _, method_info)| method_info.descriptor == base_descriptor) .next() { Some(t) => t, None => { return Err(Error::RunTimeError(format!( "InvokeVirtual: Failed to find requested method '{}' with descriptor '{}' in the type hierarchy of '{}'", base_method_name, base_descriptor_string, this_object_class_name, ))); }, }; let invoked_class_file = self.class_store.class_file_from_idx(invoked_class_index).unwrap(); let new_frame = StackFrame::new( invoked_class_file, invoked_class_index, invoked_method_index as u16, arguments.make_contiguous() ); return Ok(JVMCallbackOperation::PushFrame(new_frame)); }, Instruction::LoadByteImmediate(byte) => { // sign extend into int let frame_result = frame.operand_stack.push(StackValue::Int(byte as i32)); match frame_result { Ok(_) => (), Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))), } } Instruction::LoadShortImmediate(short) => { // sign extend into int let frame_result = frame.operand_stack.push(StackValue::Int(short as i32)); match frame_result { Ok(_) => (), Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))), } } Instruction::LoadFromBArray() => { let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let array = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let element = self.heap_area.object_area.get_array_element(array, index); wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(element))?; } Instruction::LoadConstant(index) => { // TODO: Handle error instead of unwrap match class.pool_entry(index as u16).unwrap() { ConstantPoolInfo::Class(_) => { let class_name = class.gather_class(index as u16).unwrap(); if ! self.class_store.have_class(class_name) { frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::LoadClass(class_name.to_string())); } if ! self.class_store.was_init(class_name).unwrap() { 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 class_object_ref = self.class_store.get_class_objectref_from_index(class_index); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(class_object_ref)))?; } ConstantPoolInfo::String(_) => { // TODO: Handle error instead of unwrap let string_constant = class.gather_string(index as u16).unwrap(); let string_obj_ref = self.heap_area.make_handmade_string(string_constant, &self.class_store); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?; } ConstantPoolInfo::Float(_) => { // TODO: Handle error instead of unwrap let float_constant = class.gather_float(index as u16).unwrap(); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(float_constant)))?; } ConstantPoolInfo::Integer(int_data) => { // TODO: Handle error instead of unwrap let int_constant = int_data.value; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_constant)))?; } _ => { println!("{:?}", class.pool_entry(index as u16).unwrap()); todo!() } } }, Instruction::LoadCostantWide(wide_index) => { // TODO: Handle error instead of unwrap match class.pool_entry(wide_index).unwrap() { ConstantPoolInfo::Class(_) => { let class_name = class.gather_class(wide_index).unwrap(); let class_object_ref = if class_name.starts_with('[') { let component_name = class_name.trim_start_matches('['); let array_level = class_name.len() - component_name.len(); let array_type_desc = AbstractTypeDescription { array_level: array_level as u8, kind: component_name.into(), }; if let Some(array_ref) = self.class_store.get_array_class_ref(&array_type_desc) { array_ref } else { let mut test_type = array_type_desc.super_component(); let mut test_object_ref = self.class_store.class_ref_for_type(test_type.clone()).unwrap(); while let Some(new_test_object_ref) = self.class_store.class_ref_for_type(test_type.array()) { test_type = test_type.array(); test_object_ref = new_test_object_ref; } frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::MakeArrayClass(test_object_ref, test_type)) } } else { if ! self.class_store.have_class(class_name) { frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::LoadClass(class_name.to_string())); } if ! self.class_store.was_init(class_name).unwrap() { 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(); self.class_store.get_class_objectref_from_index(class_index) }; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(class_object_ref)))?; } ConstantPoolInfo::String(_) => { // TODO: Handle error instead of unwrap let string_constant = class.gather_string(wide_index).unwrap(); let string_obj_ref = self.heap_area.make_handmade_string(string_constant, &self.class_store); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?; } ConstantPoolInfo::Integer(int_data) => { let int_value = int_data.value; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_value)))?; } _ => { println!("{:?}", class.pool_entry(wide_index).unwrap()); todo!() } } } Instruction::LoadLocalFloat0() => { load_local_float(class, method, frame, 0)?; } Instruction::LoadLocalFloat1() => { load_local_float(class, method, frame, 1)?; } Instruction::LoadLocalFloat2() => { load_local_float(class, method, frame, 2)?; } Instruction::LoadLocalFloat3() => { load_local_float(class, method, frame, 3)?; } Instruction::LoadLocalInt(index) => { load_local_int(class, method, frame, index as usize)?; } Instruction::LoadLocalInt0() => { load_local_int(class, method, frame, 0)?; } Instruction::LoadLocalInt1() => { load_local_int(class, method, frame, 1)?; } Instruction::LoadLocalInt2() => { load_local_int(class, method, frame, 2)?; } Instruction::LoadLocalInt3() => { load_local_int(class, method, frame, 3)?; } Instruction::LoadLocalReference(index) => { load_local_reference(class, method, frame, index as usize)?; } Instruction::LoadLocalReference0() => { load_local_reference(class, method, frame, 0)?; } Instruction::LoadLocalReference1() => { load_local_reference(class, method, frame, 1)?; } Instruction::LoadLocalReference2() => { load_local_reference(class, method, frame, 2)?; } Instruction::LoadLocalReference3() => { load_local_reference(class, method, frame, 3)?; } Instruction::LogicalShiftIntRight() => { let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 & 0b00011111; let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let uint = u32::from_ne_bytes(int.to_ne_bytes()); let u_result = uint >> shift; let i_result = i32::from_ne_bytes(u_result.to_ne_bytes()); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(i_result)))?; } Instruction::LookupSwitch(default_offset, pairs) => { let key = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let jump_offset = match pairs.binary_search_by(|(match_key, _offset)| match_key.cmp(&key)) { Ok(offset) => pairs[offset].1, Err(_) => default_offset, }; frame.instruction_pointer -= offset as u32; frame.instruction_pointer = if jump_offset < 0 { frame.instruction_pointer - jump_offset.abs() as u32} else { frame.instruction_pointer + jump_offset.abs() as u32}; } 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::NewArray(component_class_index) => { // construct single level array let component_class_name = class.gather_class(component_class_index)?; if ! self.class_store.have_class(component_class_name) { // rewind the bytecode offset, I'll need to execute this instruction again frame.instruction_pointer -= offset as u32; return Ok(JVMCallbackOperation::LoadClass(component_class_name.to_string())); } if ! self.class_store.was_init(component_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(component_class_name.to_string())); } let component_class_descriptor = AbstractTypeDescription { array_level: 0, kind: match component_class_name { _ => AbstractTypeKind::Classname(component_class_name.to_string()) } }; let array_descriptor = AbstractTypeDescription { array_level: 1, kind: component_class_descriptor.kind.clone(), }; if let None = self.class_store.get_array_class_ref(&array_descriptor) { // rewind the bytecode offset, I'll need to execute this instruction again frame.instruction_pointer -= offset as u32; let component_class_index = self.class_store.class_idx_from_name(component_class_name).unwrap(); let component_class_class_ref = self.class_store.get_class_objectref_from_index(component_class_index); return Ok(JVMCallbackOperation::MakeArrayClass(component_class_class_ref, component_class_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, component_class_descriptor, array_capacity as usize); wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(array_ref)))?; } Instruction::NewPrimitiveArray(array_type) => { let array_capacity = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; const CHAR: u8 = 5; const BYTE: u8 = 8; let array_ref = match array_type { BYTE => { let array_ref = self.heap_area.make_primitive_byte_array(array_capacity as usize, &self.class_store); array_ref } CHAR => { let array_ref = self.heap_area.make_primitive_char_array(array_capacity as usize, &self.class_store); array_ref } _ => todo!() }; 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::Pop() => { wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?; } Instruction::PushConstFloat0() => { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(0.0)))?; } Instruction::PushConstFloat1() => { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(1.0)))?; } Instruction::PushConstFloat2() => { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(2.0)))?; } Instruction::PushConstIntM1() => { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(-1)))?; } 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::PushNull() => { wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(ObjectReference::NULL)))?; } Instruction::PutField(fieldref_index) => { let (_target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?; let value = match expected_field_descriptor.as_str() { "J" | "D" => wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_2(0))?, _ => match wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))? { StackValue::Int(i) => FieldValue::Int(i), StackValue::Reference(r) => FieldValue::Reference(r), StackValue::Float(f) => FieldValue::Float(f), stack_value @ _ => { println!("{stack_value:?}"); todo!() } } }; let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; self.heap_area.object_area.set_object_field(this_object, target_field_name, value, frame.class_index, &self.class_store)?; } 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, AbstractTypeKind::Int()) => { let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; FieldValue::Int(int_value) } (0..=255, AbstractTypeKind::Classname(_field_type_name)) => { let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; if ref_value != ObjectReference::NULL { 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(&parsed_native_name, &matched_field.descriptor) { 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) } (1..=255, _) => { let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; FieldValue::Reference(ref_value) } _ => { println!("{:?}", matched_field); todo!() } }; self.heap_area.static_area.set(target_class_name, target_field_name, set_value)?; } Instruction::ReturnInt() => { match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) { (_, AbstractTypeKind::Byte() | AbstractTypeKind::Boolean()| AbstractTypeKind::Int() | AbstractTypeKind::Char() | AbstractTypeKind::Short()) => (), _ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type))) } let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; return Ok(JVMCallbackOperation::ReturnFrame(StackValue::Int(int))); } Instruction::ReturnReference() => { match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) { (_, AbstractTypeKind::Classname(_)) => (), _ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type))) } let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; return Ok(JVMCallbackOperation::ReturnFrame(StackValue::Reference(ref_value))); } Instruction::ReturnVoid() => { let expected_type = AbstractTypeDescription { array_level: 0, kind: AbstractTypeKind::Void(), }; if method.descriptor.return_type != expected_type { return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type))) } return Ok(JVMCallbackOperation::PopFrame()); }, Instruction::StoreIntoBArray() => { let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; match self.heap_area.object_area.get_entry(array_ref) { CompartmentEntry::ByteArray(_) => { let byte_value = value as i8; self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Byte(byte_value)); } _ => todo!(), // TODO: Handle as error, Boolean arrays also } } Instruction::StoreIntoCArray() => { let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; match self.heap_area.object_area.get_entry(array_ref) { CompartmentEntry::CharArray(_) => { let int_bytes = value.to_ne_bytes(); let char_value = u16::from_ne_bytes([int_bytes[2], int_bytes[3]]); self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Char(char_value)); } _ => todo!(), // TODO: Handle as error } } Instruction::StoreIntoRArray() => { let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; // TODO: Type checking self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Reference(value)); } Instruction::SubtractInt() => { let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_1 - value_2)))?; } Instruction::StoreLocalInt(index) => { let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Int(int)))?; } Instruction::StoreLocalInt0() => { 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)))?; } Instruction::StoreLocalInt1() => { let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; 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(0))?; 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(0))?; wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Int(int)))?; } Instruction::StoreLocalReference(index) => { let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Reference(reference)))?; } Instruction::StoreLocalReference0() => { let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Reference(reference)))?; } Instruction::StoreLocalReference1() => { let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Reference(reference)))?; } Instruction::StoreLocalReference2() => { let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Reference(reference)))?; } Instruction::StoreLocalReference3() => { let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?; wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Reference(reference)))?; } Instruction::TableSwitch(default, low, high, offsets) => { let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; frame.instruction_pointer -= offset as u32; if index < low || index > high { frame.instruction_pointer = if default < 0 { frame.instruction_pointer - default.abs() as u32} else { frame.instruction_pointer + default.abs() as u32}; } else { let offset_index = index - low; let jump_offset = offsets[offset_index as usize]; frame.instruction_pointer = if jump_offset < 0 { frame.instruction_pointer - jump_offset.abs() as u32} else { frame.instruction_pointer + jump_offset.abs() as u32}; } } Instruction::XorInt() => { let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_1 ^ value_2)))?; } _ => { return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction))) }, } } // TODO: Review this, maybe crash when there is no return? Ok(JVMCallbackOperation::PopFrame()) } } pub enum JVMCallbackOperation { PopFrame(), ReturnFrame(StackValue), PushFrame(StackFrame), LoadClass(String), InitClass(String), MakeArrayClass(ObjectReference, AbstractTypeDescription), } 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_float(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> { let loaded_value = wrap_stackframe_error(class, method, frame.load_local_float(index as u16))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(loaded_value)))?; Ok(()) } fn load_local_int(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> { let loaded_value = wrap_stackframe_error(class, method, frame.load_local_int(index as u16))?; wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(loaded_value)))?; Ok(()) } fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut VecDeque, argument_types: &Box<[AbstractTypeDescription]>, stack: &mut OperandStack) -> Result<(), Error> { for argument_type_index in 0..argument_types.len() { let argument_type = &argument_types[argument_types.len() - argument_type_index - 1]; if argument_type.array_level != 0 { // TODO: Type checking arguments.push_front( StackValue::Reference(wrap_stackframe_error(class, method, stack.pop_reference(0))?), ) } else { match argument_type.kind { AbstractTypeKind::Void() => return Err(Error::RunTimeError("Functions cannot take arguments of type void".to_string())), // TODO: Add better description AbstractTypeKind::Byte() => { arguments.push_front( StackValue::Byte( wrap_stackframe_error( class, method, stack.pop_byte(0) )? ) ) }, AbstractTypeKind::Char() => { arguments.push_front( StackValue::Char( wrap_stackframe_error( class, method, stack.pop_char(0) )? ) ) }, AbstractTypeKind::Double() => { arguments.push_front( StackValue::Double1( wrap_stackframe_error( class, method, stack.pop_double1(0) )? ) ); arguments.push_front( StackValue::Double0( wrap_stackframe_error( class, method, stack.pop_double0(0) )? ) ); }, AbstractTypeKind::Float() => { arguments.push_front( StackValue::Float( wrap_stackframe_error( class, method, stack.pop_float(0) )? ) ) }, AbstractTypeKind::Int() => { arguments.push_front( StackValue::Int( wrap_stackframe_error( class, method, stack.pop_int(0) )? ) ) }, AbstractTypeKind::Long() => { arguments.push_front( StackValue::Long1( wrap_stackframe_error( class, method, stack.pop_long1(0) )? ) ); arguments.push_front( StackValue::Long0( wrap_stackframe_error( class, method, stack.pop_long0(0) )? ) ); }, AbstractTypeKind::Classname(ref name) => { // TODO: Type checking arguments.push_front( StackValue::Reference( wrap_stackframe_error( class, method, stack.pop_reference(0) )? ) ) }, AbstractTypeKind::Short() => { arguments.push_front( StackValue::Short( wrap_stackframe_error( class, method, stack.pop_short(0) )? ) ) }, AbstractTypeKind::Boolean() => { arguments.push_front( StackValue::Boolean( wrap_stackframe_error( class, method, stack.pop_boolean(0) )? ) ) }, } } } Ok(()) } pub fn wrap_stackframe_error(class: &JavaClassFile, method: &MethodInfo, frame_result: Result) -> Result { match frame_result { Ok(t) => Ok(t), Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))), } }