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),
|
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
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
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::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(
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue