Compare commits

...

2 commits

4 changed files with 182 additions and 17 deletions

View file

@ -34,6 +34,7 @@ impl Bytecode {
0x13 => (Instruction::LoadCostantWide((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0x13 => (Instruction::LoadCostantWide((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
0x14 => (Instruction::LoadConstant64((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0x14 => (Instruction::LoadConstant64((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
0x15 => (Instruction::LoadLocalInt(self.bytes[offset+1]), 2),
0x19 => (Instruction::LoadLocalReference(self.bytes[offset+1]), 2), 0x19 => (Instruction::LoadLocalReference(self.bytes[offset+1]), 2),
0x1A => (Instruction::LoadLocalInt0(), 1), 0x1A => (Instruction::LoadLocalInt0(), 1),
0x1B => (Instruction::LoadLocalInt1(), 1), 0x1B => (Instruction::LoadLocalInt1(), 1),
@ -80,8 +81,10 @@ impl Bytecode {
0x7A => (Instruction::ArithmeticShiftIntRight(), 1), 0x7A => (Instruction::ArithmeticShiftIntRight(), 1),
0x7C => (Instruction::LogicalShiftIntRight(), 1), 0x7C => (Instruction::LogicalShiftIntRight(), 1),
0x7E => (Instruction::AndInt(), 1),
0x80 => (Instruction::OrInt(), 1), 0x80 => (Instruction::OrInt(), 1),
0x82 => (Instruction::XorInt(), 1),
0x84 => (Instruction::IncrementLocalInt(self.bytes[offset+1], i8::from_be_bytes([self.bytes[offset+2]])), 3), 0x84 => (Instruction::IncrementLocalInt(self.bytes[offset+1], i8::from_be_bytes([self.bytes[offset+2]])), 3),
0x95 => (Instruction::CompareFloatL(), 1), 0x95 => (Instruction::CompareFloatL(), 1),
@ -148,6 +151,33 @@ impl Bytecode {
(Instruction::BranchAlways(i16::from_be_bytes(bytes)), 3) (Instruction::BranchAlways(i16::from_be_bytes(bytes)), 3)
} }
0xAA => {
let padding = 4 - (offset % 4) - 1;
let default_bytes = [self.bytes[offset+padding+1], self.bytes[offset+padding+2], self.bytes[offset+padding+3], self.bytes[offset+padding+4]];
let default = i32::from_be_bytes(default_bytes);
let low_bytes = [self.bytes[offset+padding+5], self.bytes[offset+padding+6], self.bytes[offset+padding+7], self.bytes[offset+padding+8]];
let low = i32::from_be_bytes(low_bytes);
let high_bytes = [self.bytes[offset+padding+9], self.bytes[offset+padding+10], self.bytes[offset+padding+11], self.bytes[offset+padding+12]];
let high = i32::from_be_bytes(high_bytes);
// TODO: Throw
assert!(low <= high);
let offsets_count = (high - low + 1) as usize;
let mut offsets_vec = Vec::with_capacity(offsets_count);
for i in 0..offsets_count {
let offset_bytes = [self.bytes[offset+padding+12+(i*4)+1], self.bytes[offset+padding+12+(i*4)+2], self.bytes[offset+padding+12+(i*4)+3], self.bytes[offset+padding+12+(i*4)+4]];
let offset = i32::from_be_bytes(offset_bytes);
offsets_vec.push(offset);
}
(Instruction::TableSwitch(default, low, high, offsets_vec.into()), 1 + padding + 12 + 4 * offsets_count)
}
0xAB => { 0xAB => {
let padding = 4 - (offset % 4) - 1; let padding = 4 - (offset % 4) - 1;
let default_bytes = [self.bytes[offset+padding+1], self.bytes[offset+padding+2], self.bytes[offset+padding+3], self.bytes[offset+padding+4]]; let default_bytes = [self.bytes[offset+padding+1], self.bytes[offset+padding+2], self.bytes[offset+padding+3], self.bytes[offset+padding+4]];
@ -177,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),
@ -248,6 +279,7 @@ pub enum Instruction {
LoadCostantWide(u16) = 0x13, // Push from constant pool with wide index, don't load LoadCostantWide(u16) = 0x13, // Push from constant pool with wide index, don't load
// double or long or whatever // double or long or whatever
LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool LoadConstant64(u16) = 0x14, // Push Long or Double from constant pool
LoadLocalInt(u8) = 0x15, // Load int from indexed local variable
LoadLocalReference(u8) = 0x19, // Load reference from indexed local variable LoadLocalReference(u8) = 0x19, // Load reference from indexed local variable
LoadLocalInt0() = 0x1A, // Load int from local variable LoadLocalInt0() = 0x1A, // Load int from local variable
LoadLocalInt1() = 0x1B, // Load int from local variable LoadLocalInt1() = 0x1B, // Load int from local variable
@ -294,8 +326,10 @@ pub enum Instruction {
ArithmeticShiftIntRight() = 0x7A, // shift int ArithmeticShiftIntRight() = 0x7A, // shift int
LogicalShiftIntRight() = 0x7C, // shift int right with zero extension LogicalShiftIntRight() = 0x7C, // shift int right with zero extension
AndInt() = 0x7E, // bitwise and
OrInt() = 0x80, // value, value => or OrInt() = 0x80, // value, value => or
XorInt() = 0x82, // value, value => xor
IncrementLocalInt(u8, i8) = 0x84, // increment local variable by constant i8 IncrementLocalInt(u8, i8) = 0x84, // increment local variable by constant i8
CompareFloatL() = 0x95, // compare float, push -1 if one is NaN CompareFloatL() = 0x95, // compare float, push -1 if one is NaN
@ -317,7 +351,8 @@ pub enum Instruction {
BranchReferenceInequality(i16) = 0xA6, BranchReferenceInequality(i16) = 0xA6,
BranchAlways(i16) = 0xA7, // branch if true BranchAlways(i16) = 0xA7, // branch if true
LookupSwitch(i32, Box<[(i32, i32)]>) = 0xAB, // offset based on switch value TableSwitch(i32, i32, i32, Box<[i32]>) = 0xAA, // jump based on indexed range
LookupSwitch(i32, Box<[(i32, i32)]>) = 0xAB, // jump based on switch value
ReturnInt() = 0xAC, // return integer from function ReturnInt() = 0xAC, // return integer from function
ReturnReference() = 0xB0, // return top-ref from current function ReturnReference() = 0xB0, // return top-ref from current function
@ -329,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

View file

@ -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)]

View file

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

View file

@ -701,7 +701,14 @@ impl JVM {
let element = self.heap_area.object_area.get_array_element(array_reference, element_index); 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))?; 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) => { Instruction::BranchAlways(branch_offset) => {
frame.instruction_pointer -= offset as u32; frame.instruction_pointer -= offset as u32;
@ -834,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 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_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 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 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 class_description = AbstractTypeDescription::parse_full(native_class_description)?;
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 object_description = AbstractTypeDescription::parse_full(native_class_name)?; let object_description = AbstractTypeDescription::parse_full(native_class_name)?;
if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 }
};
if ! self.class_store.are_types_compatible(&object_description, &class_description) { 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}")))
} }
@ -972,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 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_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 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 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 class_description = AbstractTypeDescription::parse_full(native_class_description)?;
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 object_description = AbstractTypeDescription::parse_full(native_class_name)?; let object_description = AbstractTypeDescription::parse_full(native_class_name)?;
if self.class_store.are_types_compatible(&object_description, &class_description) { 1 } else { 0 }
};
let instruction_result = 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)?;
@ -1323,6 +1397,9 @@ impl JVM {
load_local_float(class, method, frame, 3)?; load_local_float(class, method, frame, 3)?;
} }
Instruction::LoadLocalInt(index) => {
load_local_int(class, method, frame, index as usize)?;
}
Instruction::LoadLocalInt0() => { Instruction::LoadLocalInt0() => {
load_local_int(class, method, frame, 0)?; load_local_int(class, method, frame, 0)?;
} }
@ -1737,6 +1814,26 @@ impl JVM {
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Reference(reference)))?; 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))) return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction)))
}, },