I can't even describe my disappointment at interfaces
This commit is contained in:
parent
d7d159d115
commit
9190d3f7e7
4 changed files with 116 additions and 15 deletions
|
@ -207,6 +207,7 @@ impl Bytecode {
|
||||||
0xB6 => (Instruction::InvokeVirtual((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
0xB6 => (Instruction::InvokeVirtual((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||||
0xB7 => (Instruction::InvokeSpecial((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
0xB7 => (Instruction::InvokeSpecial((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||||
0xB8 => (Instruction::InvokeStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
0xB8 => (Instruction::InvokeStatic((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||||
|
0xB9 => (Instruction::InvokeInterface((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||||
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),
|
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),
|
0xBB => (Instruction::NewObject((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||||
0xBC => (Instruction::NewPrimitiveArray(self.bytes[offset+1]), 2),
|
0xBC => (Instruction::NewPrimitiveArray(self.bytes[offset+1]), 2),
|
||||||
|
@ -363,6 +364,7 @@ pub enum Instruction {
|
||||||
InvokeVirtual(u16) = 0xB6, // invoke function on a class
|
InvokeVirtual(u16) = 0xB6, // invoke function on a class
|
||||||
InvokeSpecial(u16) = 0xB7, // invoke instance method
|
InvokeSpecial(u16) = 0xB7, // invoke instance method
|
||||||
InvokeStatic(u16) = 0xB8, // invoke static function
|
InvokeStatic(u16) = 0xB8, // invoke static function
|
||||||
|
InvokeInterface(u16) = 0xB9, // invoke interface function
|
||||||
InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function
|
InvokeDynamic(u16, u16) = 0xBA, // invoke dynamic function
|
||||||
NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference
|
NewObject(u16) = 0xBB, // Create a new object from a constant-pool class reference
|
||||||
NewPrimitiveArray(u8) = 0xBC, // make a primitive array
|
NewPrimitiveArray(u8) = 0xBC, // make a primitive array
|
||||||
|
|
|
@ -5,7 +5,7 @@ use core::str::Utf8Error;
|
||||||
|
|
||||||
use crate::accessmasks::*;
|
use crate::accessmasks::*;
|
||||||
use crate::bytecode::Bytecode;
|
use crate::bytecode::Bytecode;
|
||||||
use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantStringInfo, ConstantMethodRefInfo, ConstantFloatInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo };
|
use crate::constantpool::{ ConstantFieldRefInfo, ConstantPoolInfo, ConstantUtf8Info, ConstantInterfaceMethodRefInfo, ConstantStringInfo, ConstantMethodRefInfo, ConstantFloatInfo, ConstantClassInfo, ConstantNameAndTypeInfo, ConstantIntegerInfo, ConstantLongInfo };
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum Error {
|
pub enum Error {
|
||||||
|
@ -206,6 +206,17 @@ impl JavaClassFile {
|
||||||
return pool_entry(&self.constant_pool, index);
|
return pool_entry(&self.constant_pool, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pool_interfacemethodref_entry(&self, index: u16) -> Result<&ConstantInterfaceMethodRefInfo, Error> {
|
||||||
|
let pool_entry = self.pool_entry(index)?;
|
||||||
|
|
||||||
|
let methodref_entry = match pool_entry {
|
||||||
|
ConstantPoolInfo::InterfaceMethodRef(data) => data,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
|
||||||
|
return Ok(methodref_entry);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn pool_methodref_entry(&self, index: u16) -> Result<&ConstantMethodRefInfo, Error> {
|
pub fn pool_methodref_entry(&self, index: u16) -> Result<&ConstantMethodRefInfo, Error> {
|
||||||
let pool_entry = self.pool_entry(index)?;
|
let pool_entry = self.pool_entry(index)?;
|
||||||
|
|
||||||
|
@ -329,6 +340,19 @@ impl JavaClassFile {
|
||||||
Ok(class_name)
|
Ok(class_name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn gather_interfacemethodref(&self, index: u16) -> Result<(&String, &String, &String), Error> {
|
||||||
|
let methodref = self.pool_interfacemethodref_entry(index)?;
|
||||||
|
let class_entry = self.pool_class_entry(methodref.class_index)?;
|
||||||
|
let class_name_entry = self.pool_utf8_entry(class_entry.name_index)?;
|
||||||
|
let name_and_type_entry = self.pool_nameandtype_entry(methodref.name_and_type_index)?;
|
||||||
|
|
||||||
|
let class_name = &class_name_entry.utf8;
|
||||||
|
let method_name = &self.pool_utf8_entry(name_and_type_entry.name_index)?.utf8;
|
||||||
|
let method_descriptor = &self.pool_utf8_entry(name_and_type_entry.descriptor_index)?.utf8;
|
||||||
|
|
||||||
|
return Ok((class_name, method_name, method_descriptor));
|
||||||
|
}
|
||||||
|
|
||||||
pub fn gather_methodref(&self, index: u16) -> Result<(&String, &String, &String), Error> {
|
pub fn gather_methodref(&self, index: u16) -> Result<(&String, &String, &String), Error> {
|
||||||
let methodref = self.pool_methodref_entry(index)?;
|
let methodref = self.pool_methodref_entry(index)?;
|
||||||
let class_entry = self.pool_class_entry(methodref.class_index)?;
|
let class_entry = self.pool_class_entry(methodref.class_index)?;
|
||||||
|
@ -886,6 +910,13 @@ impl AbstractTypeDescription {
|
||||||
|
|
||||||
return Ok((offset, AbstractTypeDescription { array_level, kind }))
|
return Ok((offset, AbstractTypeDescription { array_level, kind }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn for_class_name(name: &str) -> Self {
|
||||||
|
AbstractTypeDescription {
|
||||||
|
array_level: 0,
|
||||||
|
kind: AbstractTypeKind::Classname(name.to_string()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Debug, Eq, PartialEq)]
|
||||||
|
|
|
@ -256,6 +256,7 @@ impl ObjectArea {
|
||||||
match self.get_entry(reference) {
|
match self.get_entry(reference) {
|
||||||
CompartmentEntry::Object(o) => class_store.get_class_objectref_from_index(o.class_index),
|
CompartmentEntry::Object(o) => class_store.get_class_objectref_from_index(o.class_index),
|
||||||
CompartmentEntry::ReferenceArray(a) => a.class_ref,
|
CompartmentEntry::ReferenceArray(a) => a.class_ref,
|
||||||
|
CompartmentEntry::ByteArray(a) => a.class_ref,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
95
src/jvm.rs
95
src/jvm.rs
|
@ -841,16 +841,22 @@ impl JVM {
|
||||||
Instruction::CheckCast(classref_index) => {
|
Instruction::CheckCast(classref_index) => {
|
||||||
// TODO: Class loading checks
|
// TODO: Class loading checks
|
||||||
let class_name = class.gather_class(classref_index)?;
|
let class_name = class.gather_class(classref_index)?;
|
||||||
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 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
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 native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store);
|
||||||
let object_description = AbstractTypeDescription::parse_full(native_class_name)?;
|
|
||||||
|
|
||||||
if ! self.class_store.are_types_compatible(&object_description, &class_description) {
|
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
|
// TODO: Throw Exception
|
||||||
return Err(Error::RunTimeError(format!("Trying to cast an object of type {native_class_name} to {class_name}")))
|
return Err(Error::RunTimeError(format!("Trying to cast an object of type {native_class_name} to {class_name}")))
|
||||||
}
|
}
|
||||||
|
@ -979,20 +985,81 @@ impl JVM {
|
||||||
Instruction::InstanceOf(classref_index) => {
|
Instruction::InstanceOf(classref_index) => {
|
||||||
// TODO: Class loading checks
|
// TODO: Class loading checks
|
||||||
let class_name = class.gather_class(classref_index)?;
|
let class_name = class.gather_class(classref_index)?;
|
||||||
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 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
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 native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store);
|
||||||
let object_description = AbstractTypeDescription::parse_full(native_class_name)?;
|
|
||||||
|
|
||||||
let instruction_result = if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 };
|
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)))?;
|
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) => {
|
Instruction::InvokeSpecial(methodref_index) => {
|
||||||
// No instance-based dispatch
|
// No instance-based dispatch
|
||||||
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;
|
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(methodref_index)?;
|
||||||
|
|
Loading…
Reference in a new issue