Native method registry java.lang.Class wtf
This commit is contained in:
parent
3282694b32
commit
b4c428685f
9 changed files with 987 additions and 40 deletions
251
src/jvm.rs
251
src/jvm.rs
|
@ -12,6 +12,9 @@ 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::ignore_call;
|
||||
use crate::native_registry::{ NativeRegistry };
|
||||
use crate::stackframe;
|
||||
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
||||
|
||||
|
@ -53,6 +56,7 @@ pub struct JVM {
|
|||
pub class_store: ClassStore,
|
||||
pub stack_frames: Vec<StackFrame>,
|
||||
pub heap_area: HeapArea,
|
||||
pub native_registry: NativeRegistry,
|
||||
}
|
||||
|
||||
impl JVM {
|
||||
|
@ -61,9 +65,30 @@ impl 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<usize, Error> {
|
||||
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,
|
||||
|
@ -208,6 +233,10 @@ impl JVM {
|
|||
primitive_class_object
|
||||
}
|
||||
|
||||
fn register_native(&mut self, class_name: &str, method_name: &str, method_descriptor: &MethodDescriptor) {
|
||||
self.native_registry.register("java/lang/System", "registerNatives", ignore_call);
|
||||
}
|
||||
|
||||
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[&str]) -> Result<(), Error> {
|
||||
let entry_class = JavaClassFile {
|
||||
minor_version: 0,
|
||||
|
@ -278,11 +307,11 @@ impl JVM {
|
|||
|
||||
self.class_store.add_class(entry_class, true)?; // 0
|
||||
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
||||
self.class_store.load_class(&"java/lang/Object".to_string())?; // 2
|
||||
self.class_store.load_class(&"java/lang/Number".to_string())?; // 3
|
||||
self.class_store.load_class(&"java/lang/Byte".to_string())?; // 4
|
||||
self.class_store.load_class(&"java/lang/String".to_string())?; // 5
|
||||
self.class_store.load_class(&"java/lang/Class".to_string())?; // 6
|
||||
self.load_class(&"java/lang/Object".to_string())?; // 2
|
||||
self.load_class(&"java/lang/Number".to_string())?; // 3
|
||||
self.load_class(&"java/lang/Byte".to_string())?; // 4
|
||||
self.load_class(&"java/lang/String".to_string())?; // 5
|
||||
self.load_class(&"java/lang/Class".to_string())?; // 6
|
||||
|
||||
self.make_class_class("Ljava/lang/Byte;");
|
||||
self.make_class_class("Ljava/lang/String;");
|
||||
|
@ -388,7 +417,7 @@ impl JVM {
|
|||
|
||||
if ! self.class_store.have_class(&class_name) {
|
||||
println!("Loading Class {class_name}");
|
||||
self.class_store.load_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());
|
||||
|
@ -536,6 +565,25 @@ impl JVM {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn native_call(&mut self) -> Result<JVMCallbackOperation, Error> {
|
||||
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<JVMCallbackOperation, Error> {
|
||||
|
||||
let frame = {
|
||||
|
@ -544,6 +592,11 @@ impl JVM {
|
|||
};
|
||||
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 {
|
||||
return self.native_call()
|
||||
}
|
||||
|
||||
let code_attr = method.get_code_attribute().unwrap();
|
||||
let bytecode = & code_attr.code;
|
||||
|
||||
|
@ -577,6 +630,16 @@ impl JVM {
|
|||
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_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))?;
|
||||
|
||||
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_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))?;
|
||||
|
@ -587,6 +650,24 @@ impl JVM {
|
|||
}
|
||||
}
|
||||
|
||||
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::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::BranchZero(branch_offset) => {
|
||||
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
|
@ -596,12 +677,29 @@ impl JVM {
|
|||
}
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
|
@ -742,6 +840,9 @@ impl JVM {
|
|||
)));
|
||||
}
|
||||
|
||||
if callee_method_info.access_flags & MethodAccessFlag::Native {
|
||||
}
|
||||
|
||||
let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?;
|
||||
// TODO: Throw exception on fail
|
||||
|
||||
|
@ -759,6 +860,7 @@ impl JVM {
|
|||
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,
|
||||
|
@ -849,11 +951,93 @@ impl JVM {
|
|||
Ok(_) => (),
|
||||
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
||||
}
|
||||
}
|
||||
|
||||
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)))?;
|
||||
},
|
||||
_ => {
|
||||
println!("{:?}", class.pool_entry(index as u16).unwrap());
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Instruction::LoadWideConstant(wide_index) => {
|
||||
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: if component_name.len() == 1 {
|
||||
component_name.into()
|
||||
} else {
|
||||
AbstractTypeKind::Classname(component_name.to_string())
|
||||
}
|
||||
};
|
||||
|
||||
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();
|
||||
|
@ -862,7 +1046,10 @@ impl JVM {
|
|||
|
||||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?;
|
||||
},
|
||||
_ => todo!(),
|
||||
_ => {
|
||||
println!("{:?}", class.pool_entry(wide_index).unwrap());
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -998,6 +1185,10 @@ impl JVM {
|
|||
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::PutStatic(fieldref_index) => {
|
||||
let (target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
||||
|
||||
|
@ -1042,11 +1233,13 @@ impl JVM {
|
|||
(0..=255, AbstractTypeKind::Classname(_field_type_name)) => {
|
||||
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
||||
|
||||
let value_native_name = self.heap_area.object_area.get_reference_native_class_name(ref_value, &self.class_store);
|
||||
let parsed_native_name = AbstractTypeDescription::parse_first(value_native_name).unwrap().1;
|
||||
if 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(&matched_field.descriptor, &parsed_native_name) {
|
||||
return Err(Error::RunTimeError(format!("PutStatic: Trying to set a value with type '{parsed_native_name:?}' on a field with type '{:?}'", matched_field.descriptor)));
|
||||
if ! self.class_store.are_types_compatible(&matched_field.descriptor, &parsed_native_name) {
|
||||
return Err(Error::RunTimeError(format!("PutStatic: Trying to set a value with type '{parsed_native_name:?}' on a field with type '{:?}'", matched_field.descriptor)));
|
||||
}
|
||||
}
|
||||
|
||||
FieldValue::Reference(ref_value)
|
||||
|
@ -1070,6 +1263,16 @@ impl JVM {
|
|||
|
||||
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,
|
||||
|
@ -1090,17 +1293,7 @@ impl JVM {
|
|||
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int >> shift)))?;
|
||||
},
|
||||
|
||||
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::StoreIntoBArray() => {
|
||||
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))?;
|
||||
|
@ -1115,6 +1308,16 @@ impl JVM {
|
|||
}
|
||||
}
|
||||
|
||||
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::StoreLocalInt(index) => {
|
||||
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
||||
|
||||
|
@ -1175,7 +1378,7 @@ impl JVM {
|
|||
|
||||
}
|
||||
|
||||
enum JVMCallbackOperation {
|
||||
pub enum JVMCallbackOperation {
|
||||
PopFrame(),
|
||||
ReturnFrame(StackValue),
|
||||
PushFrame(StackFrame),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue