2813 lines
144 KiB
Rust
2813 lines
144 KiB
Rust
use crate::virtual_thread::VirtualThread;
|
|
use crate::classfile::ExceptionTableEntry;
|
|
use core::fmt::{Display, Formatter};
|
|
|
|
use std::collections::VecDeque;
|
|
use std::error::Error as ErrorTrait;
|
|
|
|
use crate::accessmasks::{ ClassAccessFlagMask, ClassAccessFlag, MethodAccessFlagMask, MethodAccessFlag, FieldAccessFlag, FieldAccessFlagMask };
|
|
use crate::bytecode::{ Bytecode, Instruction };
|
|
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::heap_area::{ HeapArea, FieldValue, ObjectReference, CompartmentEntry };
|
|
use crate::iterators::{ ClassFieldIterator, ClassMethodIterator, CompatibleTypesIterator };
|
|
use crate::native_methods;
|
|
use crate::native_methods::EntryPoint;
|
|
use crate::native_registry::NativeRegistry;
|
|
use crate::stackframe;
|
|
use crate::stackframe::{ StackFrame, StackValue, OperandStack };
|
|
|
|
#[derive(Debug)]
|
|
pub enum Error {
|
|
ClassStoreError(classstore::Error),
|
|
ClassFileError(classfile::Error),
|
|
StackFrameError(stackframe::Error, String),
|
|
BadNameError(String),
|
|
RunTimeError(String),
|
|
OpcodeError(String),
|
|
}
|
|
|
|
impl From<classfile::Error> for Error {
|
|
fn from(value: classfile::Error) -> Self {
|
|
return Error::ClassFileError(value);
|
|
}
|
|
}
|
|
|
|
impl From<classstore::Error> for Error {
|
|
fn from(value: classstore::Error) -> Self {
|
|
return Error::ClassStoreError(value);
|
|
}
|
|
}
|
|
|
|
impl ErrorTrait for Error {}
|
|
impl Display for Error {
|
|
fn fmt(&self, formatter: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
|
|
writeln!(formatter, "{self}")?;
|
|
if let Some(e) = self.source() {
|
|
writeln!(formatter, "\tCaused by: {e:?}")?;
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct JVM {
|
|
pub class_store: ClassStore,
|
|
pub heap_area: HeapArea,
|
|
pub native_registry: NativeRegistry,
|
|
pub threads: Vec<VirtualThread>,
|
|
pub active_thread: usize
|
|
}
|
|
|
|
impl JVM {
|
|
pub fn new() -> Self {
|
|
return JVM {
|
|
class_store: ClassStore::new(),
|
|
heap_area: HeapArea::new(usize::MAX),
|
|
native_registry: NativeRegistry::default(),
|
|
threads: Vec::new(),
|
|
active_thread: 0,
|
|
}
|
|
}
|
|
|
|
fn load_class(&mut self, class_name: &String) -> Result<usize, Error> {
|
|
let class_index = self.class_store.load_class(class_name)?;
|
|
|
|
let class_file = self.class_store.class_file_from_idx(class_index).unwrap();
|
|
|
|
for (method_name, method_implementation) in class_file.methods.iter()
|
|
.filter(|m| m.access_flags & MethodAccessFlag::Native)
|
|
.map(| m | (&m.name, native_methods::function_for(class_name, m))) {
|
|
|
|
let method_implementation = match method_implementation {
|
|
Ok(m) => m,
|
|
Err(e) => return Err(e)
|
|
};
|
|
|
|
self.native_registry.register(class_name, method_name, method_implementation);
|
|
}
|
|
|
|
Ok(class_index)
|
|
}
|
|
|
|
fn class_native_class_data() -> JavaClassFile {
|
|
JavaClassFile {
|
|
minor_version: 0,
|
|
major_version: 0,
|
|
constant_pool: Box::new([
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::NativeClassData".to_string() }),
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 4 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Object".to_string() }),
|
|
]
|
|
),
|
|
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
|
this_class: 1,
|
|
super_class: 3,
|
|
interfaces: Box::new([]),
|
|
fields: Box::new([
|
|
FieldInfo {
|
|
access_flags: FieldAccessFlagMask {
|
|
mask: FieldAccessFlag::Public.discriminant()
|
|
},
|
|
name: "native_class_descriptor_index".to_string(),
|
|
descriptor: AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Int(),
|
|
},
|
|
attributes: Box::new([]),
|
|
},
|
|
]),
|
|
methods: Box::new([]),
|
|
attributes: Box::new([]),
|
|
}
|
|
}
|
|
|
|
fn make_class_class(&mut self, class_name: &str) {
|
|
let class_object = self.heap_area.make_object(&self.class_store, self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap());
|
|
let class_data_object = self.heap_area.make_object(&self.class_store, self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap());
|
|
|
|
self.heap_area.object_area.set_object_field(
|
|
class_object,
|
|
"classData",
|
|
FieldValue::Reference(class_data_object),
|
|
self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap(),
|
|
&self.class_store,
|
|
).unwrap();
|
|
// set native name index on class data
|
|
self.heap_area.object_area.set_object_field(
|
|
class_data_object,
|
|
"native_class_descriptor_index",
|
|
FieldValue::Int(self.class_store.add_native_class_descriptor(class_name.to_string()) as i32),
|
|
self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(),
|
|
&self.class_store,
|
|
).unwrap();
|
|
|
|
let parsed_class_name = match AbstractTypeDescription::parse_first(class_name).unwrap() {
|
|
(_, desc) => {
|
|
assert!(desc.array_level == 0);
|
|
match desc.kind {
|
|
AbstractTypeKind::Classname(name) => name,
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
};
|
|
self.class_store.set_class_objectref_by_index(self.class_store.class_idx_from_name(&parsed_class_name).unwrap(), class_object);
|
|
}
|
|
|
|
fn make_array_class(&mut self, element_class_ref: ObjectReference, element_descriptor: AbstractTypeDescription) {
|
|
let class_class_index = self.class_store.class_idx_from_name(&"java/lang/Class".to_string()).unwrap();
|
|
|
|
let array_class_object = self.heap_area.make_object(&self.class_store, class_class_index);
|
|
let array_class_data_object = self.heap_area.make_object(&self.class_store, 1);
|
|
let array_type_description = AbstractTypeDescription {
|
|
array_level: 1 + element_descriptor.array_level,
|
|
kind: element_descriptor.kind,
|
|
};
|
|
// set component type
|
|
self.heap_area.object_area.set_object_field(
|
|
array_class_object,
|
|
"componentType",
|
|
FieldValue::Reference(element_class_ref),
|
|
class_class_index,
|
|
&self.class_store,
|
|
).unwrap();
|
|
// set classdata object
|
|
self.heap_area.object_area.set_object_field(
|
|
array_class_object,
|
|
"classData",
|
|
FieldValue::Reference(array_class_data_object),
|
|
class_class_index,
|
|
&self.class_store,
|
|
).unwrap();
|
|
// set native name index on class data
|
|
self.heap_area.object_area.set_object_field(
|
|
array_class_data_object,
|
|
"native_class_descriptor_index",
|
|
FieldValue::Int(self.class_store.add_native_class_descriptor((&array_type_description).into()) as i32),
|
|
self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(),
|
|
&self.class_store,
|
|
).unwrap();
|
|
|
|
let array_class_file = JavaClassFile {
|
|
minor_version: 0,
|
|
major_version: 63,
|
|
constant_pool: Box::new(
|
|
[
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: (&array_type_description).into() }),
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 4 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Object".to_string() }),
|
|
]
|
|
),
|
|
access_flags: ClassAccessFlagMask {
|
|
mask: ClassAccessFlag::Public.discriminant() | ClassAccessFlag::Synthetic.discriminant()
|
|
},
|
|
this_class: 1,
|
|
super_class: 3,
|
|
interfaces: Box::new([]),
|
|
fields: Box::new([]),
|
|
methods: Box::new([]),
|
|
attributes: Box::new([]),
|
|
};
|
|
|
|
let class_index = self.class_store.add_class(array_class_file, true).unwrap();
|
|
self.class_store.set_class_objectref_by_index(class_index, array_class_object);
|
|
|
|
self.class_store.put_array_class_ref(
|
|
array_type_description,
|
|
array_class_object,
|
|
);
|
|
}
|
|
|
|
fn make_primitive_class(&mut self, class_name: &str, class_descriptor: &str, string_names: bool) -> ObjectReference {
|
|
let class_class_index = self.class_store.class_idx_from_name(&String::from("java/lang/Class")).unwrap();
|
|
let data_class_index = self.class_store.class_idx_from_name(&String::from("::NativeClassData")).unwrap();
|
|
|
|
let primitive_class_object = self.heap_area.make_object(&self.class_store, class_class_index);
|
|
let primitive_class_data_object = self.heap_area.make_object(&self.class_store, data_class_index);
|
|
|
|
// set classdata object
|
|
self.heap_area.object_area.set_object_field(
|
|
primitive_class_object,
|
|
"classData",
|
|
FieldValue::Reference(primitive_class_data_object),
|
|
class_class_index,
|
|
&self.class_store,
|
|
).unwrap();
|
|
|
|
if string_names {
|
|
let name_string_ref = self.heap_area.make_handmade_string(&class_name.into(), &self.class_store);
|
|
// set name string object
|
|
self.heap_area.object_area.set_object_field(
|
|
primitive_class_object,
|
|
"name",
|
|
FieldValue::Reference(name_string_ref),
|
|
class_class_index,
|
|
&self.class_store,
|
|
).unwrap();
|
|
}
|
|
|
|
// set native name index on class data
|
|
self.heap_area.object_area.set_object_field(
|
|
primitive_class_data_object,
|
|
"native_class_descriptor_index",
|
|
FieldValue::Int(self.class_store.add_native_class_descriptor(class_descriptor.into()) as i32),
|
|
self.class_store.class_idx_from_name(&"::NativeClassData".to_string()).unwrap(),
|
|
&self.class_store,
|
|
).unwrap();
|
|
|
|
// TODO: Set Static
|
|
|
|
primitive_class_object
|
|
}
|
|
|
|
pub fn entrypoint(&mut self, class_name: &String, method_name: &String, arguments: &[&str]) -> Result<(), Error> {
|
|
let entry_class = JavaClassFile {
|
|
minor_version: 0,
|
|
major_version: 0,
|
|
constant_pool: Box::new([
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 2 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "::EntryPoint".to_string() }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "Code".to_string() }),
|
|
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 5, name_and_type_index: 6}),
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 7 }), // 5
|
|
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 8, descriptor_index: 9 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: class_name.to_string() }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: method_name.to_string() }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "([Ljava/lang/String;)V".to_string() }),
|
|
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 1, name_and_type_index: 11}), // 10
|
|
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 12, descriptor_index: 13 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "populateUnsafeConstants".to_string() }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "()V".to_string() }),
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 15 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "jdk/internal/misc/UnsafeConstants".to_string() }), // 15
|
|
ConstantPoolInfo::MethodRef(ConstantMethodRefInfo { class_index: 17, name_and_type_index: 19}),
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 18 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/System".to_string() }),
|
|
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 20, descriptor_index: 13 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "initPhase1".to_string() }), // 20
|
|
ConstantPoolInfo::InterfaceMethodRef(ConstantInterfaceMethodRefInfo { class_index: 22, name_and_type_index: 24}),
|
|
ConstantPoolInfo::Class(ConstantClassInfo { name_index: 23 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "java/lang/Throwable".to_string() }),
|
|
ConstantPoolInfo::NameAndType(ConstantNameAndTypeInfo { name_index: 25, descriptor_index: 13 }),
|
|
ConstantPoolInfo::Utf8(ConstantUtf8Info { utf8: "printStackTrace".to_string() }), // 25
|
|
]
|
|
),
|
|
access_flags: ClassAccessFlagMask { mask: ClassAccessFlag::Super.discriminant() },
|
|
this_class: 1,
|
|
super_class: 0,
|
|
interfaces: Box::new([]),
|
|
fields: Box::new([]),
|
|
methods: Box::new([
|
|
MethodInfo {
|
|
access_flags: MethodAccessFlagMask {
|
|
mask: MethodAccessFlag::Private.discriminant() | MethodAccessFlag::Static.discriminant()
|
|
},
|
|
name: "call_main".to_string(),
|
|
descriptor: MethodDescriptor {
|
|
argument_types: Box::new([]),
|
|
return_type: AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Void(),
|
|
}
|
|
},
|
|
code_attribute_index: 0,
|
|
attributes: Box::new([
|
|
AttributeInfo {
|
|
attribute_name_index: 3,
|
|
data: AttributeData::Code(
|
|
CodeAttributeData {
|
|
max_stack: 1,
|
|
max_locals: 1,
|
|
code: Bytecode {
|
|
bytes: Box::new([
|
|
// access something from UnsafeConstants
|
|
0x12_u8.to_be(), // ldc
|
|
0x0E_u8.to_be(), // index 14 into the constant pool
|
|
0x57_u8.to_be(), // pop
|
|
|
|
// Update UnsafeConstants to actual values
|
|
0xb8_u8.to_be(), // invokestatic
|
|
0x0A_u16.to_be_bytes()[0], // index 10 into the constant
|
|
0x0A_u16.to_be_bytes()[1], // pool
|
|
|
|
// call initPhase1
|
|
0xb8_u8.to_be(), // invokestatic
|
|
0x10_u16.to_be_bytes()[0], // index 10 into the constant
|
|
0x10_u16.to_be_bytes()[1], // pool
|
|
|
|
0x2A_u8.to_be(), // aload_0
|
|
0xB8_u8.to_be(), // invokestatic
|
|
0x04_u16.to_be_bytes()[0], // index 4 into the constant
|
|
0x04_u16.to_be_bytes()[1], // pool
|
|
|
|
0xB1_u8.to_be(), // returnvoid
|
|
// index 14
|
|
|
|
0xB9_u8.to_be(), // invokeinterface
|
|
21_u16.to_be_bytes()[0], // index 21 constant
|
|
21_u16.to_be_bytes()[1], // index 21 constant
|
|
0x00_u8.to_be(), // constant 0
|
|
]),
|
|
},
|
|
exception_table: Box::new([
|
|
ExceptionTableEntry {
|
|
start_pc: 0,
|
|
end_pc: u16::MAX,
|
|
handler_pc: 14,
|
|
catch_type: 0, // Catchall
|
|
}
|
|
]),
|
|
attributes: Box::new([]),
|
|
}
|
|
)
|
|
}
|
|
])
|
|
},
|
|
MethodInfo {
|
|
access_flags: MethodAccessFlagMask {
|
|
mask: MethodAccessFlag::Private.discriminant() | MethodAccessFlag::Static.discriminant() | MethodAccessFlag::Native.discriminant()
|
|
},
|
|
name: "populateUnsafeConstants".to_string(),
|
|
descriptor: MethodDescriptor {
|
|
argument_types: Box::new([]),
|
|
return_type: AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Void(),
|
|
}
|
|
},
|
|
code_attribute_index: 0,
|
|
attributes: Box::new([])
|
|
}
|
|
]),
|
|
attributes: Box::new([]),
|
|
};
|
|
|
|
self.native_registry.register("::EntryPoint", "populateUnsafeConstants", EntryPoint::populate_unsafe_constants);
|
|
|
|
self.class_store.add_class(entry_class, true)?; // 0
|
|
self.class_store.add_class(JVM::class_native_class_data(), true)?; // 1
|
|
self.load_class_hierarchy(&"java/lang/Object".to_string())?; // 2
|
|
self.load_class_hierarchy(&"java/lang/Number".to_string())?; // 3
|
|
let byte_class_index = self.load_class_hierarchy(&"java/lang/Byte".to_string())?; // 4
|
|
let string_class_index = self.load_class_hierarchy(&"java/lang/String".to_string())?; // 5
|
|
let class_class_index = self.load_class_hierarchy(&"java/lang/Class".to_string())?; // 6
|
|
let system_class_index = self.load_class_hierarchy(&"java/lang/System".to_string())?; // 7
|
|
|
|
self.make_class_class("Ljava/lang/Byte;");
|
|
self.make_class_class("Ljava/lang/String;");
|
|
self.make_array_class(
|
|
self.class_store.get_class_objectref_from_index(byte_class_index),
|
|
AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Classname("java/lang/Byte".into()),
|
|
}
|
|
);
|
|
self.make_array_class(
|
|
self.class_store.get_class_objectref_from_index(string_class_index),
|
|
AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Classname("java/lang/String".into()),
|
|
}
|
|
);
|
|
|
|
self.heap_area.fill_byte_cache(&self.class_store);
|
|
|
|
self.class_store.primitive_classes.byte_class = self.make_primitive_class("byte", "B", false);
|
|
self.make_array_class(
|
|
self.class_store.primitive_classes.byte_class,
|
|
AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Byte(),
|
|
}
|
|
);
|
|
{
|
|
// we can only make a byte class name after the byte array is loaded
|
|
let string_ref = self.heap_area.make_handmade_string(&String::from("byte"), &self.class_store);
|
|
self.heap_area.object_area.set_object_field(
|
|
self.class_store.primitive_classes.byte_class,
|
|
"name",
|
|
FieldValue::Reference(string_ref),
|
|
class_class_index,
|
|
&self.class_store
|
|
).unwrap();
|
|
}
|
|
self.class_store.primitive_classes.int_class = self.make_primitive_class("int", "I", true);
|
|
self.class_store.primitive_classes.char_class = self.make_primitive_class("char", "C", true);
|
|
self.class_store.primitive_classes.long_class = self.make_primitive_class("long", "J", true);
|
|
self.class_store.primitive_classes.float_class = self.make_primitive_class("float", "F", true);
|
|
self.class_store.primitive_classes.short_class = self.make_primitive_class("short", "S", true);
|
|
self.class_store.primitive_classes.double_class = self.make_primitive_class("double", "D", true);
|
|
self.class_store.primitive_classes.boolean_class = self.make_primitive_class("boolean", "Z", true);
|
|
|
|
self.make_array_class(
|
|
self.class_store.primitive_classes.char_class,
|
|
AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Char(),
|
|
}
|
|
);
|
|
self.make_array_class(
|
|
self.class_store.primitive_classes.int_class,
|
|
AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Int(),
|
|
}
|
|
);
|
|
|
|
|
|
let string_refs = arguments.iter()
|
|
.map(|s| self.heap_area.make_handmade_string(&s.to_string(), &self.class_store))
|
|
.collect();
|
|
let argument_array_ref = self.heap_area.make_array(&self.class_store, string_refs);
|
|
|
|
self.setup_main_thread()?;
|
|
|
|
// push the entry frame which will call main
|
|
let entry_frame = StackFrame::new(self.class_store.get_class(&String::from("::EntryPoint")).unwrap().0, 0, 0, &[StackValue::Reference(argument_array_ref)]);
|
|
self.active_thread_mut().add_frame(entry_frame);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn setup_main_thread(&mut self) -> Result<(), Error> {
|
|
let thread_group_class_index = self.load_class_hierarchy(&"java/lang/ThreadGroup".to_string())?;
|
|
let main_thread_group_reference = self.heap_area.make_object(&self.class_store, thread_group_class_index);
|
|
|
|
let holder_class_index = self.load_class_hierarchy(&"java/lang/Thread$FieldHolder".to_string())?;
|
|
let main_thread_holder_reference = self.heap_area.make_object(&self.class_store, holder_class_index);
|
|
self.heap_area.object_area.set_object_field(
|
|
main_thread_holder_reference,
|
|
"threadStatus",
|
|
FieldValue::Int(1), // alive
|
|
holder_class_index,
|
|
&self.class_store
|
|
)?;
|
|
self.heap_area.object_area.set_object_field(
|
|
main_thread_holder_reference,
|
|
"group",
|
|
main_thread_group_reference.into(),
|
|
holder_class_index,
|
|
&self.class_store
|
|
)?;
|
|
|
|
let thread_class_index = self.load_class_hierarchy(&"java/lang/Thread".to_string())?;
|
|
let main_thread_reference = {
|
|
self.heap_area.make_object(&self.class_store, thread_class_index)
|
|
};
|
|
self.heap_area.object_area.set_object_field(
|
|
main_thread_reference,
|
|
"eetop",
|
|
FieldValue::Long(0), // thread array index
|
|
thread_class_index,
|
|
&self.class_store
|
|
)?;
|
|
let main_thread_name = self.heap_area.make_handmade_string(&"main".to_string(), &self.class_store);
|
|
self.heap_area.object_area.set_object_field(
|
|
main_thread_reference,
|
|
"name",
|
|
FieldValue::Reference(main_thread_name),
|
|
thread_class_index,
|
|
&self.class_store
|
|
)?;
|
|
self.heap_area.object_area.set_object_field(
|
|
main_thread_reference,
|
|
"holder",
|
|
FieldValue::Reference(main_thread_holder_reference),
|
|
thread_class_index,
|
|
&self.class_store
|
|
)?;
|
|
|
|
|
|
self.threads.push(
|
|
VirtualThread::new(main_thread_reference)
|
|
);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn run(&mut self) -> Result<(), Error> {
|
|
while self.active_thread_mut().stack_frames.len() != 0 {
|
|
|
|
let jvm_op = self.bytecode_loop()?;
|
|
match jvm_op {
|
|
JVMCallbackOperation::PopFrame() => {
|
|
let frame_count = self.active_thread_mut().stack_frames.len() - 1;
|
|
self.active_thread_mut().stack_frames.truncate(frame_count);
|
|
},
|
|
|
|
JVMCallbackOperation::ReturnFrame(value) => {
|
|
// Pop returning frame
|
|
let frame_count = self.active_thread_mut().stack_frames.len() - 1;
|
|
self.active_thread_mut().stack_frames.truncate(frame_count);
|
|
|
|
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];
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(value))?;
|
|
}
|
|
|
|
JVMCallbackOperation::PushFrame(frame) => self.active_thread_mut().stack_frames.push(frame),
|
|
|
|
JVMCallbackOperation::LoadClass(name) => {
|
|
// TODO: throw exception
|
|
self.load_class_hierarchy(&name)?;
|
|
},
|
|
|
|
JVMCallbackOperation::InitClass(name) => {
|
|
// TODO: throw exception
|
|
self.init_class_hierarchy(&name)?;
|
|
},
|
|
|
|
JVMCallbackOperation::MakeArrayClass(component_class_ref, component_descriptor) => {
|
|
self.make_array_class(component_class_ref, component_descriptor);
|
|
}
|
|
|
|
JVMCallbackOperation::ThrowException(exception) => {
|
|
let mut is_handler_found = false;
|
|
let exception_type_index = self.heap_area.object_area.get_object_class_index(exception);
|
|
while ! is_handler_found {
|
|
is_handler_found = {
|
|
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];
|
|
if method.is_native() {
|
|
false
|
|
} else {
|
|
class.is_method_bytecode_protected(method, frame.instruction_pointer as u16, exception_type_index, &self.class_store)?
|
|
}
|
|
};
|
|
|
|
if ! is_handler_found {
|
|
self.active_thread_mut().stack_frames.pop();
|
|
}
|
|
}
|
|
|
|
if is_handler_found {
|
|
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];
|
|
frame.operand_stack.clear();
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(exception)))?;
|
|
frame.instruction_pointer = method.get_protected_handler_pc(frame.instruction_pointer as u16).unwrap() as u32;
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn load_class_hierarchy(&mut self, name: &String) -> Result<usize, Error> {
|
|
let mut waiting_queue = VecDeque::new();
|
|
waiting_queue.push_back(name.clone());
|
|
let mut return_class_index = usize::MAX;
|
|
|
|
while waiting_queue.len() != 0 {
|
|
let class_name = waiting_queue.pop_front().unwrap();
|
|
|
|
if ! self.class_store.have_class(&class_name) {
|
|
let new_class_index = self.load_class(&class_name)?;
|
|
|
|
if class_name == *name {
|
|
return_class_index = new_class_index;
|
|
}
|
|
|
|
let (file, _) = self.class_store.get_class(&class_name).unwrap();
|
|
if file.has_super_class() {
|
|
waiting_queue.push_back(file.get_super_class_name()?.to_string());
|
|
}
|
|
|
|
for interface_index in &file.interfaces {
|
|
let interface_name = file.gather_class(*interface_index)?;
|
|
waiting_queue.push_back(interface_name.to_string());
|
|
}
|
|
println!("Loaded Class {class_name} ({new_class_index})");
|
|
}
|
|
}
|
|
|
|
Ok(return_class_index)
|
|
}
|
|
|
|
fn init_class_hierarchy(&mut self, name: &String) -> Result<(), Error> {
|
|
// TODO: Work around the clones
|
|
|
|
let mut current_name = name.clone();
|
|
|
|
while ! self.class_store.was_init(¤t_name).unwrap() {
|
|
let class_index = self.class_store.class_idx_from_name(¤t_name).unwrap();
|
|
self.init_class(class_index)?;
|
|
|
|
let class_file = self.class_store.class_file_from_idx(class_index).unwrap();
|
|
|
|
if class_file.has_super_class() {
|
|
current_name = class_file.get_super_class_name().unwrap().clone();
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn init_class(&mut self, class_idx: usize) -> Result<(), Error> {
|
|
{
|
|
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
|
|
|
self.heap_area.make_static(class_file, class_idx);
|
|
|
|
for field in &class_file.fields {
|
|
if field.access_flags & FieldAccessFlag::Static {
|
|
let cvalue_attrs: Vec<&ConstantValueAttributeData> = (&field.attributes).iter()
|
|
.filter(|a| match a.data { AttributeData::ConstantValue(_) => true, _ => false })
|
|
.map(|a| match &a.data { AttributeData::ConstantValue(c) => c, _ => unreachable!() })
|
|
.collect();
|
|
|
|
assert!(cvalue_attrs.len() < 2);
|
|
// TODO: Throw error
|
|
|
|
if cvalue_attrs.len() == 1 {
|
|
let constant_value_info = cvalue_attrs[0];
|
|
|
|
assert!(field.descriptor.array_level == 0);
|
|
// TODO: Throw Error
|
|
|
|
let field_value = match field.descriptor.kind {
|
|
AbstractTypeKind::Boolean() => {
|
|
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Boolean(int_entry.value != 0)
|
|
},
|
|
AbstractTypeKind::Byte() => {
|
|
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Byte(int_entry.value as i8)
|
|
},
|
|
AbstractTypeKind::Char() => {
|
|
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Char(int_entry.value as u16)
|
|
},
|
|
AbstractTypeKind::Int() => {
|
|
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Int(int_entry.value)
|
|
},
|
|
AbstractTypeKind::Short() => {
|
|
let int_entry = class_file.pool_int_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Short(int_entry.value as i16)
|
|
},
|
|
AbstractTypeKind::Long() => {
|
|
let long_entry = class_file.pool_long_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Long(long_entry.value)
|
|
},
|
|
AbstractTypeKind::Float() => {
|
|
let float_entry = class_file.pool_float_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Float(float_entry.value)
|
|
},
|
|
AbstractTypeKind::Double() => {
|
|
let double_entry = class_file.pool_double_entry(constant_value_info.constant_value_index)?;
|
|
|
|
FieldValue::Double(double_entry.value)
|
|
}
|
|
AbstractTypeKind::Classname(ref name) => {
|
|
if name == "java/lang/String" {
|
|
let string_entry = class_file.gather_string(constant_value_info.constant_value_index)?;
|
|
|
|
let string_object = self.heap_area.make_handmade_string(string_entry, &self.class_store);
|
|
|
|
FieldValue::Reference(string_object)
|
|
} else {
|
|
todo!()
|
|
}
|
|
},
|
|
|
|
_ => {
|
|
println!("{:?}", field.descriptor.kind);
|
|
todo!()
|
|
},
|
|
};
|
|
|
|
self.heap_area.static_area.set(class_file.get_classname()?, &field.name, field_value)?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// class object
|
|
self.make_class_class(&("L".to_owned() + &self.class_store.class_file_from_idx(class_idx).unwrap().get_classname()?.clone() + ";"));
|
|
|
|
// Push clinit function
|
|
let class_file = self.class_store.class_file_from_idx(class_idx).unwrap();
|
|
let clinit_idx = class_file.find_method_index(&"<clinit>".to_string());
|
|
|
|
if let Some(method_index) = clinit_idx {
|
|
let clinit_frame = StackFrame::new(
|
|
class_file,
|
|
class_idx,
|
|
method_index as u16,
|
|
&[],
|
|
);
|
|
|
|
self.active_thread_mut().add_frame(clinit_frame);
|
|
}
|
|
|
|
// finish
|
|
self.class_store.set_init(class_idx, true);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn native_call(&mut self) -> 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 class_name = class.get_classname().unwrap();
|
|
|
|
let native_method = match self.native_registry.get(class_name, &method.name) {
|
|
Some(m) => m,
|
|
None => {
|
|
return Err(Error::RunTimeError(format!("Tried to call native method '{class_name}.{}' but there is no such method in the method registry", method.name)));
|
|
}
|
|
};
|
|
|
|
native_method(self)
|
|
}
|
|
|
|
fn bytecode_loop(&mut self) -> Result<JVMCallbackOperation, Error> {
|
|
//println!("Enter bytecode loop:");
|
|
|
|
let frame_index = self.active_thread_mut().stack_frames.len() - 1;
|
|
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];
|
|
|
|
if method.access_flags & MethodAccessFlag::Native {
|
|
println!("{:25}.{:15}: (native)", class.get_classname().unwrap(), method.name);
|
|
return self.native_call()
|
|
}
|
|
|
|
let code_attr = method.get_code_attribute().unwrap();
|
|
let bytecode = & code_attr.code;
|
|
|
|
while frame.instruction_pointer as usize != bytecode.bytes.len() {
|
|
let (instruction, offset) = bytecode.next_instruction(frame.instruction_pointer as usize);
|
|
frame.instruction_pointer += offset as u32;
|
|
|
|
println!("{} locals: {:?}", " ".repeat(frame_index), frame.locals);
|
|
println!("{} stack: {:?}", " ".repeat(frame_index), frame.operand_stack);
|
|
println!("{}{}.{}:{:<10}{instruction:?}\n", " ".repeat(frame_index), class.get_classname().unwrap(), method.name, frame.instruction_pointer);
|
|
|
|
match instruction {
|
|
|
|
Instruction::AddDouble() => {
|
|
let value_0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_double(value_0 + value_1))?;
|
|
}
|
|
|
|
Instruction::AddInt() => {
|
|
let value_0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_0 + value_1)))?;
|
|
}
|
|
|
|
Instruction::AddLong() => {
|
|
let value_0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(value_0 + value_1))?;
|
|
}
|
|
|
|
Instruction::ArithmeticShiftIntLeft() => {
|
|
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))? as u8 & 0b00011111;
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
// rust does arithmetic shift on singed values
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int << shift)))?;
|
|
}
|
|
Instruction::ArithmeticShiftIntRight() => {
|
|
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 & 0b00011111;
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
// rust does arithmetic shift on singed values
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int >> shift)))?;
|
|
}
|
|
|
|
Instruction::ArithmeticShiftLongLeft() => {
|
|
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 % 64;
|
|
let long = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
// rust does arithmetic shift on signed values
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long << shift))?;
|
|
}
|
|
|
|
Instruction::ArithmeticShiftLongRight() => {
|
|
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 % 64;
|
|
let long = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
// rust does arithmetic shift on signed values
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long >> shift))?;
|
|
}
|
|
|
|
Instruction::ArrayLength() => {
|
|
let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
let array_length = self.heap_area.object_area.get_array_length(array_reference);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(array_length as i32)))?;
|
|
},
|
|
|
|
Instruction::ArrayElement() => {
|
|
let element_index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array_reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
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))?;
|
|
}
|
|
|
|
Instruction::AndInt() => {
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_1 & value_2)))?;
|
|
}
|
|
|
|
Instruction::AndLong() => {
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(value_1 & value_2))?;
|
|
}
|
|
|
|
Instruction::BranchAlways(branch_offset) => {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
|
|
Instruction::BranchIntEquality(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
if value_1 == value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchIntGreaterEquals(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
if value_1 >= value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchIntGreaterThan(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
if value_1 > value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchIntInequality(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
if value_1 != value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchIntLessEquals(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
if value_1 <= value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchIntLessThan(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if value_1 < value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchNegative(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if test_value < 0 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchNonNull(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
if test_value != ObjectReference::NULL {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchNonNegative(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if test_value >= 0 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchNonPositive(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if test_value <= 0 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchNonZero(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if test_value != 0 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchNull(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
if test_value == ObjectReference::NULL {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchPositive(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if test_value > 0 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchReferenceEquality(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
if value_1 == value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchReferenceInequality(branch_offset) => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
if value_1 != value_2 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::BranchZero(branch_offset) => {
|
|
let test_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
if test_value == 0 {
|
|
frame.instruction_pointer -= offset as u32;
|
|
frame.instruction_pointer = if branch_offset < 0 { frame.instruction_pointer - branch_offset.abs() as u32} else { frame.instruction_pointer + branch_offset.abs() as u32};
|
|
}
|
|
}
|
|
|
|
Instruction::CheckCast(classref_index) => {
|
|
// TODO: Class loading checks
|
|
let class_name = class.gather_class(classref_index)?;
|
|
if ! class_name.starts_with("[") { // don't load array classes
|
|
if ! self.class_store.have_class(class_name) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(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(class_name.to_string()));
|
|
}
|
|
}
|
|
|
|
let object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
if object != ObjectReference::NULL {
|
|
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_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
|
|
return Err(Error::RunTimeError(format!("Trying to cast an object of type {native_class_name} to {class_name}")))
|
|
}
|
|
}
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(object)))?;
|
|
|
|
}
|
|
|
|
Instruction::CompareDoubleG() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
|
|
let comparison_result = if value_1.is_nan() || value_2.is_nan() {
|
|
1
|
|
} else if value_1 == value_2 {
|
|
0
|
|
} else if value_1 < value_2 {
|
|
-1
|
|
} else {
|
|
1
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?;
|
|
}
|
|
|
|
Instruction::CompareDoubleL() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
|
|
let comparison_result = if value_1.is_nan() || value_2.is_nan() {
|
|
-1
|
|
} else if value_1 == value_2 {
|
|
0
|
|
} else if value_1 < value_2 {
|
|
-1
|
|
} else {
|
|
1
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?;
|
|
}
|
|
|
|
Instruction::CompareFloatG() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
|
|
let comparison_result = if value_1.is_nan() || value_2.is_nan() {
|
|
1
|
|
} else if value_1 == value_2 {
|
|
0
|
|
} else if value_1 < value_2 {
|
|
-1
|
|
} else {
|
|
1
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?;
|
|
}
|
|
|
|
Instruction::CompareFloatL() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
|
|
let comparison_result = if value_1.is_nan() || value_2.is_nan() {
|
|
-1
|
|
} else if value_1 == value_2 {
|
|
0
|
|
} else if value_1 < value_2 {
|
|
-1
|
|
} else {
|
|
1
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?;
|
|
}
|
|
|
|
Instruction::CompareLong() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let comparison_result = if value_1 > value_2 {
|
|
1
|
|
} else if value_1 < value_2 {
|
|
-1
|
|
} else {
|
|
0
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(comparison_result)))?;
|
|
}
|
|
|
|
Instruction::ConvertFloatToDouble() => {
|
|
let float = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
let double = float as f64;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_double(double))?;
|
|
}
|
|
|
|
Instruction::ConvertFloatToInt() => {
|
|
let float = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
let int_value = match float {
|
|
f32::INFINITY => i32::MAX,
|
|
f32::NEG_INFINITY => i32::MIN,
|
|
v @ _ => if v.is_nan() { 0 } else { v as i32 } ,
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_value)))?;
|
|
}
|
|
|
|
Instruction::ConvertDoubleToInt() => {
|
|
let double = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
let int_value = match double {
|
|
f64::INFINITY => i32::MAX,
|
|
f64::NEG_INFINITY => i32::MIN,
|
|
v @ _ => if v.is_nan() { 0 } else { v as i32 } ,
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_value)))?;
|
|
}
|
|
|
|
Instruction::ConvertDoubleToLong() => {
|
|
let double = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
let long = match double {
|
|
f64::INFINITY => i64::MAX,
|
|
f64::NEG_INFINITY => i64::MIN,
|
|
v @ _ => if v.is_nan() { 0 } else { v as i64 }
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long))?;
|
|
}
|
|
|
|
Instruction::ConvertIntToByte() => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let byte_value = int_value & 0x000000FF;
|
|
|
|
frame.operand_stack.push(StackValue::Int(byte_value)).unwrap();
|
|
}
|
|
|
|
Instruction::ConvertIntToChar() => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let char_value = int_value & 0x0000FFFF;
|
|
|
|
frame.operand_stack.push(StackValue::Int(char_value)).unwrap();
|
|
}
|
|
|
|
Instruction::ConvertIntToDouble() => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let double_value = int_value as f64;
|
|
|
|
frame.operand_stack.push_double(double_value).unwrap();
|
|
}
|
|
|
|
Instruction::ConvertIntToFloat() => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let float_value = int_value as f32;
|
|
|
|
frame.operand_stack.push(StackValue::Float(float_value)).unwrap();
|
|
}
|
|
|
|
Instruction::ConvertIntToLong() => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let long_value = int_value as i64;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
|
|
Instruction::ConvertLongToInt() => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let int_value = long_value as i32;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_value)))?;
|
|
}
|
|
|
|
Instruction::ConvertLongToFloat() => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let float_value = long_value as f32;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(float_value)))?;
|
|
}
|
|
|
|
Instruction::DivideDouble() => {
|
|
// TODO: Obey all the rules
|
|
let quotient = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
let divident = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
|
|
frame.operand_stack.push_double(divident / quotient).unwrap();
|
|
}
|
|
|
|
Instruction::DivideFloat() => {
|
|
// TODO: Obey all the rules
|
|
let quotient = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
let divident = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
|
|
frame.operand_stack.push(StackValue::Float(divident / quotient)).unwrap();
|
|
}
|
|
|
|
Instruction::DivideInt() => {
|
|
// TODO: Obey all the rules
|
|
let quotient = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let divident = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
frame.operand_stack.push(StackValue::Int(divident / quotient)).unwrap();
|
|
}
|
|
|
|
Instruction::DivideLong() => {
|
|
// TODO: Obey all the rules
|
|
let quotient = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let divident = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let result = divident / quotient;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(result))?;
|
|
}
|
|
|
|
Instruction::Duplicate() => {
|
|
let popped_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
|
}
|
|
|
|
Instruction::DuplicateComputationalValue() => {
|
|
if frame.operand_stack.computational_type_at(0) == 1 {
|
|
let popped_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(popped_value))?;
|
|
} else {
|
|
let popped_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_2(0))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(popped_value))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(popped_value))?;
|
|
}
|
|
}
|
|
|
|
Instruction::DuplicateInsertDown() => {
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(value_1))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(value_2))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(value_1))?;
|
|
}
|
|
|
|
Instruction::EnterMonitor() => {
|
|
// TODO: Revisit this when doing multi-threading
|
|
let _monitored_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
}
|
|
Instruction::ExitMonitor() => {
|
|
// TODO: Revisit this when doing multi-threading
|
|
let _monitored_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
}
|
|
|
|
Instruction::GetField(fieldref_index) => {
|
|
let (class_name, field_name, field_descriptor) = class.gather_fieldref(fieldref_index).unwrap();
|
|
|
|
let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
// TODO: Are there any methods callable on arrays?
|
|
let this_object_class_index = self.heap_area.object_area.get_object_class_index(this_object);
|
|
let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap();
|
|
let this_object_descriptor = AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Classname(this_object_class_name.to_string())
|
|
};
|
|
let object_descriptor = AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Classname(class_name.to_string())
|
|
};
|
|
|
|
if ! self.class_store.are_types_compatible(&this_object_descriptor, &object_descriptor) {
|
|
return Err(Error::RunTimeError(format!(
|
|
"GetField: Cannot fetch '{field_name}' from class '{class_name}' on instance from class '{}: Types are incompatible'",
|
|
this_object_class_name
|
|
)))
|
|
}
|
|
|
|
let fetched_value = self.heap_area.object_area.get_object_field(this_object, field_name, frame.class_index, &self.class_store).unwrap();
|
|
|
|
// println!("{fetched_value:?} {field_descriptor}");
|
|
// TODO: Check field and value compatability
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?;
|
|
}
|
|
|
|
Instruction::GetStatic(fieldref_index) => {
|
|
let (class_name, field_name, field_descriptor) = class.gather_fieldref(fieldref_index).unwrap();
|
|
|
|
if ! self.class_store.have_class(class_name) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(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(class_name.to_string()));
|
|
}
|
|
|
|
let class_index = self.class_store.class_idx_from_name(class_name).unwrap();
|
|
let owning_class = CompatibleTypesIterator::new(class_index, &self.class_store)
|
|
.filter(|name| {
|
|
let index = self.class_store.class_idx_from_name(name).unwrap();
|
|
let class_file = self.class_store.class_file_from_idx(index).unwrap();
|
|
class_file.fields.iter()
|
|
.filter(|finfo| finfo.access_flags & FieldAccessFlag::Static)
|
|
.filter(|finfo| finfo.name == *field_name)
|
|
.next()
|
|
.is_some()
|
|
})
|
|
.next()
|
|
.unwrap_or_else(|| class_name);
|
|
|
|
if ! self.class_store.was_init(owning_class).unwrap() {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::InitClass(owning_class.to_string()));
|
|
}
|
|
|
|
// TODO: Throw error
|
|
let parsed_field_descriptor = AbstractTypeDescription::parse_full(field_descriptor).unwrap();
|
|
|
|
// TODO: Throw error
|
|
let fetched_value = self.heap_area.static_area.get(owning_class, field_name, parsed_field_descriptor).unwrap();
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(fetched_value))?;
|
|
}
|
|
|
|
Instruction::IncrementLocalInt(index, constant) => {
|
|
let int = wrap_stackframe_error(class, method, frame.load_local_int(index as u16))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Int(int + constant as i32)))?;
|
|
}
|
|
|
|
Instruction::InstanceOf(classref_index) => {
|
|
let class_name = class.gather_class(classref_index)?;
|
|
let object = wrap_stackframe_error(class, method, frame.operand_stack.peek_reference(0))?;
|
|
|
|
let instruction_result = if object == ObjectReference::NULL {
|
|
frame.operand_stack.pop_reference(0).unwrap();
|
|
0
|
|
} else {
|
|
let native_class_name = self.heap_area.object_area.get_reference_native_class_name(object, &self.class_store);
|
|
|
|
if class_name.starts_with("[") {
|
|
// TODO: Create array class on demand
|
|
} else {
|
|
if ! self.class_store.have_class(class_name) {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(class_name).unwrap() {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::InitClass(class_name.to_string()));
|
|
}
|
|
}
|
|
|
|
frame.operand_stack.pop_reference(0).unwrap();
|
|
|
|
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)))?;
|
|
}
|
|
|
|
Instruction::InvokeDynamic(invokedynamic_index, _zero) => {
|
|
let methodhandle_reference = class.pool_methodhandle_entry(invokedynamic_index)?;
|
|
}
|
|
|
|
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 mut arguments = VecDeque::new();
|
|
fill_arguments(class, method, &mut arguments, &parsed_expected_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: Filter abstract
|
|
// TODO: Check method info match
|
|
let (class_index, method_index, _method_info) = match ClassMethodIterator::new(object_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 implementation of method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_interface_name)));
|
|
}
|
|
};
|
|
|
|
|
|
let implementing_class_file = self.class_store.class_file_from_idx(class_index).unwrap();
|
|
let interface_frame = StackFrame::new(
|
|
implementing_class_file,
|
|
class_index,
|
|
method_index as u16,
|
|
arguments.make_contiguous()
|
|
);
|
|
|
|
return Ok(JVMCallbackOperation::PushFrame(interface_frame));
|
|
}
|
|
|
|
Instruction::InvokeSpecial(methodref_index) => {
|
|
// No instance-based dispatch
|
|
let (supplied_class_name, supplied_method_name, supplied_descriptor_string) = class.gather_methodref(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 target_class_index = self.class_store.class_idx_from_name(supplied_class_name).unwrap();
|
|
|
|
let parsed_expected_descriptor: MethodDescriptor = MethodDescriptor::try_from(supplied_descriptor_string)?;
|
|
let (class_index, method_index, method_info) = match ClassMethodIterator::new(target_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!("InvokeSpecial: Failed to find requested method '{}' with descriptor '{}' in the class '{}'", supplied_method_name, supplied_descriptor_string, supplied_class_name)));
|
|
}
|
|
};
|
|
|
|
let mut arguments = VecDeque::new();
|
|
fill_arguments(class, method, &mut arguments, &method_info.descriptor.argument_types, &mut frame.operand_stack)?;
|
|
|
|
// this
|
|
arguments.push_front(
|
|
StackValue::Reference(
|
|
wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?
|
|
)
|
|
);
|
|
|
|
|
|
let new_frame = StackFrame::new(
|
|
self.class_store.class_file_from_idx(class_index).unwrap(),
|
|
class_index,
|
|
method_index as u16,
|
|
&arguments.make_contiguous(),
|
|
);
|
|
|
|
return Ok(JVMCallbackOperation::PushFrame(new_frame));
|
|
},
|
|
|
|
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));
|
|
},
|
|
|
|
Instruction::InvokeVirtual(methodref_index) => {
|
|
let (base_class_name, base_method_name, base_descriptor_string) = class.gather_methodref(methodref_index)?;
|
|
|
|
if ! self.class_store.have_class(base_class_name) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(base_class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(base_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(base_class_name.to_string()));
|
|
}
|
|
|
|
let base_descriptor = MethodDescriptor::try_from(base_descriptor_string).unwrap();
|
|
|
|
|
|
// extract arguments, they are on top of the stack
|
|
let mut arguments = VecDeque::new();
|
|
fill_arguments(class, method, &mut arguments, &base_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));
|
|
|
|
// Are there any methods callable on arrays?, turns out there are
|
|
let this_object_class_index = self.heap_area.object_area.get_reference_class_index(this_object, &self.class_store);
|
|
let this_object_class_name = self.class_store.class_name_from_index(this_object_class_index).unwrap().to_string();
|
|
let this_object_descriptor = AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Classname(this_object_class_name.clone())
|
|
};
|
|
let base_object_descriptor = AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Classname(base_class_name.to_string())
|
|
};
|
|
|
|
|
|
// TODO: Throw error
|
|
if ! self.class_store.are_types_compatible(&this_object_descriptor, &base_object_descriptor) {
|
|
return Err(Error::RunTimeError(format!("InvokeVirtual: Cannot call '{base_method_name}' from class '{base_class_name}' on instance from class '{this_object_class_name}: Types are incompatible'")))
|
|
}
|
|
|
|
let (invoked_class_index, invoked_method_index, _invoked_method_info) = match ClassMethodIterator::new(this_object_class_index, &self.class_store)
|
|
.filter(|(_, _, method_info)| method_info.name == *base_method_name)
|
|
.filter(|(_, _, method_info)| method_info.descriptor == base_descriptor)
|
|
.next()
|
|
{
|
|
Some(t) => t,
|
|
None => {
|
|
return Err(Error::RunTimeError(format!(
|
|
"InvokeVirtual: Failed to find requested method '{}' with descriptor '{}' in the type hierarchy of '{}'",
|
|
base_method_name,
|
|
base_descriptor_string,
|
|
this_object_class_name,
|
|
)));
|
|
},
|
|
};
|
|
|
|
let invoked_class_file = self.class_store.class_file_from_idx(invoked_class_index).unwrap();
|
|
let new_frame = StackFrame::new(
|
|
invoked_class_file,
|
|
invoked_class_index,
|
|
invoked_method_index as u16,
|
|
arguments.make_contiguous()
|
|
);
|
|
|
|
return Ok(JVMCallbackOperation::PushFrame(new_frame));
|
|
},
|
|
|
|
Instruction::LoadByteImmediate(byte) => {
|
|
// sign extend into int
|
|
|
|
let frame_result = frame.operand_stack.push(StackValue::Int(byte as i32));
|
|
match frame_result {
|
|
Ok(_) => (),
|
|
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
|
}
|
|
}
|
|
|
|
Instruction::LoadShortImmediate(short) => {
|
|
// sign extend into int
|
|
|
|
let frame_result = frame.operand_stack.push(StackValue::Int(short as i32));
|
|
match frame_result {
|
|
Ok(_) => (),
|
|
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
|
}
|
|
}
|
|
|
|
Instruction::LoadFromBArray() => {
|
|
let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
let element = self.heap_area.object_area.get_array_element(array, index);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_field_value(element))?;
|
|
}
|
|
|
|
Instruction::LoadConstant(index) => {
|
|
// TODO: Handle error instead of unwrap
|
|
match class.pool_entry(index as u16).unwrap() {
|
|
ConstantPoolInfo::Class(_) => {
|
|
let class_name = class.gather_class(index as u16).unwrap();
|
|
|
|
if ! self.class_store.have_class(class_name) {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(class_name).unwrap() {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::InitClass(class_name.to_string()));
|
|
}
|
|
|
|
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);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(class_object_ref)))?;
|
|
}
|
|
|
|
ConstantPoolInfo::String(_) => {
|
|
// TODO: Handle error instead of unwrap
|
|
let string_constant = class.gather_string(index as u16).unwrap();
|
|
|
|
let string_obj_ref = self.heap_area.make_handmade_string(string_constant, &self.class_store);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?;
|
|
}
|
|
ConstantPoolInfo::Float(_) => {
|
|
// TODO: Handle error instead of unwrap
|
|
let float_constant = class.gather_float(index as u16).unwrap();
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(float_constant)))?;
|
|
}
|
|
ConstantPoolInfo::Integer(int_data) => {
|
|
// TODO: Handle error instead of unwrap
|
|
let int_constant = int_data.value;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_constant)))?;
|
|
}
|
|
_ => {
|
|
println!("{:?}", class.pool_entry(index as u16).unwrap());
|
|
todo!()
|
|
}
|
|
}
|
|
},
|
|
|
|
Instruction::LoadCostantWide(wide_index) => {
|
|
// TODO: Handle error instead of unwrap
|
|
match class.pool_entry(wide_index).unwrap() {
|
|
ConstantPoolInfo::Class(_) => {
|
|
let class_name = class.gather_class(wide_index).unwrap();
|
|
|
|
let class_object_ref = if class_name.starts_with('[') {
|
|
let component_name = class_name.trim_start_matches('[');
|
|
let array_level = class_name.len() - component_name.len();
|
|
let array_type_desc = AbstractTypeDescription {
|
|
array_level: array_level as u8,
|
|
kind: component_name.into(),
|
|
};
|
|
|
|
if let Some(array_ref) = self.class_store.get_array_class_ref(&array_type_desc) {
|
|
array_ref
|
|
} else {
|
|
let mut test_type = array_type_desc.super_component();
|
|
|
|
if component_name.len() != 1 {
|
|
let base_type_name = test_type.extract_class_name();
|
|
if ! self.class_store.have_class(base_type_name) {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(base_type_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(base_type_name).unwrap() {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::InitClass(base_type_name.to_string()));
|
|
}
|
|
}
|
|
|
|
let mut test_object_ref = self.class_store.class_ref_for_type(test_type.clone()).unwrap();
|
|
|
|
while let Some(new_test_object_ref) = self.class_store.class_ref_for_type(test_type.array()) {
|
|
test_type = test_type.array();
|
|
test_object_ref = new_test_object_ref;
|
|
}
|
|
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::MakeArrayClass(test_object_ref, test_type))
|
|
}
|
|
} else {
|
|
if ! self.class_store.have_class(class_name) {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(class_name).unwrap() {
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::InitClass(class_name.to_string()));
|
|
}
|
|
|
|
let class_index = self.class_store.class_idx_from_name(class_name).unwrap();
|
|
self.class_store.get_class_objectref_from_index(class_index)
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(class_object_ref)))?;
|
|
}
|
|
|
|
ConstantPoolInfo::String(_) => {
|
|
// TODO: Handle error instead of unwrap
|
|
let string_constant = class.gather_string(wide_index).unwrap();
|
|
|
|
let string_obj_ref = self.heap_area.make_handmade_string(string_constant, &self.class_store);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(string_obj_ref)))?;
|
|
}
|
|
|
|
ConstantPoolInfo::Integer(int_data) => {
|
|
let int_value = int_data.value;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(int_value)))?;
|
|
}
|
|
ConstantPoolInfo::Float(float_data) => {
|
|
let float_value = float_data.value;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(float_value)))?;
|
|
}
|
|
_ => {
|
|
println!("{:?}", class.pool_entry(wide_index).unwrap());
|
|
todo!()
|
|
}
|
|
}
|
|
}
|
|
|
|
Instruction::LoadConstant64(wide_index) => {
|
|
match class.pool_entry(wide_index).unwrap() {
|
|
ConstantPoolInfo::Long(long_data) => {
|
|
let long_value = long_data.value;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
ConstantPoolInfo::Double(double_data) => {
|
|
let double = double_data.value;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_double(double))?;
|
|
}
|
|
_ => {
|
|
println!("{:?}", class.pool_entry(wide_index).unwrap());
|
|
todo!()
|
|
}
|
|
}
|
|
}
|
|
|
|
Instruction::LoadLocalDouble(index) => {
|
|
load_local_double(class, method, frame, index as usize)?;
|
|
}
|
|
|
|
Instruction::LoadLocalDouble0() => {
|
|
load_local_double(class, method, frame, 0)?;
|
|
}
|
|
|
|
Instruction::LoadLocalFloat(index) => {
|
|
load_local_float(class, method, frame, index as usize)?;
|
|
}
|
|
Instruction::LoadLocalFloat0() => {
|
|
load_local_float(class, method, frame, 0)?;
|
|
}
|
|
Instruction::LoadLocalFloat1() => {
|
|
load_local_float(class, method, frame, 1)?;
|
|
}
|
|
Instruction::LoadLocalFloat2() => {
|
|
load_local_float(class, method, frame, 2)?;
|
|
}
|
|
Instruction::LoadLocalFloat3() => {
|
|
load_local_float(class, method, frame, 3)?;
|
|
}
|
|
|
|
Instruction::LoadLocalInt(index) => {
|
|
load_local_int(class, method, frame, index as usize)?;
|
|
}
|
|
Instruction::LoadLocalInt0() => {
|
|
load_local_int(class, method, frame, 0)?;
|
|
}
|
|
Instruction::LoadLocalInt1() => {
|
|
load_local_int(class, method, frame, 1)?;
|
|
}
|
|
Instruction::LoadLocalInt2() => {
|
|
load_local_int(class, method, frame, 2)?;
|
|
}
|
|
Instruction::LoadLocalInt3() => {
|
|
load_local_int(class, method, frame, 3)?;
|
|
}
|
|
|
|
Instruction::LoadLocalLong(index) => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.load_local_long(index as u16))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
Instruction::LoadLocalLong0() => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.load_local_long(0))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
Instruction::LoadLocalLong1() => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.load_local_long(1))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
Instruction::LoadLocalLong2() => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.load_local_long(2))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
Instruction::LoadLocalLong3() => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.load_local_long(3))?;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(long_value))?;
|
|
}
|
|
|
|
Instruction::LoadLocalReference(index) => {
|
|
load_local_reference(class, method, frame, index as usize)?;
|
|
}
|
|
Instruction::LoadLocalReference0() => {
|
|
load_local_reference(class, method, frame, 0)?;
|
|
}
|
|
Instruction::LoadLocalReference1() => {
|
|
load_local_reference(class, method, frame, 1)?;
|
|
}
|
|
Instruction::LoadLocalReference2() => {
|
|
load_local_reference(class, method, frame, 2)?;
|
|
}
|
|
Instruction::LoadLocalReference3() => {
|
|
load_local_reference(class, method, frame, 3)?;
|
|
}
|
|
|
|
Instruction::LogicalShiftIntRight() => {
|
|
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 & 0b00011111;
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let uint = u32::from_ne_bytes(int.to_ne_bytes());
|
|
let u_result = uint >> shift;
|
|
let i_result = i32::from_ne_bytes(u_result.to_ne_bytes());
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(i_result)))?;
|
|
}
|
|
|
|
Instruction::LogicalShiftLongRight() => {
|
|
let shift = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))? as u8 & 0b00111111;
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let uint = u64::from_ne_bytes(int.to_ne_bytes());
|
|
let u_result = uint >> shift;
|
|
let i_result = i64::from_ne_bytes(u_result.to_ne_bytes());
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(i_result))?;
|
|
}
|
|
|
|
Instruction::LookupSwitch(default_offset, pairs) => {
|
|
let key = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let jump_offset = match pairs.binary_search_by(|(match_key, _offset)| match_key.cmp(&key)) {
|
|
Ok(offset) => pairs[offset].1,
|
|
Err(_) => default_offset,
|
|
};
|
|
|
|
frame.instruction_pointer -= offset 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::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))?;
|
|
|
|
let result = factor_1 * factor_2;
|
|
let java_result = result;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(java_result)))?;
|
|
}
|
|
|
|
Instruction::MultiplyInt() => {
|
|
let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
let (result, overflowed) = i32::overflowing_mul(factor_1, factor_2);
|
|
let java_result = match (overflowed, factor_1, factor_2) {
|
|
(true, 0..=i32::MAX, 0..=i32::MAX) => if result > 0 { -result } else { result },
|
|
(true, 0..=i32::MAX, i32::MIN..0) | (true, i32::MIN..0, 0..=i32::MAX) => if result < 0 { -result } else { result },
|
|
(true, i32::MIN..0, i32::MIN..0) => if result > 0 { - result } else { result },
|
|
(false, _, _) => result,
|
|
};
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(java_result)))?;
|
|
}
|
|
|
|
Instruction::MultiplyLong() => {
|
|
let factor_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let factor_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
let (result, overflowed) = i64::overflowing_mul(factor_1, factor_2);
|
|
let java_result = if overflowed {
|
|
if result < 0 && ((factor_1 >= 0 && factor_2 <= 0 ) || (factor_1 < 0 && factor_2 >= 0)) {
|
|
result.abs()
|
|
} else if factor_1 < 0 && factor_2 < 0 {
|
|
if result < 0 { result } else { -result }
|
|
} else {
|
|
-result.abs()
|
|
}
|
|
} else {
|
|
result
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(java_result))?;
|
|
}
|
|
|
|
Instruction::ModuloInt() => {
|
|
let int_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let int_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
let modulo = int_2.wrapping_rem(int_1);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(modulo)))?;
|
|
}
|
|
|
|
Instruction::NegateInt() => {
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let negated = -int;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(negated)))?;
|
|
}
|
|
|
|
Instruction::NegateLong() => {
|
|
let long = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let negated = -long;
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(negated))?;
|
|
}
|
|
|
|
Instruction::NewArray(component_class_index) => {
|
|
// construct single level array
|
|
let component_class_name = class.gather_class(component_class_index)?;
|
|
|
|
if ! self.class_store.have_class(component_class_name) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
return Ok(JVMCallbackOperation::LoadClass(component_class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(component_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(component_class_name.to_string()));
|
|
}
|
|
|
|
let component_class_descriptor = AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: match component_class_name {
|
|
_ => AbstractTypeKind::Classname(component_class_name.to_string())
|
|
}
|
|
};
|
|
|
|
let array_descriptor = AbstractTypeDescription {
|
|
array_level: 1,
|
|
kind: component_class_descriptor.kind.clone(),
|
|
};
|
|
|
|
if let None = self.class_store.get_array_class_ref(&array_descriptor) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
|
|
let component_class_index = self.class_store.class_idx_from_name(component_class_name).unwrap();
|
|
let component_class_class_ref = self.class_store.get_class_objectref_from_index(component_class_index);
|
|
return Ok(JVMCallbackOperation::MakeArrayClass(component_class_class_ref, component_class_descriptor));
|
|
}
|
|
|
|
let array_capacity = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array_ref = self.heap_area.make_empty_array(&self.class_store, component_class_descriptor, array_capacity as usize);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(array_ref)))?;
|
|
}
|
|
|
|
Instruction::NewPrimitiveArray(array_type) => {
|
|
let array_capacity = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
const CHAR: u8 = 5;
|
|
const BYTE: u8 = 8;
|
|
const INT: u8 = 10;
|
|
let array_ref = match array_type {
|
|
BYTE => {
|
|
let array_ref = self.heap_area.make_primitive_byte_array(array_capacity as usize, &self.class_store);
|
|
|
|
array_ref
|
|
}
|
|
|
|
CHAR => {
|
|
let array_ref = self.heap_area.make_primitive_char_array(array_capacity as usize, &self.class_store);
|
|
|
|
array_ref
|
|
}
|
|
|
|
INT => {
|
|
let array_ref = self.heap_area.make_primitive_int_array(array_capacity as usize, &self.class_store);
|
|
|
|
array_ref
|
|
}
|
|
|
|
_ => todo!()
|
|
};
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(array_ref)))?;
|
|
}
|
|
|
|
Instruction::NewObject(class_index) => {
|
|
let class_name = class.gather_class(class_index)?;
|
|
|
|
if ! self.class_store.have_class(class_name) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
|
|
return Ok(JVMCallbackOperation::LoadClass(class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(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(class_name.to_string()));
|
|
}
|
|
|
|
let class_index = self.class_store.class_idx_from_name(class_name).unwrap();
|
|
let new_object = self.heap_area.make_object(&self.class_store, class_index);
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(new_object)))?;
|
|
}
|
|
|
|
Instruction::NoOperation() => {
|
|
|
|
}
|
|
|
|
Instruction::OrInt() => {
|
|
let value_0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(value_0 | value_1)))?;
|
|
}
|
|
|
|
Instruction::Pop() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))?;
|
|
}
|
|
|
|
Instruction::PushConstDouble1() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_double(1.0))?;
|
|
}
|
|
Instruction::PushConstFloat0() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(0.0)))?;
|
|
}
|
|
Instruction::PushConstFloat1() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(1.0)))?;
|
|
}
|
|
Instruction::PushConstFloat2() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(2.0)))?;
|
|
}
|
|
|
|
Instruction::PushConstIntM1() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(-1)))?;
|
|
}
|
|
Instruction::PushConstInt0() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(0)))?;
|
|
}
|
|
Instruction::PushConstInt1() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(1)))?;
|
|
}
|
|
Instruction::PushConstInt2() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(2)))?;
|
|
}
|
|
Instruction::PushConstInt3() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(3)))?;
|
|
}
|
|
Instruction::PushConstInt4() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(4)))?;
|
|
}
|
|
Instruction::PushConstInt5() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Int(5)))?;
|
|
}
|
|
|
|
Instruction::PushConstLong0() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(0))?;
|
|
}
|
|
Instruction::PushConstLong1() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(1))?;
|
|
}
|
|
|
|
Instruction::PushNull() => {
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(ObjectReference::NULL)))?;
|
|
}
|
|
|
|
Instruction::PutField(fieldref_index) => {
|
|
let (_target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
|
|
|
let value = match expected_field_descriptor.as_str() {
|
|
"J" | "D" => wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_2(0))?,
|
|
_ => match wrap_stackframe_error(class, method, frame.operand_stack.pop_computational_1(0))? {
|
|
StackValue::Int(i) => {
|
|
match expected_field_descriptor.as_str() {
|
|
"Z" => FieldValue::Boolean(i != 0),
|
|
"B" => FieldValue::Byte(i as i8),
|
|
"C" => FieldValue::Char(i as u16),
|
|
_ => FieldValue::Int(i)
|
|
}
|
|
}
|
|
StackValue::Reference(r) => FieldValue::Reference(r),
|
|
StackValue::Float(f) => FieldValue::Float(f),
|
|
StackValue::Byte(b) => FieldValue::Byte(b),
|
|
stack_value @ _ => {
|
|
println!("{stack_value:?}");
|
|
todo!()
|
|
}
|
|
}
|
|
};
|
|
|
|
let this_object = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
self.heap_area.object_area.set_object_field(this_object, target_field_name, value, frame.class_index, &self.class_store)?;
|
|
}
|
|
|
|
Instruction::PutStatic(fieldref_index) => {
|
|
let (target_class_name, target_field_name, expected_field_descriptor) = class.gather_fieldref(fieldref_index)?;
|
|
|
|
if ! self.class_store.have_class(target_class_name) {
|
|
// rewind the bytecode offset, I'll need to execute this instruction again
|
|
frame.instruction_pointer -= offset as u32;
|
|
|
|
return Ok(JVMCallbackOperation::LoadClass(target_class_name.to_string()));
|
|
}
|
|
if ! self.class_store.was_init(target_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(target_class_name.to_string()));
|
|
}
|
|
|
|
let matched_field = match ClassFieldIterator::new(self.class_store.class_idx_from_name(target_class_name).unwrap(), &self.class_store)
|
|
.filter(|f| f.name == *target_field_name)
|
|
.filter(|f| f.access_flags & FieldAccessFlag::Static)
|
|
.next() {
|
|
Some(f) => f,
|
|
None => return Err(Error::RunTimeError(format!("PutStatic: Trying to set field '{}' on class '{}' but there is no such static field", target_field_name, target_class_name)))
|
|
};
|
|
|
|
|
|
let (consumed_chars, expected_descriptor) = AbstractTypeDescription::parse_first(expected_field_descriptor)?;
|
|
assert!(expected_field_descriptor.len() == consumed_chars);
|
|
|
|
// TODO: Throw exception on fail
|
|
if matched_field.descriptor != expected_descriptor {
|
|
return Err(Error::RunTimeError(format!("PutStatic: Field descriptor mismatch: '{expected_descriptor:?}' but found '{:?}'", matched_field.descriptor)))
|
|
}
|
|
|
|
let set_value = match (matched_field.descriptor.array_level, &matched_field.descriptor.kind) {
|
|
|
|
(0, AbstractTypeKind::Boolean()) => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
FieldValue::Boolean((int_value & 1) != 0)
|
|
}
|
|
|
|
(0, AbstractTypeKind::Int()) => {
|
|
let int_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
FieldValue::Int(int_value)
|
|
}
|
|
|
|
(0, AbstractTypeKind::Long()) => {
|
|
let long_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
FieldValue::Long(long_value)
|
|
}
|
|
|
|
(0..=255, AbstractTypeKind::Classname(_field_type_name)) => {
|
|
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
if ref_value != ObjectReference::NULL {
|
|
let value_native_name = self.heap_area.object_area.get_reference_native_class_name(ref_value, &self.class_store);
|
|
let parsed_native_name = AbstractTypeDescription::parse_first(value_native_name).unwrap().1;
|
|
|
|
if ! self.class_store.are_types_compatible(&parsed_native_name, &matched_field.descriptor) {
|
|
return Err(Error::RunTimeError(format!("PutStatic: Trying to set a value with type '{parsed_native_name:?}' on a field with type '{:?}'", matched_field.descriptor)));
|
|
}
|
|
}
|
|
|
|
FieldValue::Reference(ref_value)
|
|
}
|
|
|
|
(1..=255, _) => {
|
|
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
FieldValue::Reference(ref_value)
|
|
}
|
|
|
|
_ => {
|
|
println!("{:?}", matched_field);
|
|
todo!()
|
|
}
|
|
};
|
|
|
|
self.heap_area.static_area.set(target_class_name, target_field_name, set_value)?;
|
|
}
|
|
|
|
Instruction::ReturnDouble() => {
|
|
match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) {
|
|
(0, AbstractTypeKind::Double()) => (),
|
|
_ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
|
}
|
|
|
|
let double = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
|
|
return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Double(double)));
|
|
}
|
|
|
|
Instruction::ReturnFloat() => {
|
|
match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) {
|
|
(0, AbstractTypeKind::Float()) => (),
|
|
_ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
|
}
|
|
|
|
let float = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
|
|
return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Float(float)));
|
|
}
|
|
|
|
Instruction::ReturnInt() => {
|
|
match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) {
|
|
(_, AbstractTypeKind::Byte() | AbstractTypeKind::Boolean()| AbstractTypeKind::Int() | AbstractTypeKind::Char() | AbstractTypeKind::Short()) => (),
|
|
_ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
|
}
|
|
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int_compatible(0))?;
|
|
|
|
return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Int(int)));
|
|
}
|
|
Instruction::ReturnLong() => {
|
|
match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) {
|
|
(0, AbstractTypeKind::Long()) => (),
|
|
_ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
|
}
|
|
|
|
let long = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Long(long)));
|
|
}
|
|
Instruction::ReturnReference() => {
|
|
match (method.descriptor.return_type.array_level, &method.descriptor.return_type.kind) {
|
|
(1..=u8::MAX, _) => (),
|
|
(_, AbstractTypeKind::Classname(_)) => (),
|
|
_ => return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
|
}
|
|
|
|
let ref_value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
return Ok(JVMCallbackOperation::ReturnFrame(FieldValue::Reference(ref_value)));
|
|
}
|
|
Instruction::ReturnVoid() => {
|
|
let expected_type = AbstractTypeDescription {
|
|
array_level: 0,
|
|
kind: AbstractTypeKind::Void(),
|
|
};
|
|
|
|
if method.descriptor.return_type != expected_type {
|
|
return Err(Error::OpcodeError(format!("Found opcode '{:?}' on method returning '{:?}'", instruction, method.descriptor.return_type)))
|
|
}
|
|
|
|
return Ok(JVMCallbackOperation::PopFrame());
|
|
},
|
|
|
|
Instruction::StoreIntoBArray() => {
|
|
let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
match self.heap_area.object_area.get_entry(array_ref) {
|
|
CompartmentEntry::ByteArray(_) => {
|
|
let byte_value = value as i8;
|
|
|
|
self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Byte(byte_value));
|
|
}
|
|
_ => todo!(), // TODO: Handle as error, Boolean arrays also
|
|
}
|
|
}
|
|
|
|
Instruction::StoreIntoCArray() => {
|
|
let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
match self.heap_area.object_area.get_entry(array_ref) {
|
|
CompartmentEntry::CharArray(_) => {
|
|
let int_bytes = value.to_ne_bytes();
|
|
let char_value = u16::from_ne_bytes([int_bytes[2], int_bytes[3]]);
|
|
|
|
self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Char(char_value));
|
|
}
|
|
_ => todo!(), // TODO: Handle as error
|
|
}
|
|
}
|
|
|
|
Instruction::StoreIntoIArray() => {
|
|
let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
// TODO: Type checking
|
|
|
|
self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Int(value));
|
|
}
|
|
|
|
Instruction::StoreIntoRArray() => {
|
|
let value = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
let index = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let array_ref = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
// TODO: Type checking
|
|
|
|
self.heap_area.object_area.set_array_element(array_ref, index as usize, FieldValue::Reference(value));
|
|
}
|
|
|
|
Instruction::SubtractInt() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
let value_1 = 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::SubtractLong() => {
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(value_1 - value_2))?;
|
|
}
|
|
|
|
Instruction::StoreLocalDouble(index) => {
|
|
let double = wrap_stackframe_error(class, method, frame.operand_stack.pop_double(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local_double(index as u16, double))?;
|
|
}
|
|
|
|
Instruction::StoreLocalFloat(index) => {
|
|
let float = wrap_stackframe_error(class, method, frame.operand_stack.pop_float(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Float(float)))?;
|
|
}
|
|
|
|
Instruction::StoreLocalInt(index) => {
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Int(int)))?;
|
|
}
|
|
Instruction::StoreLocalInt0() => {
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Int(int)))?;
|
|
}
|
|
Instruction::StoreLocalInt1() => {
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Int(int)))?;
|
|
}
|
|
Instruction::StoreLocalInt2() => {
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Int(int)))?;
|
|
}
|
|
Instruction::StoreLocalInt3() => {
|
|
let int = wrap_stackframe_error(class, method, frame.operand_stack.pop_int(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Int(int)))?;
|
|
}
|
|
|
|
Instruction::StoreLocalLong(index) => {
|
|
let _ = wrap_stackframe_error(class, method, frame.operand_stack.pop_long1(0))?;
|
|
let long0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long0(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Long0(long0)))?;
|
|
wrap_stackframe_error(class, method, frame.store_local(index as u16 + 1, StackValue::Long1()))?;
|
|
}
|
|
Instruction::StoreLocalLong0() => {
|
|
let _ = wrap_stackframe_error(class, method, frame.operand_stack.pop_long1(0))?;
|
|
let long0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long0(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Long0(long0)))?;
|
|
wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Long1()))?;
|
|
}
|
|
Instruction::StoreLocalLong1() => {
|
|
let _ = wrap_stackframe_error(class, method, frame.operand_stack.pop_long1(0))?;
|
|
let long0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long0(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Long0(long0)))?;
|
|
wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Long1()))?;
|
|
}
|
|
Instruction::StoreLocalLong2() => {
|
|
let _ = wrap_stackframe_error(class, method, frame.operand_stack.pop_long1(0))?;
|
|
let long0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long0(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Long0(long0)))?;
|
|
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Long1()))?;
|
|
}
|
|
Instruction::StoreLocalLong3() => {
|
|
let _ = wrap_stackframe_error(class, method, frame.operand_stack.pop_long1(0))?;
|
|
let long0 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long0(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(3, StackValue::Long0(long0)))?;
|
|
wrap_stackframe_error(class, method, frame.store_local(4, StackValue::Long1()))?;
|
|
}
|
|
|
|
Instruction::StoreLocalReference(index) => {
|
|
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(index as u16, StackValue::Reference(reference)))?;
|
|
}
|
|
Instruction::StoreLocalReference0() => {
|
|
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(0, StackValue::Reference(reference)))?;
|
|
}
|
|
Instruction::StoreLocalReference1() => {
|
|
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(1, StackValue::Reference(reference)))?;
|
|
}
|
|
Instruction::StoreLocalReference2() => {
|
|
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.store_local(2, StackValue::Reference(reference)))?;
|
|
}
|
|
Instruction::StoreLocalReference3() => {
|
|
let reference = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
|
|
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::ThrowException() => {
|
|
let exception = wrap_stackframe_error(class, method, frame.operand_stack.pop_reference(0))?;
|
|
if exception == ObjectReference::NULL {
|
|
// TODO: Throw NullPointerException
|
|
} else {
|
|
// TODO: Check throwable instance
|
|
return Ok(JVMCallbackOperation::ThrowException(exception));
|
|
}
|
|
}
|
|
|
|
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)))?;
|
|
}
|
|
|
|
Instruction::XorLong() => {
|
|
let value_1 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
let value_2 = wrap_stackframe_error(class, method, frame.operand_stack.pop_long(0))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_long(value_1 ^ value_2))?;
|
|
}
|
|
|
|
_ => {
|
|
return Err(Error::RunTimeError(format!("Opcode not implemented yet: {:?}", instruction)))
|
|
},
|
|
|
|
}
|
|
}
|
|
|
|
// TODO: Review this, maybe crash when there is no return?
|
|
Ok(JVMCallbackOperation::PopFrame())
|
|
}
|
|
|
|
pub fn active_thread_mut(&mut self) -> &mut VirtualThread {
|
|
return &mut self.threads[self.active_thread];
|
|
}
|
|
|
|
}
|
|
|
|
pub enum JVMCallbackOperation {
|
|
PopFrame(),
|
|
ReturnFrame(FieldValue),
|
|
PushFrame(StackFrame),
|
|
LoadClass(String),
|
|
InitClass(String),
|
|
MakeArrayClass(ObjectReference, AbstractTypeDescription),
|
|
ThrowException(ObjectReference),
|
|
}
|
|
|
|
fn load_local_reference(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> {
|
|
let loaded_value = wrap_stackframe_error(class, method, frame.load_local_reference(index as u16))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Reference(loaded_value)))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn load_local_double(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> {
|
|
let loaded_value = wrap_stackframe_error(class, method, frame.load_local_double(index as u16))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push_double(loaded_value))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn load_local_float(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> {
|
|
let loaded_value = wrap_stackframe_error(class, method, frame.load_local_float(index as u16))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(StackValue::Float(loaded_value)))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn load_local_int(class: &JavaClassFile, method: &MethodInfo, frame: &mut StackFrame, index: usize) -> Result<(), Error> {
|
|
let int_compatible_stackvalue = wrap_stackframe_error(class, method, frame.load_local_int_compatible(index as u16))?;
|
|
|
|
wrap_stackframe_error(class, method, frame.operand_stack.push(int_compatible_stackvalue))?;
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn fill_arguments(class: &JavaClassFile, method: &MethodInfo, arguments: &mut VecDeque<StackValue>, argument_types: &Box<[AbstractTypeDescription]>, stack: &mut OperandStack) -> Result<(), Error> {
|
|
for argument_type_index in 0..argument_types.len() {
|
|
let argument_type = &argument_types[argument_types.len() - argument_type_index - 1];
|
|
if argument_type.array_level != 0 {
|
|
// TODO: Type checking
|
|
arguments.push_front(
|
|
StackValue::Reference(wrap_stackframe_error(class, method, stack.pop_reference(0))?),
|
|
)
|
|
} else {
|
|
match argument_type.kind {
|
|
AbstractTypeKind::Void() => return Err(Error::RunTimeError("Functions cannot take arguments of type void".to_string())),
|
|
// TODO: Add better description
|
|
AbstractTypeKind::Byte() => {
|
|
arguments.push_front(
|
|
StackValue::Byte(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_byte(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
AbstractTypeKind::Char() => {
|
|
arguments.push_front(
|
|
StackValue::Int(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_int_compatible(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
AbstractTypeKind::Double() => {
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_double1(0)
|
|
)?;
|
|
arguments.push_front(StackValue::Double1());
|
|
arguments.push_front(
|
|
StackValue::Double0(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_double0(0)
|
|
)?
|
|
)
|
|
);
|
|
},
|
|
AbstractTypeKind::Float() => {
|
|
arguments.push_front(
|
|
StackValue::Float(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_float(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
AbstractTypeKind::Int() => {
|
|
arguments.push_front(
|
|
StackValue::Int(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_int(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
AbstractTypeKind::Long() => {
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_long1(0)
|
|
)?;
|
|
arguments.push_front(
|
|
StackValue::Long1(
|
|
)
|
|
);
|
|
arguments.push_front(
|
|
StackValue::Long0(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_long0(0)
|
|
)?
|
|
)
|
|
);
|
|
},
|
|
AbstractTypeKind::Classname(ref name) => {
|
|
// TODO: Type checking
|
|
arguments.push_front(
|
|
StackValue::Reference(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_reference(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
AbstractTypeKind::Short() => {
|
|
arguments.push_front(
|
|
StackValue::Short(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_short(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
AbstractTypeKind::Boolean() => {
|
|
arguments.push_front(
|
|
StackValue::Int(
|
|
wrap_stackframe_error(
|
|
class,
|
|
method,
|
|
stack.pop_boolean(0)
|
|
)?
|
|
)
|
|
)
|
|
},
|
|
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
pub fn wrap_stackframe_error<T>(class: &JavaClassFile, method: &MethodInfo, frame_result: Result<T, stackframe::Error>) -> Result<T, Error> {
|
|
match frame_result {
|
|
Ok(t) => Ok(t),
|
|
Err(err) => return Err(Error::StackFrameError(err, format!("in '{}', in class '{}'", method.name, class.get_classname().unwrap()))),
|
|
}
|
|
}
|