More bytecode ops

This commit is contained in:
vegowotenks 2024-09-06 21:11:03 +02:00
parent 9243c0b291
commit 5bc0d813e5
7 changed files with 395 additions and 120 deletions

View file

@ -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)))
},