feat: new instructions

This commit is contained in:
vegowotenks 2025-05-02 09:36:23 +02:00
parent fba596ce5a
commit 14d09dfa30
4 changed files with 139 additions and 77 deletions

View file

@ -271,6 +271,7 @@ impl Bytecode {
0xC1 => (Instruction::InstanceOf((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3), 0xC1 => (Instruction::InstanceOf((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
0xC2 => (Instruction::EnterMonitor(), 1), 0xC2 => (Instruction::EnterMonitor(), 1),
0xC3 => (Instruction::ExitMonitor(), 1), 0xC3 => (Instruction::ExitMonitor(), 1),
0xC5 => (Instruction::MultiNewArray((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16, self.bytes[offset+3]), 4),
0xC6 => { 0xC6 => {
let bytes = [self.bytes[offset+1], self.bytes[offset+2]]; let bytes = [self.bytes[offset+1], self.bytes[offset+2]];
@ -481,6 +482,7 @@ pub enum Instruction {
InstanceOf(u16) = 0xC1, // push integer result for success InstanceOf(u16) = 0xC1, // push integer result for success
EnterMonitor() = 0xC2, // enter the synchronization monitor of an object EnterMonitor() = 0xC2, // enter the synchronization monitor of an object
ExitMonitor() = 0xC3, // exit the synchronization monitor of an object ExitMonitor() = 0xC3, // exit the synchronization monitor of an object
MultiNewArray(u16, u8) = 0xC4, // multidimensional new array
BranchNull(i16) = 0xC6, // branch if Null BranchNull(i16) = 0xC6, // branch if Null
BranchNonNull(i16) = 0xC7, // branch if Null BranchNonNull(i16) = 0xC7, // branch if Null

View file

@ -213,6 +213,12 @@ impl JavaClassFile {
return None; return None;
} }
pub fn find_bootstrapmethods_attribute(&self) -> Option<&BootstrapMethodsData> {
self.attributes.iter()
.filter_map(|attr| match attr.data { AttributeData::BootstrapMethods(ref data) => Some(data), _ => None } )
.next()
}
pub fn pool_entry(&self, index: u16) -> Result<&ConstantPoolInfo, Error> { pub fn pool_entry(&self, index: u16) -> Result<&ConstantPoolInfo, Error> {
return pool_entry(&self.constant_pool, index); return pool_entry(&self.constant_pool, index);
} }
@ -261,6 +267,17 @@ impl JavaClassFile {
return Ok(fieldref_entry); return Ok(fieldref_entry);
} }
pub fn pool_invokedynamic_entry(&self, index: u16) -> Result<&ConstantInvokeDynamicInfo, Error> {
let pool_entry = self.pool_entry(index)?;
let fieldref_entry = match pool_entry {
ConstantPoolInfo::InvokeDynamic(data) => data,
_ => unreachable!(),
};
return Ok(fieldref_entry);
}
pub fn pool_class_entry(&self, index: u16) -> Result<&ConstantClassInfo, Error> { pub fn pool_class_entry(&self, index: u16) -> Result<&ConstantClassInfo, Error> {
let pool_entry = self.pool_entry(index)?; let pool_entry = self.pool_entry(index)?;
@ -715,8 +732,8 @@ impl NestMembersAttributeData {
#[derive(Debug)] #[derive(Debug)]
pub struct BootstrapMethodEntry { pub struct BootstrapMethodEntry {
bootstrap_method_ref: u16, pub bootstrap_method_ref: u16,
bootstrap_arguments: Box<[u16]> pub bootstrap_arguments: Box<[u16]>
} }
impl BootstrapMethodEntry { impl BootstrapMethodEntry {
@ -741,7 +758,7 @@ impl BootstrapMethodEntry {
#[derive(Debug)] #[derive(Debug)]
pub struct BootstrapMethodsData { pub struct BootstrapMethodsData {
bootstrap_methods: Box<[BootstrapMethodEntry]> pub bootstrap_methods: Box<[BootstrapMethodEntry]>
} }
impl BootstrapMethodsData { impl BootstrapMethodsData {

View file

@ -99,8 +99,8 @@ impl TryFrom<u8> for ConstantMethodHandleType {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ConstantMethodHandleInfo { pub struct ConstantMethodHandleInfo {
reference_kind: ConstantMethodHandleType, pub reference_kind: ConstantMethodHandleType,
reference_index: u16, pub reference_index: u16,
} }
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -110,8 +110,8 @@ pub struct ConstantMethodTypeInfo {
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
pub struct ConstantInvokeDynamicInfo { pub struct ConstantInvokeDynamicInfo {
bootstrap_method_attr_index: u16, pub bootstrap_method_attr_index: u16,
name_and_type_index: u16, pub name_and_type_index: u16,
} }

View file

@ -11,7 +11,7 @@ use crate::classfile;
use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData }; use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData };
use crate::classstore; use crate::classstore;
use crate::classstore::ClassStore; use crate::classstore::ClassStore;
use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info}; use crate::constantpool::{ ConstantClassInfo, ConstantInterfaceMethodRefInfo, ConstantMethodHandleType, ConstantMethodRefInfo, ConstantNameAndTypeInfo, ConstantPoolInfo, ConstantUtf8Info};
use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry }; use crate::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry };
use crate::iterators::{ ClassFieldIterator, ClassMethodIterator, CompatibleTypesIterator }; use crate::iterators::{ ClassFieldIterator, ClassMethodIterator, CompatibleTypesIterator };
use crate::native_methods; use crate::native_methods;
@ -1224,7 +1224,7 @@ impl JVM {
let long = match double { let long = match double {
f64::INFINITY => i64::MAX, f64::INFINITY => i64::MAX,
f64::NEG_INFINITY => i64::MIN, f64::NEG_INFINITY => i64::MIN,
v @ _ => if v.is_nan() { 0 } else { v as i64 } v => if v.is_nan() { 0 } else { v as i64 }
}; };
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long))?; wrap_stackframe_error(class, method, frame.operand_stack.push_long(long))?;
@ -1472,7 +1472,24 @@ impl JVM {
} }
Instruction::InvokeDynamic(invokedynamic_index, _zero) => { Instruction::InvokeDynamic(invokedynamic_index, _zero) => {
let methodhandle_reference = class.pool_methodhandle_entry(invokedynamic_index)?; let invokedynamic_info = class.pool_invokedynamic_entry(invokedynamic_index)?;
let bootstrap_table = class.find_bootstrapmethods_attribute().unwrap();
let (method_name, method_descriptor) = class.gather_nameandtype(invokedynamic_info.name_and_type_index).unwrap();
let bootstrap_entry = &bootstrap_table.bootstrap_methods[invokedynamic_info.bootstrap_method_attr_index as usize];
let method_handle = class.pool_methodhandle_entry(bootstrap_entry.bootstrap_method_ref).unwrap();
match method_handle.reference_kind {
ConstantMethodHandleType::RefGetField(_) => todo!(),
ConstantMethodHandleType::RefGetStatic(_) => todo!(),
ConstantMethodHandleType::RefPutField(_) => todo!(),
ConstantMethodHandleType::RefPutStatic(_) => todo!(),
ConstantMethodHandleType::RefInvokeVirtual(_) => todo!(),
ConstantMethodHandleType::RefInvokeStatic(_) => return self.perform_invokestatic(offset, method_handle.reference_index),
ConstantMethodHandleType::RefInvokeSpecial(_) => todo!(),
ConstantMethodHandleType::RefNewInvokeSpecial(_) => todo!(),
ConstantMethodHandleType::RefInvokeInterface(_) => todo!(),
}
} }
Instruction::InvokeInterface(methodref_index) => { Instruction::InvokeInterface(methodref_index) => {
@ -1587,71 +1604,8 @@ impl JVM {
}, },
Instruction::InvokeStatic(methodref_index) => { Instruction::InvokeStatic(methodref_index) => {
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref_compatible(methodref_index)?; let result = self.perform_invokestatic(offset, methodref_index);
return result;
if ! self.class_store.have_class(supplied_class_name) {
// rewind the bytecode offset, I'll need to execute this instruction again
frame.instruction_pointer -= offset as u32;
return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string()));
}
if ! self.class_store.was_init(supplied_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(supplied_class_name.to_string()));
}
let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?;
// TODO: Throw exception on fail
let (callee_class_file, callee_class_index) = self.class_store.get_class(supplied_class_name)?;
let (callee_class_index, callee_method_index, callee_method_info) = match ClassMethodIterator::new(callee_class_index, &self.class_store)
.filter(|(_, _, minfo)| minfo.name == *supplied_method_name)
.filter(|(_, _, minfo)| minfo.descriptor == supplied_descriptor)
.next() {
Some(m) => m,
None => {
// TODO: Throw exception
return Err(Error::RunTimeError(format!("InvokeStatic: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name)));
}
};
if ! (callee_method_info.access_flags & MethodAccessFlag::Static) {
// TODO: Throw IncompatibleClassChangeError
return Err(Error::RunTimeError(format!(
"Invoked method '{}' in class '{}' does not have Access::Static (from invokestatic from '{}' in class '{}')",
method.name,
class.get_classname().unwrap(),
supplied_method_name,
supplied_class_name,
)));
}
if supplied_descriptor != callee_method_info.descriptor {
// TODO: Throw exception on fail
return Err(Error::RunTimeError(format!(
"Mismatched method descriptors between caller and callee: Caller ({}) wanted '{}' but found '{}' on Callee ({})",
class.get_classname().unwrap(),
supplied_descriptor_string,
callee_method_info.descriptor.source_string(),
supplied_class_name,
)));
}
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,
callee_method_index as u16,
&arguments.make_contiguous(),
);
return Ok(JVMCallbackOperation::PushFrame(new_frame));
}, },
Instruction::InvokeVirtual(methodref_index) => { Instruction::InvokeVirtual(methodref_index) => {
@ -2011,6 +1965,20 @@ impl JVM {
frame.instruction_pointer = if jump_offset < 0 { frame.instruction_pointer - jump_offset.abs() as u32} else { frame.instruction_pointer + jump_offset.abs() as u32}; 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::MultiNewArray(type_index, dimension_count) => {
let dimension_count = dimension_count as usize;
let mut dimension_sizes = vec![0; dimension_count].into_boxed_slice();
for i in 0..dimension_count {
let dimension_size = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
dimension_sizes[dimension_count - i - 1] = dimension_size;
// top-most dimension is at the front
}
self.heap_area.make_multi_array(dimension_sizes);
unreachable!();
}
Instruction::MultiplyFloat() => { Instruction::MultiplyFloat() => {
let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?; let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
@ -2249,7 +2217,7 @@ impl JVM {
StackValue::Reference(r) => FieldValue::Reference(r), StackValue::Reference(r) => FieldValue::Reference(r),
StackValue::Float(f) => FieldValue::Float(f), StackValue::Float(f) => FieldValue::Float(f),
StackValue::Byte(b) => FieldValue::Byte(b), StackValue::Byte(b) => FieldValue::Byte(b),
stack_value @ _ => { stack_value => {
println!("{stack_value:?}"); println!("{stack_value:?}");
todo!() todo!()
} }
@ -2626,6 +2594,81 @@ impl JVM {
return &mut self.threads[self.active_thread]; return &mut self.threads[self.active_thread];
} }
fn perform_invokestatic(&mut self, offset: usize, reference_index: u16) -> Result<JVMCallbackOperation, Error> {
let frame = {
let thread = &mut self.threads[self.active_thread];
let frame_index = thread.stack_frames.len() - 1;
&mut thread.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 (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref_compatible(reference_index)?;
if ! self.class_store.have_class(supplied_class_name) {
// rewind the bytecode offset, I'll need to execute this instruction again
frame.instruction_pointer -= offset as u32;
return Ok(JVMCallbackOperation::LoadClass(supplied_class_name.to_string()));
}
if ! self.class_store.was_init(supplied_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(supplied_class_name.to_string()));
}
let supplied_descriptor: MethodDescriptor = supplied_descriptor_string.try_into()?;
// TODO: Throw exception on fail
let (callee_class_file, callee_class_index) = self.class_store.get_class(supplied_class_name)?;
let (callee_class_index, callee_method_index, callee_method_info) = match ClassMethodIterator::new(callee_class_index, &self.class_store)
.filter(|(_, _, minfo)| minfo.name == *supplied_method_name)
.filter(|(_, _, minfo)| minfo.descriptor == supplied_descriptor)
.next() {
Some(m) => m,
None => {
// TODO: Throw exception
return Err(Error::RunTimeError(format!("InvokeStatic: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name)));
}
};
if ! (callee_method_info.access_flags & MethodAccessFlag::Static) {
// TODO: Throw IncompatibleClassChangeError
return Err(Error::RunTimeError(format!(
"Invoked method '{}' in class '{}' does not have Access::Static (from invokestatic from '{}' in class '{}')",
method.name,
class.get_classname().unwrap(),
supplied_method_name,
supplied_class_name,
)));
}
if supplied_descriptor != callee_method_info.descriptor {
// TODO: Throw exception on fail
return Err(Error::RunTimeError(format!(
"Mismatched method descriptors between caller and callee: Caller ({}) wanted '{}' but found '{}' on Callee ({})",
class.get_classname().unwrap(),
supplied_descriptor_string,
callee_method_info.descriptor.source_string(),
supplied_class_name,
)));
}
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,
callee_method_index as u16,
&arguments.make_contiguous(),
);
return Ok(JVMCallbackOperation::PushFrame(new_frame));
}
} }
pub enum JVMCallbackOperation { pub enum JVMCallbackOperation {
@ -2763,7 +2806,7 @@ fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut Ve
) )
); );
}, },
AbstractTypeKind::Classname(ref name) => { AbstractTypeKind::Classname(ref _name) => {
// TODO: Type checking // TODO: Type checking
arguments.push_front( arguments.push_front(
StackValue::Reference( StackValue::Reference(