721 lines
No EOL
34 KiB
Rust
721 lines
No EOL
34 KiB
Rust
use std::cell::RefCell;
|
|
use std::io::Write;
|
|
use std::rc::Rc;
|
|
|
|
use anyhow::Error;
|
|
use log::{debug, error};
|
|
|
|
use crate::class::{Class, Object, ObjectRef, Value};
|
|
use crate::class::Value::{F32, F64, I32, I64, Null, Ref, Utf8, Void};
|
|
use crate::classloader::classdef::{AttributeType, CpEntry, Modifier};
|
|
use crate::classloader::io::{read_u16, read_u8};
|
|
use crate::classmanager;
|
|
use crate::classmanager::get_class_by_id;
|
|
use crate::vm::array::{array_load, array_store};
|
|
use crate::vm::native::invoke_native;
|
|
use crate::vm::opcodes;
|
|
use crate::vm::opcodes::*;
|
|
use crate::vm::operations::{get_signature_for_invoke, get_static};
|
|
use crate::vm::stack::StackFrame;
|
|
|
|
pub struct Vm {}
|
|
|
|
|
|
#[cfg(target_family = "unix")]
|
|
const PATH_SEPARATOR: char = ':';
|
|
|
|
#[cfg(target_family = "windows")]
|
|
const PATH_SEPARATOR: char = ';';
|
|
|
|
/// The singlethreaded VM (maybe a future Thread)
|
|
//TODO goto
|
|
//TODO error handling
|
|
impl Vm {
|
|
/// for running static initializers
|
|
pub fn new_internal() -> Self {
|
|
Self {}
|
|
}
|
|
|
|
pub fn new(stack: &mut Vec<StackFrame>) -> Self {
|
|
env_logger::builder()
|
|
.format(|buf, record| {
|
|
writeln!(buf, "{}: {}", record.level(), record.args())
|
|
})
|
|
.try_init().unwrap();
|
|
let mut vm_instance = Self {};
|
|
classmanager::init();
|
|
Vm::init(&mut vm_instance, stack);
|
|
vm_instance
|
|
}
|
|
|
|
fn init(vm: &mut Vm, stack: &mut Vec<StackFrame>) {
|
|
classmanager::load_class_by_name("java/lang/Class");
|
|
vm.execute_static(stack, "java/lang/System", "initPhase1()V", vec![]).expect("cannot create VM");
|
|
}
|
|
|
|
pub fn new_instance(class: &Class) -> Object {
|
|
Object::new(class)
|
|
}
|
|
|
|
/// execute the bytecode
|
|
pub fn execute_virtual(
|
|
&mut self,
|
|
stack: &mut Vec<StackFrame>,
|
|
class_name: &str,
|
|
method_name: &str,
|
|
args: Vec<Value>,
|
|
) -> Result<Value, Error> {
|
|
for arg in &args {
|
|
if let Ref(r) = arg {
|
|
debug!("arg {:?}",r);
|
|
} else {
|
|
debug!("arg {:?}",arg);
|
|
}
|
|
}
|
|
|
|
if let Null = args[0] {
|
|
panic!("NPE");
|
|
}
|
|
if let Ref(this) = &args[0] {
|
|
if let ObjectRef::Object(this) = this {
|
|
let thisb= this.borrow();
|
|
let cd = classmanager::get_classdef(&thisb.class_id);
|
|
let method = cd.get_method(method_name);
|
|
if let Some(method) = method {
|
|
classmanager::load_class_by_name(class_name);
|
|
let class = classmanager::get_class_by_name(class_name).unwrap();
|
|
return self.execute_class(stack, class, method.name().as_str(), args.clone());
|
|
} else {
|
|
let name = classmanager::classdef_name(&this.borrow().class_id);
|
|
if let Some(name) = name {
|
|
classmanager::load_class_by_name(&name);
|
|
let class = classmanager::get_class_by_name(&name).unwrap();
|
|
|
|
for parent_id in &class.parents {
|
|
let classdef = classmanager::get_classdef(parent_id);
|
|
let method = classdef.get_method(method_name);
|
|
if let Some(_) = method {
|
|
let class= get_class_by_id(parent_id).unwrap();
|
|
return self.execute_class(stack, class, method_name, args.clone());
|
|
}
|
|
}
|
|
} else {
|
|
panic!("ClassNotFound");
|
|
}
|
|
}
|
|
} else if let ObjectRef::Class(_class) = this {
|
|
//TODO is this right??
|
|
classmanager::load_class_by_name("java/lang/Class");//TODO preload, so this is not needed
|
|
let klazz = classmanager::get_class_by_name("java/lang/Class").unwrap();
|
|
// let klazzdef = self.classmanager.get_classdef(&klazz.id);
|
|
return self.execute_class(stack, klazz, method_name, args);
|
|
}
|
|
}
|
|
println!("this is not an object reference {}", class_name);
|
|
panic!();
|
|
}
|
|
|
|
pub fn execute_special(
|
|
&mut self,
|
|
stack: &mut Vec<StackFrame>,
|
|
class_name: &str,
|
|
method_name: &str,
|
|
args: Vec<Value>,
|
|
) -> Result<Value, Error> {
|
|
classmanager::load_class_by_name(class_name);
|
|
let class = classmanager::get_class_by_name(class_name).unwrap();
|
|
self.execute_class(stack, class, method_name, args)
|
|
}
|
|
|
|
pub fn execute_static(
|
|
&mut self,
|
|
stack: &mut Vec<StackFrame>,
|
|
class_name: &str,
|
|
method_name: &str,
|
|
args: Vec<Value>,
|
|
) -> Result<Value, Error> {
|
|
classmanager::load_class_by_name(class_name);
|
|
let class = classmanager::get_class_by_name(class_name).unwrap();
|
|
self.execute_class(stack, class, method_name, args)
|
|
}
|
|
|
|
pub fn execute_class(
|
|
&mut self,
|
|
stackframes: &mut Vec<StackFrame>,
|
|
this_class: &Class,
|
|
method_name: &str,
|
|
args: Vec<Value>,
|
|
) -> Result<Value, Error> {
|
|
debug!("execute {}.{}", this_class.name, method_name);
|
|
|
|
//TODO implement dynamic dispatch -> get method from instance
|
|
let method = classmanager::get_classdef(&this_class.id).get_method(method_name).unwrap();
|
|
let mut local_params: Vec<Option<Value>> =
|
|
args.clone().iter().map(|e| Some(e.clone())).collect();
|
|
if method.is(Modifier::Native) {
|
|
return invoke_native(self, stackframes, &this_class.name, method_name, args);
|
|
}
|
|
if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() {
|
|
let stackframe = StackFrame::new(&this_class.name, method_name);
|
|
stackframes.push(stackframe);
|
|
|
|
let pc = &mut 0;
|
|
while *pc < code.opcodes.len() {
|
|
let opcode = read_u8(&code.opcodes, pc);
|
|
let cur_frame = current_frame(stackframes);
|
|
debug!("\t{} #{} {} - {:?}", &cur_frame.at, &*pc - 1, opcodes::OPCODES[opcode as usize], cur_frame.data);
|
|
match opcode {
|
|
ACONST_NULL => {
|
|
current_frame(stackframes).push(Value::Null);
|
|
}
|
|
ICONST_M1 => {
|
|
current_frame(stackframes).push(I32(-1));
|
|
}
|
|
ICONST_0 => {
|
|
current_frame(stackframes).push(I32(0));
|
|
}
|
|
ICONST_1 => {
|
|
current_frame(stackframes).push(I32(1));
|
|
}
|
|
ICONST_2 => {
|
|
current_frame(stackframes).push(I32(2));
|
|
}
|
|
ICONST_3 => {
|
|
current_frame(stackframes).push(I32(3));
|
|
}
|
|
ICONST_4 => {
|
|
current_frame(stackframes).push(I32(4));
|
|
}
|
|
ICONST_5 => {
|
|
current_frame(stackframes).push(I32(5));
|
|
}
|
|
LCONST_0 => {
|
|
current_frame(stackframes).push(I64(0));
|
|
}
|
|
LCONST_1 => {
|
|
current_frame(stackframes).push(I64(1));
|
|
}
|
|
FCONST_0 => {
|
|
current_frame(stackframes).push(F32(0.0));
|
|
}
|
|
FCONST_1 => {
|
|
current_frame(stackframes).push(F32(1.0));
|
|
}
|
|
FCONST_2 => {
|
|
current_frame(stackframes).push(F32(2.0));
|
|
}
|
|
DCONST_0 => {
|
|
current_frame(stackframes).push(F64(0.0));
|
|
}
|
|
DCONST_1 => {
|
|
current_frame(stackframes).push(F64(1.0));
|
|
}
|
|
SIPUSH => {
|
|
let s = read_u16(&code.opcodes, pc) as i32;
|
|
current_frame(stackframes).push(I32(s));
|
|
}
|
|
BIPUSH => {
|
|
let c = read_u8(&code.opcodes, pc) as i32;
|
|
current_frame(stackframes).push(I32(c));
|
|
}
|
|
LDC => {
|
|
let cp_index = read_u8(&code.opcodes, pc) as u16;
|
|
let c = method.constant_pool.get(&cp_index).unwrap();
|
|
match c {
|
|
CpEntry::Integer(i) => {
|
|
current_frame(stackframes).push(I32(*i));
|
|
}
|
|
CpEntry::Float(f) => {
|
|
current_frame(stackframes).push(Value::F32(*f));
|
|
}
|
|
CpEntry::Double(d) => {
|
|
current_frame(stackframes).push(Value::F64(*d));
|
|
}
|
|
CpEntry::StringRef(utf8) => {
|
|
//TODO
|
|
let string = classmanager::get_classdef(&this_class.id).cp_utf8(utf8);
|
|
let string: Vec<u8> = string.as_bytes().into();
|
|
classmanager::load_class_by_name("java/lang/String");
|
|
let stringclass = classmanager::get_class_by_name("java/lang/String").unwrap();
|
|
let mut stringinstance = Vm::new_instance(stringclass);
|
|
stringinstance.set(stringclass, "java/lang/String", "value", Value::Ref(ObjectRef::new_byte_array(string)));
|
|
|
|
debug!("new string \"{}\"", utf8);
|
|
|
|
current_frame(stackframes).push(Ref(ObjectRef::Object(Rc::new(RefCell::new(stringinstance)))));
|
|
}
|
|
CpEntry::Long(l) => {
|
|
current_frame(stackframes).push(Value::I64(*l));
|
|
}
|
|
CpEntry::ClassRef(utf8) => {
|
|
let classdef = classmanager::get_classdef(&this_class.id);
|
|
let class_name = classdef.cp_utf8(utf8);
|
|
classmanager::load_class_by_name(class_name);
|
|
let klass_id = classmanager::get_classid(class_name);
|
|
if let Some(class) = classmanager::get_classobject(klass_id) {
|
|
current_frame(stackframes).push(class.clone());
|
|
} else {
|
|
unreachable!("should not be here");
|
|
}
|
|
}
|
|
_ => {
|
|
panic!("add variant {:?}", c)
|
|
}
|
|
}
|
|
}
|
|
LDC_W => {
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
let cp_entry = method.constant_pool.get(&cp_index).unwrap();
|
|
match cp_entry {
|
|
CpEntry::Integer(i) => {
|
|
current_frame(stackframes).push(I32(*i));
|
|
}
|
|
CpEntry::Float(f) => {
|
|
current_frame(stackframes).push(F32(*f));
|
|
}
|
|
CpEntry::StringRef(utf8_index) => {
|
|
if let CpEntry::Utf8(s) = method.constant_pool.get(utf8_index).unwrap() {
|
|
current_frame(stackframes).push(Utf8(s.to_owned()));
|
|
} else {}
|
|
}
|
|
_ => {
|
|
error!("{:?}", cp_entry);
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
LDC2_W => {
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
match method.constant_pool.get(&cp_index).unwrap() {
|
|
CpEntry::Double(d) => {
|
|
current_frame(stackframes).push(Value::F64(*d));
|
|
}
|
|
CpEntry::Long(l) => {
|
|
current_frame(stackframes).push(Value::I64(*l));
|
|
}
|
|
_ => {
|
|
unreachable!()
|
|
}
|
|
}
|
|
}
|
|
ILOAD | LLOAD | FLOAD | DLOAD | ALOAD => {
|
|
// omitting the type checks so far
|
|
let n = read_u8(&code.opcodes, pc) as usize;
|
|
current_frame(stackframes)
|
|
.push(local_params[n].as_ref().unwrap().clone());
|
|
}
|
|
ILOAD_0 | LLOAD_0 | FLOAD_0 | DLOAD_0 | ALOAD_0 => {
|
|
current_frame(stackframes)
|
|
.push(local_params[0].as_ref().unwrap().clone());
|
|
}
|
|
ILOAD_1 | LLOAD_1 | FLOAD_1 | DLOAD_1 | ALOAD_1 => {
|
|
current_frame(stackframes)
|
|
.push(local_params[1].as_ref().unwrap().clone());
|
|
}
|
|
ILOAD_2 | LLOAD_2 | FLOAD_2 | DLOAD_2 | ALOAD_2 => {
|
|
current_frame(stackframes)
|
|
.push(local_params[2].as_ref().unwrap().clone());
|
|
}
|
|
ILOAD_3 | LLOAD_3 | FLOAD_3 | DLOAD_3 | ALOAD_3 => {
|
|
current_frame(stackframes)
|
|
.push(local_params[3].as_ref().unwrap().clone());
|
|
}
|
|
IALOAD | LALOAD | FALOAD | DALOAD | AALOAD | BALOAD | CALOAD | SALOAD => {
|
|
let index = current_frame(stackframes).pop()?;
|
|
let arrayref = current_frame(stackframes).pop()?;
|
|
current_frame(stackframes).push(array_load(index, arrayref)?);
|
|
}
|
|
ISTORE | LSTORE | FSTORE | DSTORE | ASTORE => {
|
|
let index = read_u8(&code.opcodes, pc) as usize;
|
|
self.store(stackframes, &mut local_params, index)?;
|
|
}
|
|
ISTORE_0 | LSTORE_0 | DSTORE_0 | ASTORE_0 | FSTORE_0 => {
|
|
self.store(stackframes, &mut local_params, 0)?;
|
|
}
|
|
ISTORE_1 | LSTORE_1 | DSTORE_1 | ASTORE_1 | FSTORE_1 => {
|
|
self.store(stackframes, &mut local_params, 1)?;
|
|
}
|
|
ISTORE_2 | LSTORE_2 | DSTORE_2 | ASTORE_2 | FSTORE_2 => {
|
|
self.store(stackframes, &mut local_params, 2)?;
|
|
}
|
|
ISTORE_3 | LSTORE_3 | DSTORE_3 | ASTORE_3 | FSTORE_3 => {
|
|
self.store(stackframes, &mut local_params, 3)?;
|
|
}
|
|
BASTORE | IASTORE | LASTORE | CASTORE | SASTORE | FASTORE | DASTORE
|
|
| AASTORE => {
|
|
let value = current_frame(stackframes).pop()?;
|
|
let index = current_frame(stackframes).pop()?;
|
|
let arrayref = current_frame(stackframes).pop()?;
|
|
array_store(value, index, arrayref)?
|
|
}
|
|
POP => {
|
|
current_frame(stackframes).pop()?;
|
|
}
|
|
DUP => {
|
|
let value = current_frame(stackframes).pop()?;
|
|
current_frame(stackframes).push(value.clone());
|
|
current_frame(stackframes).push(value);
|
|
}
|
|
IADD => {
|
|
let value2 = current_frame(stackframes).pop()?;
|
|
let value1 = current_frame(stackframes).pop()?;
|
|
debug!("{:?}+{:?}", value1, value2);
|
|
current_frame(stackframes).push(I32(value1.into_i32() + value2.into_i32()));
|
|
}
|
|
IDIV => {
|
|
let value2 = current_frame(stackframes).pop()?;
|
|
let value1 = current_frame(stackframes).pop()?;
|
|
current_frame(stackframes).push(I32(value1.into_i32() / value2.into_i32()));
|
|
}
|
|
ISHR => {
|
|
let value2 = current_frame(stackframes).pop()?;
|
|
let value1 = current_frame(stackframes).pop()?;
|
|
debug!("{:?} shr {:?}", value1, value2);
|
|
current_frame(stackframes).push(I32(value1.into_i32() >> (value2.into_i32() & 0b00011111)));
|
|
}
|
|
IFEQ | IFNE | IFLT | IFGE | IFGT | IFLE => {
|
|
let jmp_to = read_u16(&code.opcodes, pc) - 3; // -3 so that offset = location of Cmp opcode
|
|
let value = current_frame(stackframes).pop()?;
|
|
Self::if_cmp(pc, opcode, jmp_to, &value, &I32(0));
|
|
}
|
|
|
|
IF_ICMPEQ | IF_ICMPNE | IF_ICMPGT | IF_ICMPGE | IF_ICMPLT | IF_ICMPLE => {
|
|
let jmp_to = read_u16(&code.opcodes, pc) - 3; // -3 so that offset = location of Cmp opcode
|
|
let value1 = current_frame(stackframes).pop()?;
|
|
let value2 = current_frame(stackframes).pop()?;
|
|
Self::if_cmp(pc, opcode, jmp_to, &value1, &value2);
|
|
}
|
|
GOTO => {
|
|
let jmp_to = read_u16(&code.opcodes, pc) - 3;
|
|
*pc += jmp_to as usize;
|
|
debug!("GOTO {}", *pc)
|
|
}
|
|
IRETURN | FRETURN | DRETURN | ARETURN => {
|
|
let result = current_frame(stackframes).pop();
|
|
stackframes.pop();
|
|
return result;
|
|
}
|
|
RETURN_VOID => {
|
|
stackframes.pop();
|
|
return Ok(Void);
|
|
}
|
|
GETSTATIC => {
|
|
let field_index = read_u16(&code.opcodes, pc);
|
|
let field_value = get_static(this_class, field_index)?;
|
|
|
|
current_frame(stackframes).push(field_value);
|
|
}
|
|
PUTSTATIC => {
|
|
let classdef = classmanager::get_classdef(&this_class.id);
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
let (class_index, field_name_and_type_index) =
|
|
classdef.cp_field_ref(&cp_index); // all these unwraps are safe as long as the class is valid
|
|
let (name_index, _) =
|
|
classdef.cp_name_and_type(field_name_and_type_index);
|
|
let name = classdef.cp_utf8(name_index);
|
|
let that_class_name_index = classdef.cp_class_ref(class_index);
|
|
let that_class_name = classdef.cp_utf8(that_class_name_index);
|
|
let that_class = classmanager::get_class_by_name(that_class_name).unwrap();
|
|
let val_index = that_class.static_field_mapping
|
|
.get(that_class_name)
|
|
.unwrap()
|
|
.get(name)
|
|
.unwrap()
|
|
.index;
|
|
let value = current_frame(stackframes).pop()?;
|
|
classmanager::set_static(&this_class.id, val_index, value);
|
|
}
|
|
GETFIELD => {
|
|
let classdef = classmanager::get_classdef(&this_class.id);
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
let (class_index, field_name_and_type_index) =
|
|
classdef.cp_field_ref(&cp_index);
|
|
let (field_name_index, _) =
|
|
classdef.cp_name_and_type(field_name_and_type_index);
|
|
let class_name_index = classdef.cp_class_ref(class_index);
|
|
let declared_type = classdef.cp_utf8(class_name_index);
|
|
|
|
let field_name = classdef.cp_utf8(field_name_index);
|
|
debug!("get field {}.{}",declared_type, field_name);
|
|
let objectref = current_frame(stackframes).pop()?;
|
|
if let Ref(instance) = objectref {
|
|
if let ObjectRef::Object(object) = instance {
|
|
let runtime_type = classmanager::get_class_by_id(&object.borrow().class_id).unwrap();
|
|
let object = object.borrow();
|
|
let value = object.get(runtime_type, declared_type, field_name);
|
|
current_frame(stackframes).push(value.clone());
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
} else {
|
|
unreachable!("objectref {:?}", objectref)
|
|
}
|
|
}
|
|
PUTFIELD => {
|
|
let classdef = classmanager::get_classdef(&this_class.id);
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
let (class_index, field_name_and_type_index) =
|
|
classdef.cp_field_ref(&cp_index);
|
|
let (field_name_index, _) =
|
|
classdef.cp_name_and_type(field_name_and_type_index);
|
|
let class_name_index = classdef.cp_class_ref(class_index);
|
|
let declared_type = classdef.cp_utf8(class_name_index);
|
|
let field_name = classdef.cp_utf8(field_name_index);
|
|
|
|
let value = current_frame(stackframes).pop()?;
|
|
let objectref = current_frame(stackframes).pop()?;
|
|
if let Ref(instance) = objectref {
|
|
if let ObjectRef::Object(object) = instance {
|
|
let runtime_type = classmanager::get_class_by_id(&object.borrow().class_id).unwrap();
|
|
object.borrow_mut().set(runtime_type, declared_type, field_name, value);
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
INVOKESPECIAL => {
|
|
// TODO differentiate these opcodes
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
if let Some(invocation) =
|
|
get_signature_for_invoke(&method.constant_pool, cp_index)
|
|
{
|
|
debug!("invoke {:?}", invocation);
|
|
let mut args = Vec::with_capacity(invocation.method.num_args);
|
|
for _ in 0..invocation.method.num_args {
|
|
args.insert(0, current_frame(stackframes).pop()?.clone());
|
|
}
|
|
args.insert(0, current_frame(stackframes).pop()?);
|
|
let return_value = self.execute_special(stackframes,
|
|
&invocation.class_name,
|
|
&invocation.method.name,
|
|
args,
|
|
)?;
|
|
if let Ref(objectref) = &return_value {
|
|
if let ObjectRef::Object(object) = objectref {
|
|
debug!("return {:?}", object);
|
|
}
|
|
} else {
|
|
debug!("return {:?}", return_value);
|
|
}
|
|
match return_value {
|
|
Void => {}
|
|
_ => {
|
|
current_frame(stackframes).push(return_value.clone());
|
|
}
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
INVOKEVIRTUAL => {
|
|
// TODO differentiate these opcodes
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
if let Some(invocation) =
|
|
get_signature_for_invoke(&method.constant_pool, cp_index)
|
|
{
|
|
debug!("invoke {:?}", invocation);
|
|
let mut args = Vec::with_capacity(invocation.method.num_args);
|
|
for _ in 0..invocation.method.num_args {
|
|
args.insert(0, current_frame(stackframes).pop()?.clone());
|
|
}
|
|
args.insert(0, current_frame(stackframes).pop()?);
|
|
let return_value = self.execute_virtual(
|
|
stackframes,
|
|
&invocation.class_name,
|
|
&invocation.method.name,
|
|
args,
|
|
)?;
|
|
if let Ref(objectref) = &return_value {
|
|
if let ObjectRef::Object(object) = objectref {
|
|
debug!("return {:?}", object);
|
|
}
|
|
} else {
|
|
debug!("return {:?}", return_value);
|
|
}
|
|
match return_value {
|
|
Void => {}
|
|
_ => {
|
|
current_frame(stackframes).push(return_value.clone());
|
|
}
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
INVOKESTATIC => {
|
|
let cp_index = read_u16(&code.opcodes, pc);
|
|
if let Some(invocation) =
|
|
get_signature_for_invoke(&method.constant_pool, cp_index)
|
|
{
|
|
let mut args = Vec::with_capacity(invocation.method.num_args);
|
|
for _ in 0..invocation.method.num_args {
|
|
args.insert(0, current_frame(stackframes).pop()?.clone());
|
|
}
|
|
let return_value = self.execute_static(stackframes,
|
|
&invocation.class_name,
|
|
&invocation.method.name,
|
|
args,
|
|
)?;
|
|
if let Ref(objectref) = &return_value {
|
|
if let ObjectRef::Object(object) = objectref {
|
|
debug!("return {:?}", object);
|
|
}
|
|
} else {
|
|
debug!("return {:?}", return_value);
|
|
}
|
|
match return_value {
|
|
Void => {}
|
|
_ => {
|
|
current_frame(stackframes).push(return_value.clone());
|
|
}
|
|
}
|
|
} else {
|
|
unreachable!()
|
|
}
|
|
}
|
|
NEW => {
|
|
let classdef = classmanager::get_classdef(&this_class.id);
|
|
let class_index = &read_u16(&code.opcodes, pc);
|
|
let class_name_index = classdef.cp_class_ref(class_index);
|
|
let class_name = classdef.cp_utf8(class_name_index);
|
|
classmanager::load_class_by_name(class_name);
|
|
let class_to_instantiate = classmanager::get_class_by_name(class_name).unwrap();
|
|
|
|
let object = ObjectRef::Object(Rc::new(RefCell::new(Vm::new_instance(
|
|
class_to_instantiate,
|
|
))));
|
|
current_frame(stackframes).push(Ref(object));
|
|
}
|
|
NEWARRAY => {
|
|
let arraytype = read_u8(&code.opcodes, pc);
|
|
let count = current_frame(stackframes).pop()?;
|
|
let array = ObjectRef::new_array(arraytype, count.into_i32() as usize);
|
|
current_frame(stackframes).push(Ref(array));
|
|
}
|
|
ANEWARRAY => {
|
|
let classdef = classmanager::get_classdef(&this_class.id);
|
|
let class_index = &read_u16(&code.opcodes, pc);
|
|
let class_name_index = classdef.cp_class_ref(class_index);
|
|
let class_name = classdef.cp_utf8(class_name_index);
|
|
classmanager::load_class_by_name(class_name);
|
|
let arraytype = classmanager::get_class_by_name(class_name).unwrap();
|
|
let count = current_frame(stackframes).pop()?.into_i32();
|
|
let array = ObjectRef::new_object_array(arraytype, count as usize);
|
|
current_frame(stackframes).push(Ref(array));
|
|
}
|
|
ARRAYLENGTH => {
|
|
let val = current_frame(stackframes).pop()?;
|
|
if let Ref(val) = val {
|
|
current_frame(stackframes).push(I32(val.get_array_length() as i32));
|
|
} else {
|
|
unreachable!("array length {:?}", val);
|
|
}
|
|
}
|
|
MONITORENTER | MONITOREXIT => {
|
|
current_frame(stackframes).pop()?;
|
|
} //TODO implement
|
|
IFNULL | IFNONNULL => {
|
|
let jmp_to = read_u16(&code.opcodes, pc) - 3;
|
|
let value = current_frame(stackframes).pop()?;
|
|
let its_null = if let Null = value { true } else { false };
|
|
|
|
if its_null && opcode == IFNULL {
|
|
debug!("\t\tIF NULL =>{}: JMP {}", its_null, *pc + jmp_to as usize);
|
|
*pc += jmp_to as usize;
|
|
}
|
|
if !its_null && opcode == IFNONNULL {
|
|
debug!("\t\tIF NOT NULL =>{}: JMP {}", its_null, *pc + jmp_to as usize);
|
|
*pc += jmp_to as usize;
|
|
}
|
|
//debug info
|
|
if !its_null && opcode == IFNULL {
|
|
debug!("\t\tIF NULL =>false: NO JMP");
|
|
}
|
|
if its_null && opcode == IFNONNULL {
|
|
debug!("\t\tIF NONNULL =>false: NO JMP");
|
|
}
|
|
}
|
|
//TODO implement all opcodes
|
|
_ => {
|
|
panic!("opcode {} not implemented {:?}", opcode, stackframes)
|
|
//TODO implement proper --stacktraces-- error handling
|
|
}
|
|
}
|
|
}
|
|
}
|
|
panic!("should not happen")
|
|
}
|
|
|
|
fn if_cmp(pc: &mut usize, opcode: u8, jmp_to: u16, value1: &Value, value2: &Value) {
|
|
if let I32(value1) = value1 {
|
|
if let I32(value2) = value2 {
|
|
let jump = match opcode {
|
|
IF_ICMPEQ => value1 == value2,
|
|
IF_ICMPNE => value1 != value2,
|
|
IF_ICMPGT => value1 > value2,
|
|
IF_ICMPGE => value1 >= value2,
|
|
IF_ICMPLT => value1 < value2,
|
|
IF_ICMPLE => value1 <= value2,
|
|
_ => false,
|
|
};
|
|
if jump {
|
|
debug!("\t\tIF({}) JMP {}", jump, *pc + jmp_to as usize);
|
|
*pc += jmp_to as usize;
|
|
} else {
|
|
debug!("\t\tIF({}) NO JMP", jump);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/// store in local param
|
|
fn store(
|
|
&mut self,
|
|
stack: &mut Vec<StackFrame>,
|
|
local_params: &mut Vec<Option<Value>>,
|
|
index: usize,
|
|
) -> Result<(), Error> {
|
|
let value = current_frame(stack).pop()?;
|
|
while local_params.len() < index + 1 {
|
|
local_params.push(None);
|
|
}
|
|
local_params[index] = Some(value);
|
|
Ok(())
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct Invocation {
|
|
class_name: String,
|
|
method: MethodSignature,
|
|
}
|
|
|
|
impl Invocation {
|
|
pub fn new(class_name: String, method: MethodSignature) -> Self {
|
|
Self {
|
|
class_name,
|
|
method,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub(crate) struct MethodSignature {
|
|
name: String,
|
|
num_args: usize,
|
|
}
|
|
|
|
impl MethodSignature {
|
|
pub(crate) fn new(name: String, num_args: usize) -> Self {
|
|
MethodSignature {
|
|
name,
|
|
num_args,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn current_frame(stackframes: &mut Vec<StackFrame>) -> &mut StackFrame {
|
|
let i = stackframes.len() - 1;
|
|
stackframes.get_mut(i).unwrap()
|
|
} |