feat: new instructions
This commit is contained in:
parent
fba596ce5a
commit
14d09dfa30
4 changed files with 139 additions and 77 deletions
|
@ -271,6 +271,7 @@ impl Bytecode {
|
|||
0xC1 => (Instruction::InstanceOf((self.bytes[offset+1] as u16) << 8 | self.bytes[offset+2] as u16), 3),
|
||||
0xC2 => (Instruction::EnterMonitor(), 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 => {
|
||||
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
|
||||
EnterMonitor() = 0xC2, // enter 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
|
||||
BranchNonNull(i16) = 0xC7, // branch if Null
|
||||
|
||||
|
|
|
@ -213,6 +213,12 @@ impl JavaClassFile {
|
|||
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> {
|
||||
return pool_entry(&self.constant_pool, index);
|
||||
}
|
||||
|
@ -261,6 +267,17 @@ impl JavaClassFile {
|
|||
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> {
|
||||
let pool_entry = self.pool_entry(index)?;
|
||||
|
||||
|
@ -715,8 +732,8 @@ impl NestMembersAttributeData {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct BootstrapMethodEntry {
|
||||
bootstrap_method_ref: u16,
|
||||
bootstrap_arguments: Box<[u16]>
|
||||
pub bootstrap_method_ref: u16,
|
||||
pub bootstrap_arguments: Box<[u16]>
|
||||
}
|
||||
|
||||
impl BootstrapMethodEntry {
|
||||
|
@ -741,7 +758,7 @@ impl BootstrapMethodEntry {
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct BootstrapMethodsData {
|
||||
bootstrap_methods: Box<[BootstrapMethodEntry]>
|
||||
pub bootstrap_methods: Box<[BootstrapMethodEntry]>
|
||||
}
|
||||
|
||||
impl BootstrapMethodsData {
|
||||
|
|
|
@ -99,8 +99,8 @@ impl TryFrom<u8> for ConstantMethodHandleType {
|
|||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ConstantMethodHandleInfo {
|
||||
reference_kind: ConstantMethodHandleType,
|
||||
reference_index: u16,
|
||||
pub reference_kind: ConstantMethodHandleType,
|
||||
pub reference_index: u16,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
|
@ -110,8 +110,8 @@ pub struct ConstantMethodTypeInfo {
|
|||
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct ConstantInvokeDynamicInfo {
|
||||
bootstrap_method_attr_index: u16,
|
||||
name_and_type_index: u16,
|
||||
pub bootstrap_method_attr_index: u16,
|
||||
pub name_and_type_index: u16,
|
||||
}
|
||||
|
||||
|
||||
|
|
183
src/jvm.rs
183
src/jvm.rs
|
@ -11,7 +11,7 @@ use crate::classfile;
|
|||
use crate::classfile::{ JavaClassFile, FieldInfo, MethodInfo, MethodDescriptor, AbstractTypeDescription, AbstractTypeKind, AttributeInfo, AttributeData, CodeAttributeData, ConstantValueAttributeData };
|
||||
use crate::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::iterators::{ ClassFieldIterator, ClassMethodIterator, CompatibleTypesIterator };
|
||||
use crate::native_methods;
|
||||
|
@ -1224,7 +1224,7 @@ impl JVM {
|
|||
let long = match double {
|
||||
f64::INFINITY => i64::MAX,
|
||||
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))?;
|
||||
|
@ -1472,7 +1472,24 @@ impl JVM {
|
|||
}
|
||||
|
||||
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) => {
|
||||
|
@ -1587,71 +1604,8 @@ impl JVM {
|
|||
},
|
||||
|
||||
Instruction::InvokeStatic(methodref_index) => {
|
||||
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref_compatible(methodref_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));
|
||||
let result = self.perform_invokestatic(offset, methodref_index);
|
||||
return result;
|
||||
},
|
||||
|
||||
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};
|
||||
}
|
||||
|
||||
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() => {
|
||||
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))?;
|
||||
|
@ -2249,7 +2217,7 @@ impl JVM {
|
|||
StackValue::Reference(r) => FieldValue::Reference(r),
|
||||
StackValue::Float(f) => FieldValue::Float(f),
|
||||
StackValue::Byte(b) => FieldValue::Byte(b),
|
||||
stack_value @ _ => {
|
||||
stack_value => {
|
||||
println!("{stack_value:?}");
|
||||
todo!()
|
||||
}
|
||||
|
@ -2626,6 +2594,81 @@ impl JVM {
|
|||
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 {
|
||||
|
@ -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
|
||||
arguments.push_front(
|
||||
StackValue::Reference(
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue