diff --git a/src/bytecode.rs b/src/bytecode.rs index 759b1b1..dba6b3d 100644 --- a/src/bytecode.rs +++ b/src/bytecode.rs @@ -40,6 +40,7 @@ impl Bytecode { 0x2C => (Instruction::LoadLocalReference2(), 1), 0x2D => (Instruction::LoadLocalReference3(), 1), + 0x32 => (Instruction::ArrayElement(), 1), 0x3B => (Instruction::StoreLocalInt0(), 1), 0x3C => (Instruction::StoreLocalInt1(), 1), 0x3D => (Instruction::StoreLocalInt2(), 1), @@ -59,7 +60,36 @@ impl Bytecode { 0x80 => (Instruction::OrInt(), 1), + 0x99 => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchZero(i16::from_be_bytes(bytes)), 3) + } + 0x9A => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchNonZero(i16::from_be_bytes(bytes)), 3) + } + 0x9B => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchNegative(i16::from_be_bytes(bytes)), 3) + } + 0x9C => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchNonPositive(i16::from_be_bytes(bytes)), 3) + } + 0x9D => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchPositive(i16::from_be_bytes(bytes)), 3) + } + 0x9E => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchNonNegative(i16::from_be_bytes(bytes)), 3) + } + 0xAC => (Instruction::ReturnInt(), 1), + 0xA7 => { + let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; + (Instruction::BranchAlways(i16::from_be_bytes(bytes)), 3) + } 0xB0 => (Instruction::ReturnReference(), 1), 0xB1 => (Instruction::ReturnVoid(), 1), @@ -73,6 +103,7 @@ impl Bytecode { 0xBA => (Instruction::InvokeDynamic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, (self.bytes[offset+3] as u16) << 8 | self.bytes[offset+4] as u16), 5), 0xBB => (Instruction::NewObject((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xBD => (Instruction::NewArray((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), + 0xBE => (Instruction::ArrayLength(), 1), _ => (Instruction::Unknown(opcode), 1) } } @@ -104,68 +135,84 @@ impl Debug for Bytecode { #[derive(Debug)] #[repr(u8)] pub enum Instruction { - NoOperation() = 0x00, // No-Operation - StoreIntoIntArray() = 0x01, // ..., arrayref, index, value - PushConstIntM1() = 0x02, // Push -1 - PushConstInt0() = 0x03, // Push 0 - PushConstInt1() = 0x04, // Push 1 - PushConstInt2() = 0x05, // Push 2 - PushConstInt3() = 0x06, // Push 3 - PushConstInt4() = 0x07, // Push 4 - PushConstInt5() = 0x08, // Push 5 - PushConstDouble0() = 0x0E, // Push 0.0 - PushConstDouble1() = 0x0F, // Push 1.0 + NoOperation() = 0x00, // No-Operation + StoreIntoIntArray() = 0x01, // ..., arrayref, index, value + PushConstIntM1() = 0x02, // Push -1 + PushConstInt0() = 0x03, // Push 0 + PushConstInt1() = 0x04, // Push 1 + PushConstInt2() = 0x05, // Push 2 + PushConstInt3() = 0x06, // Push 3 + PushConstInt4() = 0x07, // Push 4 + PushConstInt5() = 0x08, // Push 5 + PushConstDouble0() = 0x0E, // Push 0.0 + PushConstDouble1() = 0x0F, // Push 1.0 - LoadByteImmediate(u8) = 0x10, // push immediate short - LoadShortImmediate(u16) = 0x11, // push immediate short - LoadConstant(u8) = 0x12, // Push from constant pool - LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool - LoadLocalInt0() = 0x1A, // Load int from local variable - LoadLocalInt1() = 0x1B, // Load int from local variable - LoadLocalInt2() = 0x1C, // Load int from local variable - LoadLocalInt3() = 0x1D, // Load int from local variable + LoadByteImmediate(u8) = 0x10, // push immediate short + LoadShortImmediate(u16) = 0x11, // push immediate short + LoadConstant(u8) = 0x12, // Push from constant pool + LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool + LoadLocalInt0() = 0x1A, // Load int from local variable + LoadLocalInt1() = 0x1B, // Load int from local variable + LoadLocalInt2() = 0x1C, // Load int from local variable + LoadLocalInt3() = 0x1D, // Load int from local variable - LoadLocalDouble0() = 0x26, // Load local double variable reference onto stack - LoadLocalDouble1() = 0x27, // Load local double variable reference onto stack - LoadLocalDouble2() = 0x28, // Load local double variable reference onto stack - LoadLocalDouble3() = 0x29, // Load local double variable reference onto stack - LoadLocalReference0() = 0x2A, // Load local reference variable reference onto stack - LoadLocalReference1() = 0x2B, // Load local reference variable reference onto stack - LoadLocalReference2() = 0x2C, // Load local reference variable reference onto stack - LoadLocalReference3() = 0x2D, // Load local reference variable reference onto stack + LoadLocalDouble0() = 0x26, // Load local double variable reference onto stack + LoadLocalDouble1() = 0x27, // Load local double variable reference onto stack + LoadLocalDouble2() = 0x28, // Load local double variable reference onto stack + LoadLocalDouble3() = 0x29, // Load local double variable reference onto stack + LoadLocalReference0() = 0x2A, // Load local reference variable reference onto stack + LoadLocalReference1() = 0x2B, // Load local reference variable reference onto stack + LoadLocalReference2() = 0x2C, // Load local reference variable reference onto stack + LoadLocalReference3() = 0x2D, // Load local reference variable reference onto stack - StoreLocalInt0() = 0x3B, // store int into local variable - StoreLocalInt1() = 0x3C, // store int into local variable - StoreLocalInt2() = 0x3D, // store int into local variable - StoreLocalInt3() = 0x3E, // store int into local variable - StoreReference0() = 0x4B, // store reference into local variable - StoreReference1() = 0x4C, // store reference into local variable - StoreReference2() = 0x4D, // store reference into local variable - StoreReference3() = 0x4E, // store reference into local variable + ArrayElement() = 0x32, // load element from array + StoreLocalInt0() = 0x3B, // store int into local variable + StoreLocalInt1() = 0x3C, // store int into local variable + StoreLocalInt2() = 0x3D, // store int into local variable + StoreLocalInt3() = 0x3E, // store int into local variable + StoreReference0() = 0x4B, // store reference into local variable + StoreReference1() = 0x4C, // store reference into local variable + StoreReference2() = 0x4D, // store reference into local variable + StoreReference3() = 0x4E, // store reference into local variable - Pop() = 0x57, // Pop top stack value - Duplicate() = 0x59, // duplicate top stack value + Pop() = 0x57, // Pop top stack value + Duplicate() = 0x59, // duplicate top stack value - MultiplyInt() = 0x68, // int multiplication - DivideLong() = 0x6D, // long division + MultiplyInt() = 0x68, // int multiplication + DivideLong() = 0x6D, // long division - ShiftIntRight() = 0x7a, // shift int + ShiftIntRight() = 0x7a, // shift int - OrInt() = 0x80, // value, value => or + OrInt() = 0x80, // value, value => or - ReturnInt() = 0xAC, // return integer from function + BranchZero(i16) = 0x99, // branch if value == 0 + BranchNonZero(i16) = 0x9A, // branch if value != 0 + BranchNegative(i16) = 0x9B, // branch if value < 0 + BranchNonPositive(i16) = 0x9C, // branch if value <= 0 + BranchPositive(i16) = 0x9D, // branch if value > 0 + BranchNonNegative(i16) = 0x9E, // branch if value >= 0 - ReturnReference() = 0xB0, // return top-ref from current function - ReturnVoid() = 0xB1, // return void from function - GetStatic(u16) = 0xB2, // get static field from class - PutStatic(u16) = 0xB3, // set static field on class - GetField(u16) = 0xB4, // get field from class - PutField(u16) = 0xB5, // set field to a value - InvokeVirtual(u16) = 0xB6, // invoke function on a class - InvokeSpecial(u16) = 0xB7, // invoke instance method - InvokeStatic(u16) = 0xB8, // invoke static function - InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function - NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference - NewArray(u16) = 0xBD, // Create a new array from a constant-pool component class reference + BranchIntEquality(i16) = 0x9F, + BranchIntInequality(i16) = 0xA0, + BranchIntLessThan(i16) = 0xA1, + BranchIntGreaterEquals(i16) = 0xA2, + BranchIntGreaterThan(i16) = 0xA3, + + BranchAlways(i16) = 0xA7, // branch if true + ReturnInt() = 0xAC, // return integer from function + + ReturnReference() = 0xB0, // return top-ref from current function + ReturnVoid() = 0xB1, // return void from function + GetStatic(u16) = 0xB2, // get static field from class + PutStatic(u16) = 0xB3, // set static field on class + GetField(u16) = 0xB4, // get field from class + PutField(u16) = 0xB5, // set field to a value + InvokeVirtual(u16) = 0xB6, // invoke function on a class + InvokeSpecial(u16) = 0xB7, // invoke instance method + InvokeStatic(u16) = 0xB8, // invoke static function + InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function + NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference + NewArray(u16) = 0xBD, // Create a new array from a constant-pool component class reference + ArrayLength() = 0xBE, // Get length from array reference Unknown(u8), } diff --git a/src/classfile.rs b/src/classfile.rs index 4057f0e..0027c80 100644 --- a/src/classfile.rs +++ b/src/classfile.rs @@ -786,6 +786,14 @@ impl Into for &AbstractTypeDescription { } impl AbstractTypeDescription { + pub fn parse_full(s: &str) -> Result { + let (c, parsed) = Self::parse_first(s)?; + + assert!(c == s.len()); + + Ok(parsed) + } + pub fn parse_first(s: &str) -> Result<(usize, Self), Error> { let mut offset: usize = 0; let arrays_parsed = s.trim_start_matches("["); diff --git a/src/classstore.rs b/src/classstore.rs index c62d4f4..5885324 100644 --- a/src/classstore.rs +++ b/src/classstore.rs @@ -168,6 +168,13 @@ impl ClassStore { } } + pub fn class_name_from_index(&self, index: usize) -> Option<&String> { + return match self.class_file_from_idx(index) { + Some(file) => Some(file.get_classname().unwrap()), + None => None, + } + } + pub fn class_file_from_idx(&self, idx: usize) -> Option<&JavaClassFile> { return match self.classes.get(idx) { Some(entry) => Some(&entry.class_file), diff --git a/src/heap_area.rs b/src/heap_area.rs index 62a7d48..03f18e4 100644 --- a/src/heap_area.rs +++ b/src/heap_area.rs @@ -200,15 +200,15 @@ impl ObjectArea { return class_store.get_native_class_name(native_name_index as usize); } - fn get_object_class_index(&self, reference: ObjectReference) -> usize { - match self.get_object(reference) { + pub fn get_object_class_index(&self, reference: ObjectReference) -> usize { + match self.get_entry(reference) { CompartmentEntry::Object(o) => o.class_index, _ => unreachable!(), } } fn get_reference_class_ref(&self, reference: ObjectReference, class_store: &ClassStore) -> ObjectReference { - match self.get_object(reference) { + match self.get_entry(reference) { CompartmentEntry::Object(o) => class_store.get_class_objectref_from_index(o.class_index), CompartmentEntry::Array(a) => a.class_ref, _ => unreachable!(), @@ -230,7 +230,7 @@ impl ObjectArea { return ObjectReference(object_index + (DEFAULT_COMPARTMENT_CAPACITY * (compartment_index as u32 + 1))); } - fn get_object(&self, reference: ObjectReference) -> &CompartmentEntry { + fn get_entry(&self, reference: ObjectReference) -> &CompartmentEntry { let index = reference.0; let compartment_index: u32 = (index / DEFAULT_COMPARTMENT_CAPACITY) - 1; let object_index: u32 = index % DEFAULT_COMPARTMENT_CAPACITY; @@ -241,7 +241,7 @@ impl ObjectArea { return object; } - fn get_object_mut(&mut self, reference: ObjectReference) -> &mut CompartmentEntry { + fn get_entry_mut(&mut self, reference: ObjectReference) -> &mut CompartmentEntry { let index = reference.0; let compartment_index: u32 = (index / DEFAULT_COMPARTMENT_CAPACITY) - 1; let object_index: u32 = index % DEFAULT_COMPARTMENT_CAPACITY; @@ -252,9 +252,29 @@ impl ObjectArea { return object; } + pub fn get_array_length(&self, reference: ObjectReference) -> usize { + // TODO: Throw errors + let array = match self.get_entry(reference) { + CompartmentEntry::Array(a) => a, + _ => unreachable!(), + }; + + array.content.len() + } + + pub fn get_array_element(&self, array_ref: ObjectReference, element_index: i32) -> ObjectReference { + // TODO: Throw errors + let array = match self.get_entry(array_ref) { + CompartmentEntry::Array(a) => a, + _ => unreachable!(), + }; + + array.content[element_index as usize] + } + pub fn get_object_field(&self, reference: ObjectReference, field_name: &str, accessing_class_idx: usize, class_store: &ClassStore) -> Result { // TODO: Check access rights - let object = match self.get_object(reference) { + let object = match self.get_entry(reference) { CompartmentEntry::Object(o) => o, _ => unreachable!(), }; @@ -282,7 +302,7 @@ impl ObjectArea { } pub fn set_array_element(&mut self, array_reference: ObjectReference, index: usize, element: ObjectReference) { - let array = match self.get_object_mut(array_reference) { + let array = match self.get_entry_mut(array_reference) { CompartmentEntry::Array(a) => a, _ => unreachable!(), }; @@ -294,7 +314,7 @@ impl ObjectArea { pub fn set_object_field(&mut self, reference: ObjectReference, field_name: &str, value: FieldValue, accessing_class_idx: usize, class_store: &ClassStore) -> Result<(), Error> { // TODO: Check access rights - let object = match self.get_object_mut(reference) { + let object = match self.get_entry_mut(reference) { CompartmentEntry::Object(o) => o, _ => unreachable!(), }; @@ -425,6 +445,26 @@ pub struct StaticArea { impl StaticArea { + pub fn get(&self, class_name: &String, field_name: &String, expected_field_descriptor: AbstractTypeDescription) -> Result { + // TODO: Access permission checking + let static_object = match self.static_objects.get(class_name) { + Some(o) => o, + None => return Err(Error::RunTimeError(format!("Trying to get '{}.{}' but there is no such static object.", class_name, field_name))), + }; + + let field_index = match static_object.field_index_from_name(&field_name) { + Some(i) => i, + None => return Err(Error::RunTimeError(format!("Trying to get '{}.{}' but there is no such static field on this object.", class_name, field_name))), + }; + + let field_descriptor = &static_object.fields[field_index].descriptor; + if expected_field_descriptor != *field_descriptor { + return Err(Error::RunTimeError(format!("Descriptor Mismatch: Trying to get a {expected_field_descriptor:?} from '{class_name}.{field_name} but the field is of type '{field_descriptor:?}'."))); + } + + Ok(static_object.fields[field_index].value) + } + pub fn set(&mut self, class_name: &String, field_name: &String, field_value: FieldValue) -> Result<(), Error> { // TODO: Access permission checking let static_object = match self.static_objects.get_mut(class_name) { diff --git a/src/jvm.rs b/src/jvm.rs index 22bbf2d..4b582f7 100644 --- a/src/jvm.rs +++ b/src/jvm.rs @@ -194,7 +194,7 @@ impl JVM { 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::Utf8(ConstantUtf8Info { utf8: "([Ljava/lang/String;)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() }), @@ -227,10 +227,12 @@ impl JVM { attribute_name_index: 3, data: AttributeData::Code( CodeAttributeData { - max_stack: 0, + max_stack: 1, max_locals: 1, code: Bytecode { bytes: Box::new([ + 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 @@ -243,54 +245,12 @@ 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([]), }; self.class_store.add_class(entry_class, true)?; // 0 - self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1 + 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 @@ -537,13 +497,100 @@ impl JVM { match instruction { + 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(StackValue::Reference(element)))?; + }, + + 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::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::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::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::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) { @@ -654,6 +701,77 @@ impl JVM { 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 '{}: Types are incompatible'", + this_object_class_name + ))) + } + + 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 i8_int = i8::from_be_bytes([byte]); @@ -681,6 +799,15 @@ impl JVM { 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::MultiplyInt() => { let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; @@ -834,12 +961,9 @@ impl JVM { } Instruction::ReturnInt() => { - let expected_type = AbstractTypeDescription { - array_level: 0, - kind: AbstractTypeKind::Int(), - }; - if method.descriptor.return_type != expected_type { - return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type))) + 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))?; @@ -859,6 +983,13 @@ impl JVM { return Ok(JVMCallbackOperation::PopFrame()); }, + Instruction::ShiftIntRight() => { + 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))?; + + wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int >> shift)))?; + }, + Instruction::StoreLocalInt0() => { let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?; @@ -880,6 +1011,27 @@ impl JVM { wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Int(int)))?; }, + Instruction::StoreReference0() => { + 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::StoreReference1() => { + 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::StoreReference2() => { + 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::StoreReference3() => { + 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)))?; + }, + _ => { return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction))) }, diff --git a/src/main.rs b/src/main.rs index 84d646c..b17fe97 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,18 +20,18 @@ fn main() { let mut jvm = jvm::JVM::new(); match jvm.entrypoint( - &"java/Math".to_string(), - &"v".to_string(), + &"Main".to_string(), + &"main".to_string(), &["Hello World"], ) { Ok(()) => (), Err(e) => println!("{:#?}", e), } - // match jvm.run() { - // Ok(()) => (), - // Err(e) => println!("{:#?}", e), - // }; + match jvm.run() { + Ok(()) => (), + Err(e) => println!("{:#?}", e), + }; //println!("{:#?}", jvm.heap_area.static_area); } diff --git a/src/stackframe.rs b/src/stackframe.rs index 8b2095a..65fa90b 100644 --- a/src/stackframe.rs +++ b/src/stackframe.rs @@ -1,5 +1,5 @@ use crate::classfile::{ JavaClassFile, AttributeData }; -use crate::heap_area::ObjectReference; +use crate::heap_area::{ ObjectReference, FieldValue }; #[derive(Copy, Clone, Debug)] pub enum StackValue { @@ -48,6 +48,27 @@ impl OperandStack { Ok(()) } + + pub fn push_field_value(&mut self, value: FieldValue) -> Result<(), Error> { + match value { + FieldValue::Reference(r) => { + self.push(StackValue::Reference(r)) + } + + FieldValue::Boolean(b) => { + self.push(StackValue::Int(if b { 1 } else { 0 })) + } + + FieldValue::Byte(b) => { + self.push(StackValue::Int(b as i32)) + } + + _ => { + println!("{value:?}"); + todo!(); + } + } + } pub fn pop_computational_1(&mut self, index: usize) -> Result { let absolute_index = self.depth as usize - 1 - index;