From d51f6627b3ea5a3a370556e6ab5415b880a7de34 Mon Sep 17 00:00:00 2001 From: Shautvast Date: Fri, 24 Nov 2023 22:57:25 +0100 Subject: [PATCH] Updated design for stackframes. Unsafe totally removed. Overhauled class loading. still WIP --- src/class.rs | 205 +--------- src/classloader/classdef.rs | 12 +- src/classloader/code_parser.rs | 262 +++++++++++++ src/classloader/io.rs | 65 +++- src/classloader/mod.rs | 12 + src/classmanager.rs | 147 ++----- src/heap.rs | 0 src/lib.rs | 6 +- src/main.rs | 12 +- src/value.rs | 39 ++ src/vm/array.rs | 5 +- src/vm/mod.rs | 9 +- src/vm/native.rs | 161 -------- src/vm/object.rs | 160 ++++++++ src/vm/opcodes.rs | 673 +++++++++++---------------------- src/vm/operations.rs | 155 -------- src/vm/runtime.rs | 531 ++++++++++++++++++++++++++ src/vm/stack.rs | 29 -- src/vm/vm.rs | 657 -------------------------------- tests/class_tests.rs | 37 +- 20 files changed, 1355 insertions(+), 1822 deletions(-) create mode 100644 src/classloader/code_parser.rs delete mode 100644 src/heap.rs create mode 100644 src/value.rs delete mode 100644 src/vm/native.rs create mode 100644 src/vm/object.rs delete mode 100644 src/vm/operations.rs create mode 100644 src/vm/runtime.rs delete mode 100644 src/vm/stack.rs delete mode 100644 src/vm/vm.rs diff --git a/src/class.rs b/src/class.rs index 0e4862a..aa40223 100644 --- a/src/class.rs +++ b/src/class.rs @@ -1,14 +1,5 @@ -use std::cell::RefCell; use std::collections::{HashMap, LinkedList}; -use std::fmt::Debug; -use std::rc::Rc; -use log::debug; -use rand::random; - -use crate::class::ObjectRef::*; - -/// ClassId facilitates loose coupling between classes, classdefs and objects pub type ClassId = usize; #[derive(Debug, Clone)] @@ -29,6 +20,7 @@ impl TypeIndex { #[derive(Debug, Clone)] pub struct Class { pub id: ClassId, + pub initialized: bool, pub name: String, pub superclass: Option, pub parents: LinkedList, @@ -48,197 +40,4 @@ impl Class { .reduce(|acc, e| acc + e) .unwrap_or(0) } -} - - -#[derive(Debug, Clone)] -pub enum Value { - // variant returned for void methods - Void, - // 'pointer' to nothing - Null, - // primitives - I32(i32), - I64(i64), - F32(f32), - F64(f64), - BOOL(bool), - CHAR(char), - // objects and arrays - Ref(ObjectRef), - // special object - Utf8(String), -} - -impl Value { - // panics if not correct type - pub fn into_i32(self) -> i32 { - if let Value::I32(v) = self { - v - } else { - panic!("{:?} is not I32", self); - } - } - - pub fn into_object(self) -> ObjectRef { - if let Value::Ref(v) = self { - v - } else { - panic!(); - } - } -} - -#[derive(Debug, Clone)] -pub enum ObjectRef { - ByteArray(Vec), - ShortArray(Vec), - IntArray(Vec), - LongArray(Vec), - FloatArray(Vec), - DoubleArray(Vec), - BooleanArray(Vec), - CharArray(Vec), - StringArray(Vec), - ObjectArray(ClassId, Vec), - Object(Rc>), - Class(Class), -} - -impl ObjectRef { - pub fn get_array_length(&self) -> usize { - match self { - ByteArray(d) => d.len(), - ShortArray(d) => d.len(), - IntArray(d) => d.len(), - LongArray(d) => d.len(), - FloatArray(d) => d.len(), - DoubleArray(d) => d.len(), - BooleanArray(d) => d.len(), - CharArray(d) => d.len(), - StringArray(d) => d.len(), - ObjectArray(_, d) => d.len(), - _ => unreachable!("not an array {:?}", self) - } - } -} - -pub enum ArrayType { - BOOLEAN = 4, - CHAR = 5, - FLOAT = 6, - DOUBLE = 7, - BYTE = 8, - SHORT = 9, - INT = 10, - LONG = 11, -} - -impl ObjectRef { - pub fn new_object_array(class: &Class, size: usize) -> Self { - ObjectArray(class.id, Vec::with_capacity(size)) - } - - pub fn new_array(arraytype: u8, size: usize) -> Self { - match arraytype { - 8 => ByteArray(Vec::with_capacity(size)), - 9 => ShortArray(Vec::with_capacity(size)), - 10 => IntArray(Vec::with_capacity(size)), - 11 => LongArray(Vec::with_capacity(size)), - 6 => FloatArray(Vec::with_capacity(size)), - 7 => DoubleArray(Vec::with_capacity(size)), - 4 => BooleanArray(Vec::with_capacity(size)), - 5 => CharArray(Vec::with_capacity(size)), - _ => unreachable!("impossible array type") - } - } - - pub fn new_int_array(size: usize) -> Self { - IntArray(Vec::with_capacity(size)) - } - - pub fn new_byte_array(d: Vec) -> Self { - ByteArray(into_vec_i8(d)) - } -} - -fn into_vec_i8(v: Vec) -> Vec { - let mut v = std::mem::ManuallyDrop::new(v); - - // then, pick apart the existing Vec - let p = v.as_mut_ptr(); - let len = v.len(); - let cap = v.capacity(); - - // finally, adopt the data into a new Vec - unsafe { Vec::from_raw_parts(p as *mut i8, len, cap) } -} - -#[derive(Debug)] -pub struct Object { - /// unique id for instance - pub id: u32, - /// loose ref to class - pub class_id: ClassId, - /// instance field data - pub data: Vec, -} - -// object, not array -impl Object { - - pub fn new(class: &Class) -> Self { - let instance_data = Object::init_fields(class); - Self { - id: random(), - class_id: class.id, - data: instance_data, - } - } - - // initializes all non-static fields to their default values - pub(crate) fn init_fields(class: &Class) -> Vec { - let mut field_data = vec![Value::Null; class.n_object_fields()]; - - for (_, fields) in &class.object_field_mapping { - for (_, type_index) in fields { - let value = match type_index.type_name.as_str() { - "Z" => Value::BOOL(false), - "B" => Value::I32(0), - "S" => Value::I32(0), - "I" => Value::I32(0), - "J" => Value::I64(0), - "F" => Value::F32(0.0), - "D" => Value::F64(0.0), - _ => Value::Null, - }; - field_data[type_index.index] = value.into(); - } - } - - field_data - } - - pub fn set(&mut self, runtime_type: &Class, declared_type: &str, field_name: &str, value: Value) { - debug!("set {:?}.{}", runtime_type.name, field_name); - let type_index = runtime_type - .object_field_mapping - .get(declared_type) - .unwrap() - .get(field_name) - .unwrap(); - self.data[type_index.index] = value; - } - - pub fn get(&self, runtime_type: &Class, declared_type: &String, field_name: &String) -> &Value { - let type_index = runtime_type - .object_field_mapping - .get(declared_type) - .unwrap() - .get(field_name) - .unwrap(); - debug!("get {:?}:{}.{}:{} @{}", runtime_type, declared_type, field_name, type_index.type_name, type_index.index); - debug!("from data {:?}", self.data); - self.data.get(type_index.index).unwrap() - } -} +} \ No newline at end of file diff --git a/src/classloader/classdef.rs b/src/classloader/classdef.rs index 73f6c0b..35ca972 100644 --- a/src/classloader/classdef.rs +++ b/src/classloader/classdef.rs @@ -4,10 +4,11 @@ use std::fmt::{Debug, Formatter}; use std::rc::Rc; use crate::classloader::io::read_u16; +use crate::vm::opcodes::Opcode; /// This is the class representation when the bytecode had just been loaded. -pub struct ClassDef { +pub(crate) struct ClassDef { pub minor_version: u16, pub major_version: u16, pub constant_pool: Rc>, @@ -317,16 +318,17 @@ impl Field { } } -pub struct Method { +pub(crate) struct Method { pub(crate) constant_pool: Rc>, access_flags: u16, name_index: u16, descriptor_index: u16, pub(crate) attributes: HashMap, + pub code: Vec, } -impl fmt::Debug for Method { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { +impl Debug for Method { + fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { write!( f, "Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}", @@ -342,6 +344,7 @@ impl Method { name_index: u16, descriptor_index: u16, attributes: HashMap, + code: Vec, ) -> Self { Method { constant_pool, @@ -349,6 +352,7 @@ impl Method { name_index, descriptor_index, attributes, + code, } } diff --git a/src/classloader/code_parser.rs b/src/classloader/code_parser.rs new file mode 100644 index 0000000..98b8f54 --- /dev/null +++ b/src/classloader/code_parser.rs @@ -0,0 +1,262 @@ +use std::collections::HashMap; + +use log::debug; + +use crate::classloader::io::{read_i32, read_lookupswitch, read_tableswitch, read_u16, read_u8, read_wide_opcode}; +use crate::vm::opcodes::Opcode::{self, *}; + +pub(crate) fn parse_code(opcodes: &[u8]) -> Vec { + let mut code: HashMap = HashMap::new(); + let mut c = 0; + let mut opcode_index: u16 = 0; + // debug!("len {:?}", opcodes.len()); + while c < opcodes.len() { + let opcode = get_opcode(opcodes, &mut c); + code.insert(c as u16, (opcode_index, opcode)); + opcode_index += 1; + } + let code2 = code.clone(); //clone to look up + + // for jumps, map index of opcode as u8 to index of opcode as enum + code.into_iter().map(|(_, (_, opcode))| + match opcode { + IFNULL(goto) => { + debug!("goto {:?}", goto); + debug!("{:?}", code2); + IFNULL(code2.get(&goto).unwrap().0) + } + IFNONNULL(goto) => { + debug!("goto {:?}", &goto); + debug!("{:?}", code2); + IFNONNULL(code2.get(&goto).unwrap().0) + } + //TODO more jump instructions + _ => opcode + } + ).collect() +} + +fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode { + let opcode_u8 = read_u8(opcodes, c); + + let opcode = match opcode_u8 { + 0 => NOP, + 1 => ACONST_NULL, + 2 => ICONST_M1, + 3 => ICONST_0, + 4 => ICONST_1, + 5 => ICONST_2, + 6 => ICONST_3, + 7 => ICONST_4, + 8 => ICONST_5, + 9 => LCONST_0, + 10 => LCONST_1, + 11 => FCONST_0, + 12 => FCONST_1, + 13 => FCONST_2, + 14 => DCONST_0, + 15 => DCONST_1, + 16 => BIPUSH(read_u8(opcodes, c)), + 17 => SIPUSH(read_u16(opcodes, c)), + 18 => LDC(read_u8(opcodes, c) as u16), + 19 => LDC_W(read_u16(opcodes, c) as u16), + 20 => LDC2_W(read_u16(opcodes, c)), + 21 => ILOAD(read_u8(opcodes, c)), + 22 => LLOAD(read_u8(opcodes, c)), + 23 => FLOAD(read_u8(opcodes, c)), + 24 => DLOAD(read_u8(opcodes, c)), + 25 => ALOAD(read_u8(opcodes, c)), + 26 => ILOAD_0, + 27 => ILOAD_1, + 28 => ILOAD_2, + 29 => ILOAD_3, + 30 => LLOAD_0, + 31 => LLOAD_1, + 32 => LLOAD_2, + 33 => LLOAD_3, + 34 => FLOAD_0, + 35 => FLOAD_1, + 36 => FLOAD_2, + 37 => FLOAD_3, + 38 => DLOAD_0, + 39 => DLOAD_1, + 40 => DLOAD_2, + 41 => DLOAD_3, + 42 => ALOAD_0, + 43 => ALOAD_1, + 44 => ALOAD_2, + 45 => ALOAD_3, + 46 => IALOAD, + 47 => LALOAD, + 48 => FALOAD, + 49 => DALOAD, + 50 => AALOAD, + 51 => BALOAD, + 52 => CALOAD, + 53 => SALOAD, + 54 => ISTORE(read_u8(opcodes, c)), + 55 => LSTORE(read_u8(opcodes, c)), + 56 => FSTORE(read_u8(opcodes, c)), + 57 => DSTORE(read_u8(opcodes, c)), + 58 => ASTORE(read_u8(opcodes, c)), + 59 => ISTORE_0, + 60 => ISTORE_1, + 61 => ISTORE_2, + 62 => ISTORE_3, + 63 => LSTORE_0, + 64 => LSTORE_1, + 65 => LSTORE_2, + 66 => LSTORE_3, + 67 => FSTORE_0, + 68 => FSTORE_1, + 69 => FSTORE_2, + 70 => FSTORE_3, + 71 => DSTORE_0, + 72 => DSTORE_1, + 73 => DSTORE_2, + 74 => DSTORE_3, + 75 => ASTORE_0, + 76 => ASTORE_1, + 77 => ASTORE_2, + 78 => ASTORE_3, + 79 => IASTORE, + 80 => LASTORE, + 81 => FASTORE, + 82 => DASTORE, + 83 => AASTORE, + 84 => BASTORE, + 85 => CASTORE, + 86 => SASTORE, + 87 => POP, + 89 => DUP, + 90 => DUP_X1, + 91 => DUP_X2, + 92 => DUP2, + 93 => DUP2_X1, + 94 => DUP2_X2, + 96 => IADD, + 97 => LADD, + 98 => FADD, + 99 => DADD, + 100 => ISUB, + 101 => LSUB, + 102 => FSUB, + 103 => DSUB, + 104 => IMUL, + 105 => LMUL, + 106 => FMUL, + 107 => DMUL, + 108 => IDIV, + 109 => LDIV, + 110 => FDIV, + 111 => DDIV, + 112 => IREM, + 113 => LREM, + 114 => FREM, + 115 => DREM, + 116 => INEG, + 117 => LNEG, + 118 => FNEG, + 119 => DNEG, + 120 => ISHL, + 121 => LSHL, + 122 => ISHR, + 123 => LSHR, + 126 => IAND, + 127 => LAND, + 128 => IOR, + 129 => LOR, + 130 => IXOR, + 131 => LXOR, + 132 => IINC(read_u8(opcodes, c), read_u8(opcodes, c)), + 133 => I2L, + 134 => I2F, + 135 => I2D, + 136 => L2I, + 137 => L2F, + 138 => L2D, + 139 => F2I, + 140 => F2L, + 141 => F2D, + 142 => D2I, + 143 => D2L, + 144 => D2F, + 145 => I2B, + 146 => I2C, + 147 => I2S, + 148 => LCMP, + 149 => FCMPL, + 150 => FCMPG, + 151 => DCMPL, + 152 => DCMPG, + 153 => IFEQ(read_u16(opcodes, c)), + 154 => IFNE(read_u16(opcodes, c)), + 155 => IFLT(read_u16(opcodes, c)), + 156 => IFGE(read_u16(opcodes, c)), + 157 => IFGT(read_u16(opcodes, c)), + 158 => IFLE(read_u16(opcodes, c)), + 159 => IF_ICMPEQ(read_u16(opcodes, c)), + 160 => IF_ICMPNE(read_u16(opcodes, c)), + 161 => IF_ICMPLT(read_u16(opcodes, c)), + 162 => IF_ICMPGE(read_u16(opcodes, c)), + 163 => IF_ICMPGT(read_u16(opcodes, c)), + 164 => IF_ICMPLE(read_u16(opcodes, c)), + 165 => IF_ACMPEQ(read_u16(opcodes, c)), + 166 => IF_ACMPNE(read_u16(opcodes, c)), + 167 => GOTO(read_u16(opcodes, c)), + 168 => JSR(read_u16(opcodes, c)), + 169 => RET(read_u8(opcodes, c)), + 170 => TABLESWITCH(read_tableswitch(opcodes, c)), + 171 => LOOKUPSWITCH(read_lookupswitch(opcodes, c)), + 172 => IRETURN, + 174 => FRETURN, + 175 => DRETURN, + 176 => ARETURN, + 177 => RETURN_VOID, + 178 => GETSTATIC(read_u16(opcodes, c)), + 179 => PUTSTATIC(read_u16(opcodes, c)), + 180 => GETFIELD(read_u16(opcodes, c)), + 181 => PUTFIELD(read_u16(opcodes, c)), + 182 => INVOKEVIRTUAL(read_u16(opcodes, c)), + 183 => INVOKESPECIAL(read_u16(opcodes, c)), + 184 => INVOKESTATIC(read_u16(opcodes, c)), + 185 => { + let index = read_u16(opcodes, c); + let count = read_u8(opcodes, c); + *c += 1; + INVOKEINTERFACE(index, count) + } + 186 => { + let i = read_u16(opcodes, c); + *c += 2; + INVOKEDYNAMIC(i) + } + 187 => NEW(read_u16(opcodes, c)), + 188 => NEWARRAY(read_u8(opcodes, c)), + 189 => ANEWARRAY(read_u16(opcodes, c)), + 190 => ARRAYLENGTH, + 191 => ATHROW, + 192 => CHECKCAST(read_u16(opcodes, c)), + 193 => INSTANCEOF(read_u16(opcodes, c)), + 194 => MONITORENTER, + 195 => MONITOREXIT, + 196 => WIDE(Box::new(read_wide_opcode(opcodes, c))), + 197 => MULTIANEWARRAY(read_u16(opcodes, c), read_u8(opcodes, c)), + 198 => { + let j = read_u16(opcodes, c); + debug!("ifnull {}",*c as u16 + j - 3); + IFNULL(*c as u16 + j - 3) + } + 199 => { + let j = read_u16(opcodes, c); + debug!("ifnonnull {} ", *c as u16 + j - 3); + IFNONNULL(*c as u16 + j - 3) + } + 200 => GOTOW(read_i32(opcodes, c)), + 201 => JSR_W(read_i32(opcodes, c)), + + + _ => panic!("{}", opcode_u8), + }; + opcode +} \ No newline at end of file diff --git a/src/classloader/io.rs b/src/classloader/io.rs index ef195a5..2c7092b 100644 --- a/src/classloader/io.rs +++ b/src/classloader/io.rs @@ -1,6 +1,8 @@ use std::fs::{self}; use anyhow::{anyhow, Error}; +use crate::vm::opcodes::Opcode; +use crate::vm::opcodes::Opcode::*; #[cfg(target_family = "unix")] pub const PATH_SEPARATOR: char = ':'; @@ -44,7 +46,6 @@ pub fn find_class(classpath: &Vec, class_name: &str) -> Result u8 { @@ -116,3 +117,65 @@ pub(crate) fn read_f64(data: &[u8], pos: &mut usize) -> f64 { .expect("slice with incorrect length"), ) } + +pub(crate) fn read_tableswitch(data: &[u8], pos: &mut usize) -> Tableswitch { + while read_u8(data, pos) == 0 {} + *pos -= 1; + let default = read_i32(data, pos); + let low = read_i32(data, pos); + let high = read_i32(data, pos); + let mut offsets = vec![]; + for _ in low..=high { + offsets.push(read_i32(data, pos)); + } + Tableswitch { default, low, high, offsets } +} + +pub(crate) fn read_lookupswitch(data: &[u8], pos: &mut usize) -> Lookupswitch { + while read_u8(data, pos) == 0 {} + *pos -= 1; + let default = read_i32(data, pos); + let npairs = read_i32(data, pos); + let mut match_offset_pairs = vec![]; + for _ in 0..npairs { + match_offset_pairs.push((read_i32(data, pos), read_i32(data, pos))); + } + Lookupswitch { default, match_offset_pairs } +} + +pub(crate) fn read_wide_opcode(data: &[u8], pos: &mut usize) -> Opcode { + let opcode = read_u8(data, pos); + if opcode == 132 { + WIDE_IINC(read_u16(data, pos), read_u16(data, pos)) + } else { + let index = read_u16(data, pos); + match opcode { + 21 => WIDE_ILOAD(index), + 22 => WIDE_LLOAD(index), + 23 => WIDE_FLOAD(index), + 24 => WIDE_DLOAD(index), + 25 => WIDE_ALOAD(index), + 54 => WIDE_ISTORE(index), + 55 => WIDE_LSTORE(index), + 56 => WIDE_FSTORE(index), + 57 => WIDE_DSTORE(index), + 58 => WIDE_ASTORE(index), + 169 => WIDE_RET(index), + _ => { unreachable!("unknown opcode for WIDE") } + } + } +} + +#[derive(Clone, Debug)] +pub(crate) struct Tableswitch { + default: i32, + low: i32, + high: i32, + offsets: Vec, +} + +#[derive(Clone, Debug)] +pub(crate) struct Lookupswitch { + default: i32, + match_offset_pairs: Vec<(i32, i32)>, +} \ No newline at end of file diff --git a/src/classloader/mod.rs b/src/classloader/mod.rs index 1348a54..b34cd5b 100644 --- a/src/classloader/mod.rs +++ b/src/classloader/mod.rs @@ -9,9 +9,11 @@ use log::debug; use crate::classloader::io::{find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8}; use crate::classloader::classdef::{AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode}; +use crate::classloader::code_parser::parse_code; pub mod classdef; pub(crate) mod io; +mod code_parser; pub(crate) fn get_classdef(classpath: &Vec, class_name: &str) -> Result { debug!("read class {} ", class_name); @@ -94,6 +96,8 @@ fn load_class(bytecode: Vec) -> Result { } } + + Ok(ClassDef::new( minor_version, major_version, @@ -238,12 +242,20 @@ fn read_method( } } + let code = + if let Some(AttributeType::Code(code)) = attributes.get("Code") { + parse_code(&code.opcodes) + } else{ + vec![] + }; + Method::new( constant_pool, access_flags, name_index, descriptor_index, attributes, + code ) } diff --git a/src/classmanager.rs b/src/classmanager.rs index 6f781fd..871fb0e 100644 --- a/src/classmanager.rs +++ b/src/classmanager.rs @@ -5,93 +5,16 @@ use std::rc::Rc; use log::debug; use once_cell::sync::Lazy; -use crate::class::{Class, ClassId, ObjectRef, TypeIndex, Value::*, Value}; -use crate::class::Object; +use crate::class::{Class, ClassId, TypeIndex}; use crate::classloader; -use crate::classloader::classdef::{ClassDef, Modifier}; -use crate::classloader::io::PATH_SEPARATOR; -use crate::vm::Vm; +use crate::classloader::classdef::{ClassDef, Method, Modifier}; +use crate::vm::object::{Object, ObjectRef}; +use crate::value::Value; +use crate::value::Value::*; +use crate::vm::runtime::Vm; -static mut CLASSMANAGER: Lazy = Lazy::new(|| ClassManager::new()); static PRIMITIVES: Lazy> = Lazy::new(|| vec!["B", "S", "I", "J", "F", "D", "Z", "J", "C"]); -pub fn init() { - debug!("classmanager init"); - unsafe { - CLASSMANAGER.classes.clear(); - CLASSMANAGER.names.clear(); - CLASSMANAGER.classdefs.clear(); - CLASSMANAGER.class_objects.clear(); - CLASSMANAGER.static_class_data.clear(); - } -} - -pub fn set_classpath(classpath: &str) { - unsafe { - CLASSMANAGER.set_classpath(classpath); - } -} - -pub fn get_class_by_id(id: ClassId) -> Option<&'static Class> { - unsafe { - CLASSMANAGER.get_class_by_id(&id) - } -} - -pub fn classdef_name(id: &ClassId) -> Option { - unsafe { - CLASSMANAGER.classdef_name(id) - } -} - -pub fn get_classid(name: &str) -> ClassId { - unsafe { - CLASSMANAGER.get_classid(name).clone() - } -} - -pub fn get_classdef(id: ClassId) -> &'static ClassDef { - unsafe { - CLASSMANAGER.get_classdef(&id) - } -} - -pub fn load_class_by_name(name: &str) { - unsafe { - CLASSMANAGER.load_class_by_name(name) - } -} - -pub fn get_class_by_name(name: &str) -> Option<&Class> { - unsafe { - CLASSMANAGER.get_class_by_name(name) - } -} - -pub fn add_class(name: &str) -> ClassId { - unsafe { - CLASSMANAGER.add_class(name) - } -} - -pub fn get_static(id: &ClassId, index: usize) -> Value { - unsafe { - CLASSMANAGER.static_class_data.get(id).unwrap()[index].clone() - } -} - -pub fn set_static(id: ClassId, index: usize, value: Value) { - unsafe { - CLASSMANAGER.static_class_data.get_mut(&id).unwrap()[index] = value; - } -} - -pub fn get_classobject(id: ClassId) -> Option<&'static Value> { - unsafe { - CLASSMANAGER.class_objects.get(&id) - } -} - //TODO less pubs pub struct ClassManager { static_class_data: HashMap>, @@ -101,18 +24,17 @@ pub struct ClassManager { classpath: Vec, //references to classdefs, ie the static class info - pub classdefs: HashMap, + pub(crate) classdefs: HashMap, // references to the runtime class pub classes: HashMap, pub names: HashMap, pub class_objects: HashMap, - vm: Vm, } impl ClassManager { - pub fn new() -> Self { + pub fn new(classpath: Vec) -> Self { Self { static_class_data: HashMap::new(), current_id: 0, @@ -120,19 +42,23 @@ impl ClassManager { classes: HashMap::new(), class_objects: HashMap::new(), names: HashMap::new(), - classpath: vec![], - vm: Vm::new_internal(), + classpath, } } - fn set_classpath(&mut self, classpath: &str) { - self.classpath = classpath - .split(PATH_SEPARATOR) - .map(|s| s.into()) - .collect(); + pub fn get_static(&self, id: &ClassId, index: usize) -> Value { + self.static_class_data.get(id).unwrap()[index].clone() } - fn get_class_by_id(&mut self, id: &ClassId) -> Option<&Class> { + pub fn set_static(&mut self, id: ClassId, index: usize, value: Value) { + self.static_class_data.get_mut(&id).unwrap()[index] = value; + } + + pub fn get_classobject(&self, id: &ClassId) -> Option<&Value> { + self.class_objects.get(id) + } + + pub fn get_class_by_id(&mut self, id: &ClassId) -> Option<&Class> { if !self.classes.contains_key(id) { let name = self.classdef_name(id); if name.is_some() { @@ -142,20 +68,20 @@ impl ClassManager { self.classes.get(id) } - fn classdef_name(&self, id: &ClassId) -> Option { + pub fn classdef_name(&self, id: &ClassId) -> Option { self.classdefs.get(id).map(|c| c.name().to_owned()) //drops borrow to self here } - fn get_classid(&self, name: &str) -> &ClassId { + pub fn get_classid(&self, name: &str) -> &ClassId { self.names.get(name).unwrap() } - fn get_classdef(&self, id: &ClassId) -> &ClassDef { + pub(crate) fn get_classdef(&self, id: &ClassId) -> &ClassDef { self.classdefs.get(&id).unwrap() } /// loads the class if not already there - fn load_class_by_name(&mut self, name: &str) { + pub fn load_class_by_name(&mut self, name: &str) { debug!("load class {}", name); // determine no of dimensions and get type of array if any let mut chars = name.chars(); @@ -170,7 +96,7 @@ impl ClassManager { let mut type_name = name[num_dims..name.len()].to_owned(); if num_dims > 0 { - if !PRIMITIVES.contains(&type_name.as_str()){ + if !PRIMITIVES.contains(&type_name.as_str()) { type_name = type_name[1..type_name.len()].to_owned(); } let id = self.get_or_new_id(name); @@ -196,11 +122,21 @@ impl ClassManager { } } + pub(crate) fn get_method(&self, class_name: &str, method_name: &str) -> Option<&Method> { + let class_id = self.get_classid(class_name); + let classdef = self.get_classdef(class_id); + classdef.get_method(method_name) + } + /// get optional classid from cache - fn get_class_by_name(&self, name: &str) -> Option<&Class> { + pub fn get_mut_class_by_name(&mut self, name: &str) -> Option<&mut Class> { let id = self.names.get(name); - let t = self.classes.get(id.unwrap()); - t + self.classes.get_mut(id.unwrap()) + } + + pub fn get_class_by_name(&self, name: &str) -> Option<&Class> { + let id = self.names.get(name); + self.classes.get(id.unwrap()) } /// adds the class and calculates the 'offset' of it's fields (static and non-static) @@ -252,6 +188,7 @@ impl ClassManager { self.classes.insert(this_classid, Class { id: this_classid, + initialized: false, name: name.into(), superclass: superclass_id, parents, @@ -272,7 +209,7 @@ impl ClassManager { // run static init if this_classdef.methods.contains_key("()V") { - self.vm.execute_special(&mut vec![], name, "()V", vec![]).unwrap(); + Vm { stack: Vec::new()}.run2(self, this_classid,"()V"); } this_classid @@ -316,6 +253,7 @@ impl ClassManager { /// loads the class and returns it's dependencies fn load_class_and_deps(&mut self, name: &str) -> (ClassId, Vec) { + debug!("load {}", name); let id = self.get_or_new_id(name); let classdef = self.classdefs @@ -426,7 +364,7 @@ mod test { let mut fields_declared_by_java_lang_class = HashMap::new(); fields_declared_by_java_lang_class.insert("name".to_owned(), TypeIndex { type_name: "java/lang/String".into(), index: 0 }); class_field_mapping.insert("java/lang/Class".to_owned(), fields_declared_by_java_lang_class); - classes.insert(3, Class { id: 3, name: "".into(), superclass: None, parents: LinkedList::new(), interfaces: vec![], object_field_mapping: class_field_mapping, static_field_mapping: HashMap::new() }); + classes.insert(3, Class { id: 3, initialized: false, name: "".into(), superclass: None, parents: LinkedList::new(), interfaces: vec![], object_field_mapping: class_field_mapping, static_field_mapping: HashMap::new() }); let mut cm = ClassManager { static_class_data: HashMap::new(), @@ -436,7 +374,6 @@ mod test { current_id: 1, names, classpath: Vec::new(), - vm: Vm::new(&mut vec![]), }; let c_id = cm.add_class("C"); diff --git a/src/heap.rs b/src/heap.rs deleted file mode 100644 index e69de29..0000000 diff --git a/src/lib.rs b/src/lib.rs index 21e2c80..580a3c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,5 +1,5 @@ pub mod classloader; pub mod classmanager; -pub mod class; -pub mod vm; -pub mod heap; \ No newline at end of file +mod value; +mod class; +pub mod vm; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 6b310ea..7e5ab61 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,11 +1,7 @@ -use java_rs::classmanager::set_classpath; -use java_rs::vm::Vm; +use java_rs::vm::runtime::Vm; fn main() { - let mut stackframes = Vec::new(); - let mut vm = Vm::new(&mut stackframes); - set_classpath("/Users/Shautvast/dev/java/tests"); - let main_class = "testclasses.Main"; - vm.execute_static( &mut stackframes, main_class, "main([Ljava/lang/String;)V", vec![]) - .unwrap(); + let vm = Vm::new(); + vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Main", "main([Ljava/lang/String;)V"); } + diff --git a/src/value.rs b/src/value.rs new file mode 100644 index 0000000..d56f5a8 --- /dev/null +++ b/src/value.rs @@ -0,0 +1,39 @@ +use crate::vm::object::ObjectRef; + +#[derive(Debug, Clone)] +pub enum Value { + // variant returned for void methods + Void, + // 'pointer' to nothing + Null, + // primitives + I32(i32), + I64(i64), + F32(f32), + F64(f64), + BOOL(bool), + CHAR(char), + // objects and arrays + Ref(ObjectRef), + // special object + Utf8(String), +} + +impl Value { + // panics if not correct type + pub fn into_i32(self) -> i32 { + if let Value::I32(v) = self { + v + } else { + panic!("{:?} is not I32", self); + } + } + + pub fn into_object(self) -> ObjectRef { + if let Value::Ref(v) = self { + v + } else { + panic!(); + } + } +} \ No newline at end of file diff --git a/src/vm/array.rs b/src/vm/array.rs index ce59db7..1d55d66 100644 --- a/src/vm/array.rs +++ b/src/vm/array.rs @@ -1,7 +1,8 @@ use anyhow::{anyhow, Error}; +use crate::vm::object::ObjectRef::*; -use crate::class::ObjectRef::*; -use crate::class::Value::{self, *}; +use crate::value::Value; +use crate::value::Value::*; pub(crate) fn array_load(index: Value, arrayref: Value) -> Result { diff --git a/src/vm/mod.rs b/src/vm/mod.rs index af09934..63e6024 100644 --- a/src/vm/mod.rs +++ b/src/vm/mod.rs @@ -1,7 +1,4 @@ -mod vm; -pub use vm::Vm; -mod operations; -mod stack; mod array; -mod opcodes; -mod native; \ No newline at end of file +pub(crate) mod object; +pub(crate) mod opcodes; +pub mod runtime; \ No newline at end of file diff --git a/src/vm/native.rs b/src/vm/native.rs deleted file mode 100644 index 9512a17..0000000 --- a/src/vm/native.rs +++ /dev/null @@ -1,161 +0,0 @@ -#![allow(non_snake_case)] - -use std::cell::RefCell; -use std::rc::Rc; - -use anyhow::Error; -use log::debug; -use once_cell::sync::Lazy; - -use crate::class::{ObjectRef, Value}; -use crate::class::ObjectRef::Object; -use crate::class::Value::{I32, Void}; -use crate::{class, classmanager}; -use crate::vm::stack::StackFrame; -use crate::vm::Vm; - -pub fn invoke_native(vm: &mut Vm, stackframes: &mut Vec, class_name: &str, method_name: &str, _args: Vec) -> Result { - debug!("native {}.{}", class_name, method_name); - - match class_name { - "java/lang/Class" => java_lang_Class(vm, method_name), - "java/lang/System" => java_lang_System(vm, method_name), - "jdk/internal/misc/Unsafe" => jdk_internal_misc_Unsafe(vm, method_name), - "jdk/internal/util/SystemProps$Raw" => jdk_internal_util_SystemProps_Raw(vm, stackframes, method_name), - _ => unimplemented!("") - } -} - -fn java_lang_Class(_vm: &Vm, method_name: &str) -> Result { - Ok(match method_name { - "desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false), - _ => Void - }) -} - -fn java_lang_System(_vm: &Vm, method_name: &str) -> Result { - Ok(match method_name { - _ => Void - }) -} - -fn jdk_internal_misc_Unsafe(_vm: &Vm, method_name: &str) -> Result { - Ok(match method_name { - "arrayBaseOffset0(Ljava/lang/Class;)I" => I32(0), //TODO surely this is not right - "arrayIndexScale0(Ljava/lang/Class;)I" => I32(0), //TODO surely this is not right - _ => Void - }) -} - -fn jdk_internal_util_SystemProps_Raw(vm: &mut Vm, stackframes: &mut Vec, method_name: &str) -> Result { - match method_name { - "platformProperties()[Ljava/lang/String;" => platformProperties(), - "cmdProperties()Ljava/util/HashMap;" => cmdProps(vm, stackframes), //TODO ability to instantiate classes here - "vmProperties()[Ljava/lang/String;" => vmProperties(vm, stackframes), - _ => Ok(Void) - } -} - -fn cmdProps(vm: &mut Vm, stackframes: &mut Vec) -> Result { - classmanager::load_class_by_name("java/util/HashMap"); - let hashmap_class = classmanager::get_class_by_name("java/util/HashMap").unwrap(); - let hashmap = Value::Ref(Object(Rc::new(RefCell::new(class::Object::new(hashmap_class))))); // this is convoluted - vm.execute_special(stackframes, "java/util/HashMap", "()V", vec![hashmap.clone()])?; - Ok(hashmap) -} - -fn vmProperties(_vm: &mut Vm, _stackframes: &mut [StackFrame]) -> Result { - let props: Lazy> = Lazy::new(|| { - let vec: Vec = Vec::new(); - //TODO insert some values - vec - }); - Ok(Value::Ref(ObjectRef::StringArray(props.to_vec()))) -} - -fn platformProperties() -> Result { - let props: Lazy> = Lazy::new(|| { - let mut vec: Vec = Vec::new(); - //TODO set correct values - vec.push("display_country".into()); //null in jdk21 - vec.push("display_language".into()); //null in jdk21 - vec.push("display_script".into()); //null in jdk21 - vec.push("display_variant".into()); //null in jdk21 - vec.push("UTF-8".into()); - - { - #[cfg(target_family = "unix")] - vec.push("/".into()); - #[cfg(target_family = "windows")] - vec.push("\\"); - } - vec.push("format_country".into()); //null in jdk21 - vec.push("format_language".into()); //null in jdk21 - vec.push("format_script".into()); //null in jdk21 - vec.push("format_variant".into()); //null in jdk21 - vec.push("ftp_nonProxyHosts".into()); - if let Ok(ftp_proxy) = std::env::var("ftp_proxy") { - vec.push(ftp_proxy.to_owned());//TODO - vec.push(ftp_proxy); - } else { - vec.push("".to_owned()); - vec.push("".to_owned()); - } - - vec.push("http_nonProxyHosts".into()); - if let Ok(http_proxy) = std::env::var("http_proxy") { - vec.push(http_proxy.to_owned()); - vec.push(http_proxy);//TODO - } else { - vec.push("".to_owned()); - vec.push("".to_owned()); - } - if let Ok(https_proxy) = std::env::var("https_proxy") { - vec.push(https_proxy.to_owned()); - vec.push(https_proxy); - } else { - vec.push("".to_owned()); - vec.push("".to_owned()); - } - vec.push(std::env::temp_dir().display().to_string()); - - { - #[cfg(target_family = "unix")] - vec.push("\n".into()); - #[cfg(target_family = "windows")] - vec.push("\r\n"); - } - vec.push(whoami::platform().to_string()); - vec.push(whoami::devicename()); - vec.push("os_version".into()); - { - #[cfg(target_family = "unix")] - vec.push(":".into()); - #[cfg(target_family = "windows")] - vec.push(";".into()); - } - vec.push("socksNonProxyHosts".into()); - vec.push("socksProxyHost".into()); - vec.push("socksProxyPort".into()); - vec.push("UTF-8".into()); - vec.push("UTF-8".into()); - vec.push("sun_arch_abi".into()); - vec.push("sun_arch_data_model".into()); - vec.push("sun_cpu_endian".into()); //null in jdk21 - vec.push("sun_cpu_isalist".into()); //null in jdk21 - vec.push("sun_io_unicode_encoding".into()); //null in jdk21 - vec.push("sun_jnu_encoding".into()); //null in jdk21 - vec.push("sun_os_patch_level".into()); //null in jdk21 - if let Ok(curdir) = std::env::current_dir() { - vec.push(curdir.display().to_string()); - } - - let home = std::env::home_dir().unwrap(); - vec.push(home.display().to_string()); - vec.push(whoami::username()); - vec.push("FIXED_LENGTH".into()); - - vec - }); - Ok(Value::Ref(ObjectRef::StringArray(props.to_vec()))) -} \ No newline at end of file diff --git a/src/vm/object.rs b/src/vm/object.rs new file mode 100644 index 0000000..eb3b801 --- /dev/null +++ b/src/vm/object.rs @@ -0,0 +1,160 @@ +use std::cell::RefCell; +use std::rc::Rc; +use log::debug; +use rand::random; +use crate::class::{Class, ClassId}; +use crate::vm::object::ObjectRef::*; +use crate::value::Value; + +#[derive(Debug, Clone)] +pub enum ObjectRef { + ByteArray(Vec), + ShortArray(Vec), + IntArray(Vec), + LongArray(Vec), + FloatArray(Vec), + DoubleArray(Vec), + BooleanArray(Vec), + CharArray(Vec), + StringArray(Vec), + ObjectArray(ClassId, Vec), + Object(Rc>), + Class(Class), +} + +impl ObjectRef { + pub fn get_array_length(&self) -> usize { + match self { + ByteArray(d) => d.len(), + ShortArray(d) => d.len(), + IntArray(d) => d.len(), + LongArray(d) => d.len(), + FloatArray(d) => d.len(), + DoubleArray(d) => d.len(), + BooleanArray(d) => d.len(), + CharArray(d) => d.len(), + StringArray(d) => d.len(), + ObjectArray(_, d) => d.len(), + _ => unreachable!("not an array {:?}", self) + } + } +} + +pub enum ArrayType { + BOOLEAN = 4, + CHAR = 5, + FLOAT = 6, + DOUBLE = 7, + BYTE = 8, + SHORT = 9, + INT = 10, + LONG = 11, +} + +impl ObjectRef { + pub fn new_object_array(class: &Class, size: usize) -> Self { + ObjectArray(class.id, Vec::with_capacity(size)) + } + + pub fn new_array(arraytype: u8, size: usize) -> Self { + match arraytype { + 8 => ByteArray(Vec::with_capacity(size)), + 9 => ShortArray(Vec::with_capacity(size)), + 10 => IntArray(Vec::with_capacity(size)), + 11 => LongArray(Vec::with_capacity(size)), + 6 => FloatArray(Vec::with_capacity(size)), + 7 => DoubleArray(Vec::with_capacity(size)), + 4 => BooleanArray(Vec::with_capacity(size)), + 5 => CharArray(Vec::with_capacity(size)), + _ => unreachable!("impossible array type") + } + } + + pub fn new_int_array(size: usize) -> Self { + IntArray(Vec::with_capacity(size)) + } + + pub fn new_byte_array(d: Vec) -> Self { + ByteArray(into_vec_i8(d)) + } +} + +fn into_vec_i8(v: Vec) -> Vec { + let mut v = std::mem::ManuallyDrop::new(v); + + // then, pick apart the existing Vec + let p = v.as_mut_ptr(); + let len = v.len(); + let cap = v.capacity(); + + // finally, adopt the data into a new Vec + unsafe { Vec::from_raw_parts(p as *mut i8, len, cap) } +} + +#[derive(Debug)] +pub struct Object { + /// unique id for instance + pub id: u32, + /// loose ref to class + pub class_id: ClassId, + /// instance field data + pub data: Vec, +} + +// object, not array +impl Object { + pub fn new(class: &Class) -> Self { + let instance_data = Object::init_fields(class); + Self { + id: random(), + class_id: class.id, + data: instance_data, + } + } + + // initializes all non-static fields to their default values + pub(crate) fn init_fields(class: &Class) -> Vec { + let mut field_data = vec![Value::Null; class.n_object_fields()]; + + for (_, fields) in &class.object_field_mapping { + for (_, type_index) in fields { + let value = match type_index.type_name.as_str() { + "Z" => Value::BOOL(false), + "B" => Value::I32(0), + "S" => Value::I32(0), + "I" => Value::I32(0), + "J" => Value::I64(0), + "F" => Value::F32(0.0), + "D" => Value::F64(0.0), + _ => Value::Null, + }; + field_data[type_index.index] = value.into(); + } + } + + field_data + } + + pub fn set(&mut self, runtime_type: &Class, declared_type: &str, field_name: &str, value: Value) { + debug!("set {:?}.{}", runtime_type.name, field_name); + let type_index = runtime_type + .object_field_mapping + .get(declared_type) + .unwrap() + .get(field_name) + .unwrap(); + self.data[type_index.index] = value; + } + + pub fn get(&self, runtime_type: &Class, declared_type: &String, field_name: &String) -> &Value { + let type_index = runtime_type + .object_field_mapping + .get(declared_type) + .unwrap() + .get(field_name) + .unwrap(); + debug!("get {:?}:{}.{}:{} @{}", runtime_type, declared_type, field_name, type_index.type_name, type_index.index); + debug!("from data {:?}", self.data); + self.data.get(type_index.index).unwrap() + } +} \ No newline at end of file diff --git a/src/vm/opcodes.rs b/src/vm/opcodes.rs index bfd4df2..9267638 100644 --- a/src/vm/opcodes.rs +++ b/src/vm/opcodes.rs @@ -1,463 +1,218 @@ -//comment line should be above declaration +use crate::classloader::io::{Lookupswitch, Tableswitch}; -use once_cell::sync::Lazy; +#[allow(non_camel_case_types)] +#[derive(Clone, Debug)] +pub(crate) enum Opcode { + NOP, + ACONST_NULL, + ICONST_M1, + ICONST_0, + ICONST_1, + ICONST_2, + ICONST_3, + ICONST_4, + ICONST_5, + LCONST_0, + LCONST_1, + FCONST_0, + FCONST_1, + FCONST_2, + DCONST_0, + DCONST_1, + BIPUSH(u8), + SIPUSH(u16), + LDC(u16), + LDC_W(u16), + LDC2_W(u16), + ILOAD(u8), + WIDE_ILOAD(u16), + LLOAD(u8), + WIDE_LLOAD(u16), + FLOAD(u8), + WIDE_FLOAD(u16), + DLOAD(u8), + WIDE_DLOAD(u16), + ALOAD(u8), + WIDE_ALOAD(u16), + ILOAD_0, + ILOAD_1, + ILOAD_2, + ILOAD_3, + LLOAD_0, + LLOAD_1, + LLOAD_2, + LLOAD_3, + FLOAD_0, + FLOAD_1, + FLOAD_2, + FLOAD_3, + DLOAD_0, + DLOAD_1, + DLOAD_2, + DLOAD_3, + ALOAD_0, + ALOAD_1, + ALOAD_2, + ALOAD_3, + IALOAD, + LALOAD, + FALOAD, + DALOAD, + AALOAD, + BALOAD, + CALOAD, + SALOAD, + ISTORE(u8), + WIDE_ISTORE(u16), + LSTORE(u8), + WIDE_LSTORE(u16), + FSTORE(u8), + WIDE_FSTORE(u16), + DSTORE(u8), + WIDE_DSTORE(u16), + ASTORE(u8), + WIDE_ASTORE(u16), + ISTORE_0, + ISTORE_1, + ISTORE_2, + ISTORE_3, + LSTORE_0, + LSTORE_1, + LSTORE_2, + LSTORE_3, + FSTORE_0, + FSTORE_1, + FSTORE_2, + FSTORE_3, + DSTORE_0, + DSTORE_1, + DSTORE_2, + DSTORE_3, + ASTORE_0, + ASTORE_1, + ASTORE_2, + ASTORE_3, + IASTORE, + LASTORE, + FASTORE, + DASTORE, + AASTORE, + BASTORE, + CASTORE, + SASTORE, + POP, + DUP, + DUP_X1, + DUP_X2, + DUP2, + DUP2_X1, + DUP2_X2, + IADD, + LADD, + FADD, + DADD, + ISUB, + LSUB, + FSUB, + DSUB, + IMUL, + LMUL, + FMUL, + DMUL, + IDIV, + LDIV, + FDIV, + DDIV, + IREM, + LREM, + FREM, + DREM, + INEG, + LNEG, + FNEG, + DNEG, + ISHL, + LSHL, + ISHR, + LSHR, + IAND, + LAND, + IOR, + LOR, + IXOR, + LXOR, + IINC(u8,u8), + I2L, + I2F, + I2D, + L2I, + L2F, + L2D, + WIDE_IINC(u16,u16), + F2I, + F2L, + F2D, + D2I, + D2L, + D2F, + I2B, + I2C, + I2S, + LCMP, + FCMPL, + FCMPG, + DCMPL, + DCMPG, + IFEQ(u16), + IFNE(u16), + IFLT(u16), + IFGE(u16), + IFGT(u16), + IFLE(u16), + IF_ICMPEQ(u16), + IF_ICMPNE(u16), + IF_ICMPLT(u16), + IF_ICMPGE(u16), + IF_ICMPGT(u16), + IF_ICMPLE(u16), + IF_ACMPEQ(u16), + IF_ACMPNE(u16), + GOTO(u16), + JSR(u16), + RET(u8), + WIDE_RET(u16), + TABLESWITCH(Tableswitch), + LOOKUPSWITCH(Lookupswitch), + IRETURN, + FRETURN, + DRETURN, + ARETURN, + RETURN_VOID, + GETSTATIC(u16), + PUTSTATIC(u16), + GETFIELD(u16), + PUTFIELD(u16), + INVOKEVIRTUAL(u16), + INVOKESPECIAL(u16), + INVOKESTATIC(u16), + INVOKEINTERFACE(u16, u8), + INVOKEDYNAMIC(u16), + NEW(u16), + NEWARRAY(u8), + ANEWARRAY(u16), + ARRAYLENGTH, + ATHROW, + CHECKCAST(u16), + INSTANCEOF(u16), + MONITORENTER, + MONITOREXIT, + WIDE(Box), + MULTIANEWARRAY(u16, u8), + IFNULL(u16), + IFNONNULL(u16), + GOTOW(i32), + JSR_W(i32), -pub const NOP: u8 = 0; -// (0x0) Do nothing -pub const ACONST_NULL: u8 = 1; -// (0x01) Push null -pub const ICONST_M1: u8 = 2; -// (0x2) Push int constant -1 -pub const ICONST_0: u8 = 3; -// (0x3) Push int constant 0 -pub const ICONST_1: u8 = 4; -// (0x4) Push int constant 1 -pub const ICONST_2: u8 = 5; -// (0x5) Push int constant 2 -pub const ICONST_3: u8 = 6; -// (0x6) Push int constant 3 -pub const ICONST_4: u8 = 7; -// (0x7) Push int constant 4 -pub const ICONST_5: u8 = 8; -// (0x8) Push int constant 5 -pub const LCONST_0: u8 = 9; -// (0x9) Push long constant 0 -pub const LCONST_1: u8 = 10; -// (0xa) Push long constant 1 -pub const FCONST_0: u8 = 11; -// (0xb) Push float 0 -pub const FCONST_1: u8 = 12; -// (0xc) Push float 1 -pub const FCONST_2: u8 = 13; -// (0xd) Push float 2 -pub const DCONST_0: u8 = 14; -// (0xe) push double 0 -pub const DCONST_1: u8 = 15; -// (0xe) push double 1 -pub const BIPUSH: u8 = 16; -// (0x10) Push byte -pub const SIPUSH: u8 = 17; -// (0x11) Push short -pub const LDC: u8 = 18; -// (0x12) Push item from run-time pub constant pool -pub const LDC_W: u8 = 19; -// (0x13) Push item from run-time constant pool (wide index) -pub const LDC2_W: u8 = 20; -// (0x14) Push long or double from run-time constant pool (wide index) -pub const ILOAD: u8 = 21; -// (0x15) Load int from local variable -pub const LLOAD: u8 = 22; -// (0x16) Load long from local variable -pub const FLOAD: u8 = 23; -// (0x16) Load float from local variable -pub const DLOAD: u8 = 24; -// (0x18) load double from local variable -pub const ALOAD: u8 = 25; -// 0x19 Load reference from local variable -pub const ILOAD_0: u8 = 26; -// (0x1a) Load int from local variable 0 -pub const ILOAD_1: u8 = 27; -// (0x1b) Load int from local variable 1 -pub const ILOAD_2: u8 = 28; -// (0x1c) Load int from local variable 2 -pub const ILOAD_3: u8 = 29; -// (0x1d) Load int from local variable 3 -pub const LLOAD_0: u8 = 30; -// (0x1e) Load long from local variable 0 -pub const LLOAD_1: u8 = 31; -// (0x1f) Load long from local variable 1 -pub const LLOAD_2: u8 = 32; -// (0x20) Load long from local variable 2 -pub const LLOAD_3: u8 = 33; -// (0x21) Load long from local variable 3 -pub const FLOAD_0: u8 = 34; -// (0x22) Load float from local variable 0 -pub const FLOAD_1: u8 = 35; -// (0x23) Load float from local variable 1 -pub const FLOAD_2: u8 = 36; -// (0x24) Load float from local variable 2 -pub const FLOAD_3: u8 = 37; -// (0x25) Load float from local variable 3 -pub const DLOAD_0: u8 = 38; -// (0x26) Load double from local variable 0 -pub const DLOAD_1: u8 = 39; -// (0x27) Load double from local variable 1 -pub const DLOAD_2: u8 = 40; -// (0x28) Load double from local variable 2 -pub const DLOAD_3: u8 = 41; -// (0x29) Load double from local variable 3 -pub const ALOAD_0: u8 = 42; -// (0x2a) Load reference from local variable 0 -pub const ALOAD_1: u8 = 43; -// (0x2b) Load reference from local variable 1 -pub const ALOAD_2: u8 = 44; -// (0x2c) Load reference from local variable 2 -pub const ALOAD_3: u8 = 45; -// (0x2d) Load reference from local variable 3 -pub const IALOAD: u8 = 46; -// (0x2e) Load int from array -pub const LALOAD: u8 = 47; -// (0x2f) Load long from array -pub const FALOAD: u8 = 48; -// (0x30) Load float from array -pub const DALOAD: u8 = 49; -// (0x31) Load double from array -pub const AALOAD: u8 = 50; -// (0x3d) Load reference from array -pub const BALOAD: u8 = 51; -// (0x33) Load byte or boolean from array -pub const CALOAD: u8 = 52; -// (0x34) Load char from array -pub const SALOAD: u8 = 53; -// (0x34) Load short from array -pub const ISTORE: u8 = 54; -// (0x36) Store int into local variable -pub const LSTORE: u8 = 55; -// (0x37) Store long into local variable -pub const FSTORE: u8 = 56; -// (0x38) Store float into local variable -pub const DSTORE: u8 = 57; -// (0x39) store double in local variable -pub const ASTORE: u8 = 58; -// (0x3a) -pub const ISTORE_0: u8 = 59; -// (0x3b) Store int into local variable 0 -pub const ISTORE_1: u8 = 60; -// (0x3c) Store int into local variable 1 -pub const ISTORE_2: u8 = 61; -// (0x3d) Store int into local variable 2 -pub const ISTORE_3: u8 = 62; -// (0x3e) Store int into local variable 3 -pub const LSTORE_0: u8 = 63; -// (0x3f) Store long into local variable 0 -pub const LSTORE_1: u8 = 64; -// (0x40) Store long into local variable 1 -pub const LSTORE_2: u8 = 65; -// (0x41) Store long into local variable 2 -pub const LSTORE_3: u8 = 66; -// (0x42) Store long into local variable 3 -pub const FSTORE_0: u8 = 67; -// (0x43) Store float into local variable 0 -pub const FSTORE_1: u8 = 68; -// (0x44) Store float into local variable 1 -pub const FSTORE_2: u8 = 69; -// (0x45) Store float into local variable 2 -pub const FSTORE_3: u8 = 70; -// (0x46) Store float into local variable 3 -pub const DSTORE_0: u8 = 71; -// (0x47) store double in local variable 0 -pub const DSTORE_1: u8 = 72; -// (0x48) store double in local variable 1 -pub const DSTORE_2: u8 = 73; -// (0x49) store double in local variable 2 -pub const DSTORE_3: u8 = 74; -// (0x4a) store double in local variable 3 -pub const ASTORE_0: u8 = 75; -// (0x4b) -pub const ASTORE_1: u8 = 76; -// (0x4c) -pub const ASTORE_2: u8 = 77; -// (0x4d) -pub const ASTORE_3: u8 = 78; -// (0x4e) -pub const IASTORE: u8 = 79; -// (0x4f) Store into int array -pub const LASTORE: u8 = 80; -// (0x50) Store into long array -pub const FASTORE: u8 = 81; -// (0x51) Store into float array -pub const DASTORE: u8 = 82; -// (0x52) store into double array -pub const AASTORE: u8 = 83; -// (0x53) Store into object array -pub const BASTORE: u8 = 84; -// (0x54) Store into byte or boolean array -pub const CASTORE: u8 = 85; -// (0x55) Store into char array -pub const SASTORE: u8 = 86; -// (0x56) Store into short array -pub const POP: u8 = 87; -// (0x57) Pop the top operand stack value -pub const DUP: u8 = 89; -// (0x59) duplicate the top operand stack value -pub const _DUP_X1: u8 = 90; -// (0x5a) Duplicate the top operand stack value and insert two values down -pub const _DUP_X2: u8 = 91; -// (0x5b) Duplicate the top operand stack value and insert two or three values down -pub const _DUP2: u8 = 92; -// (0x5c) Duplicate the top one or two operand stack values -pub const _DUP2_X1: u8 = 93; -//(0x5d) Duplicate the top one or two operand stack values and insert two or three values down -pub const _DUP2_X2: u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down +} -pub const IADD: u8 = 96; -pub const _FADD: u8 = 98; -// (0x62) Add float -pub const _DADD: u8 = 99; // (0x63) add double -pub const ISUB: u8 = 100; -pub const _DSUB: u8 = 103; -// (0x67) subtract double -pub const _FMUL: u8 = 106; -// (0x6a) Multiply float -pub const _DMUL: u8 = 107; -// (0x6b) Multiply double -pub const IDIV: u8 = 108; -pub const _FDIV: u8 = 110; -// (0x6e) Divide float -pub const _DDIV: u8 = 111; -// (0x6f) divide double -pub const _FREM: u8 = 114; -// (0x72) Remainder float -pub const _DREM: u8 = 115; -// (0x73) remainder double -pub const _FNEG: u8 = 118; -// (0x76) Negate float -pub const _DNEG: u8 = 119; // (0x77) Negate double -pub const ISHL:u8 = 120; -pub const ISHR: u8 = 122; -pub const _F2I: u8 = 139; -// (0x8b) Convert float to int -pub const _F2L: u8 = 140; -// (0x8c) Convert float to long -pub const _F2D: u8 = 141; -// (0x8d) Convert float to double -pub const _D2I: u8 = 142; -// (0x8e) double to int -pub const _D2L: u8 = 143; -// (0x8f) double to long -pub const _D2F: u8 = 144; // (0x90) double to float - -pub const _FCMPL: u8 = 149; -// (0x95) Compare float (less than) -pub const _FCMPG: u8 = 150; -// (0x96) Compare float (greater than) -pub const _DCMPL: u8 = 151; -// (0x97) compare double (less than) -pub const _DCMPG: u8 = 152; // (0x98) compare double (greater than) - -pub const IFEQ: u8 = 153; -// (0x99) -pub const IFNE: u8 = 154; -// (0x9a) -pub const IFLT: u8 = 155; -// (0x9b) -pub const IFGE: u8 = 156; -// (0x9c) -pub const IFGT: u8 = 157; -// (0x9d) -pub const IFLE: u8 = 158; // (0x9e) - -pub const IF_ICMPEQ: u8 = 159; -// (0x9f) Branch if int comparison succeeds -pub const IF_ICMPNE: u8 = 160; -// (0x9f) Branch if int comparison succeeds -pub const IF_ICMPLT: u8 = 161; -// (0x9f) Branch if int comparison succeeds -pub const IF_ICMPGE: u8 = 162; -// (0x9f) Branch if int comparison succeeds -pub const IF_ICMPGT: u8 = 163; -// (0x9f) Branch if int comparison succeeds -pub const IF_ICMPLE: u8 = 164; -// (0x9f) Branch if int comparison succeeds -pub const GOTO: u8 = 167; // (0xa7) Branch always - -pub const IRETURN: u8 = 172; -// (0xac) ireturn -pub const FRETURN: u8 = 174; -// (0xae) Return float from method -pub const DRETURN: u8 = 175; -// (0xaf) Return double from method -pub const ARETURN: u8 = 176; -//(0xb0) return reference -pub const RETURN_VOID: u8 = 177; -// (0xb1) Return void from method (actually 'return' but that's a keyword) -pub const GETSTATIC: u8 = 178; -// (0xb2) Get static field from class -pub const PUTSTATIC: u8 = 179; -// (0xb3) Set static field in class -pub const GETFIELD: u8 = 180; -// (0xb4) Fetch field from object3 -pub const PUTFIELD: u8 = 181; -// (0xb5) Set field in object -pub const INVOKEVIRTUAL: u8 = 182; -// (0xb6) Invoke instance method; dispatch based on class -pub const INVOKESPECIAL: u8 = 183; -// (0xb7) // nvoke instance method; direct invocation of instance initialization methods and methods of the current class and its supertypes -pub const INVOKESTATIC: u8 = 184; -// (0xb8) Invoke a class (static) method -pub const NEW: u8 = 187; -// (0xbb) Create new object -pub const NEWARRAY: u8 = 188; -pub const ANEWARRAY: u8 = 189; -// (0xbd) -pub const ARRAYLENGTH: u8 = 190; -// (0xbe) -pub const _ATHROW: u8 = 191; -// (0xbf) -pub const _CHECKCAST: u8 = 192; -// (0xc0) -pub const MONITORENTER: u8 = 194; -pub const MONITOREXIT: u8 = 195; -pub const IFNULL: u8 = 198; -pub const IFNONNULL: u8 = 199; - -pub static OPCODES: Lazy> = Lazy::new(|| { - let mut opcodes = vec![""; 256]; - opcodes[NOP as usize] = "NOP"; - opcodes[ACONST_NULL as usize] = "ACONST_NULL"; - opcodes[ICONST_M1 as usize] = "ICONST_M1"; - opcodes[ICONST_0 as usize] = "ICONST_0"; - opcodes[ICONST_1 as usize] = "ICONST_1"; - opcodes[ICONST_2 as usize] = "ICONST_2"; - opcodes[ICONST_3 as usize] = "ICONST_3"; - opcodes[ICONST_4 as usize] = "ICONST_4"; - opcodes[ICONST_5 as usize] = "ICONST_5"; - opcodes[LCONST_0 as usize] = "LCONST_0"; - opcodes[LCONST_1 as usize] = "LCONST_1"; - opcodes[FCONST_0 as usize] = "FCONST_0"; - opcodes[FCONST_1 as usize] = "FCONST_1"; - opcodes[FCONST_2 as usize] = "FCONST_2"; - opcodes[DCONST_0 as usize] = "DCONST_0"; - opcodes[DCONST_1 as usize] = "DCONST_1"; - opcodes[BIPUSH as usize] = "BIPUSH"; - opcodes[SIPUSH as usize] = "SIPUSH"; - opcodes[LDC as usize] = "LDC"; - opcodes[LDC_W as usize] = "LDC_W"; - opcodes[LDC2_W as usize] = "LDC2_W"; - opcodes[ILOAD as usize] = "ILOAD"; - opcodes[LLOAD as usize] = "LLOAD"; - opcodes[FLOAD as usize] = "FLOAD"; - opcodes[DLOAD as usize] = "DLOAD"; - opcodes[ALOAD as usize] = "ALOAD"; - opcodes[ILOAD_0 as usize] = "ILOAD_0"; - opcodes[ILOAD_1 as usize] = "ILOAD_1"; - opcodes[ILOAD_2 as usize] = "ILOAD_2"; - opcodes[ILOAD_3 as usize] = "ILOAD_3"; - opcodes[LLOAD_0 as usize] = "LLOAD_0"; - opcodes[LLOAD_1 as usize] = "LLOAD_1"; - opcodes[LLOAD_2 as usize] = "LLOAD_2"; - opcodes[LLOAD_3 as usize] = "LLOAD_3"; - opcodes[FLOAD_0 as usize] = "FLOAD_0"; - opcodes[FLOAD_1 as usize] = "FLOAD_1"; - opcodes[FLOAD_2 as usize] = "FLOAD_2"; - opcodes[FLOAD_3 as usize] = "FLOAD_3"; - opcodes[DLOAD_0 as usize] = "DLOAD_0"; - opcodes[DLOAD_1 as usize] = "DLOAD_1"; - opcodes[DLOAD_2 as usize] = "DLOAD_2"; - opcodes[DLOAD_3 as usize] = "DLOAD_3"; - opcodes[ALOAD_0 as usize] = "ALOAD_0"; - opcodes[ALOAD_1 as usize] = "ALOAD_1"; - opcodes[ALOAD_2 as usize] = "ALOAD_2"; - opcodes[ALOAD_3 as usize] = "ALOAD_3"; - opcodes[IALOAD as usize] = "IALOAD"; - opcodes[LALOAD as usize] = "LALOAD"; - opcodes[FALOAD as usize] = "FALOAD"; - opcodes[DALOAD as usize] = "DALOAD"; - opcodes[AALOAD as usize] = "AALOAD"; - opcodes[BALOAD as usize] = "BALOAD"; - opcodes[CALOAD as usize] = "CALOAD"; - opcodes[SALOAD as usize] = "SALOAD"; - opcodes[ISTORE as usize] = "ISTORE"; - opcodes[LSTORE as usize] = "LSTORE"; - opcodes[FSTORE as usize] = "FSTORE"; - opcodes[DSTORE as usize] = "DSTORE"; - opcodes[ASTORE as usize] = "ASTORE"; - opcodes[ISTORE_0 as usize] = "ISTORE_0"; - opcodes[ISTORE_1 as usize] = "ISTORE_1"; - opcodes[ISTORE_2 as usize] = "ISTORE_2"; - opcodes[ISTORE_3 as usize] = "ISTORE_3"; - opcodes[LSTORE_0 as usize] = "LSTORE_0"; - opcodes[LSTORE_1 as usize] = "LSTORE_1"; - opcodes[LSTORE_2 as usize] = "LSTORE_2"; - opcodes[LSTORE_3 as usize] = "LSTORE_3"; - opcodes[FSTORE_0 as usize] = "FSTORE_0"; - opcodes[FSTORE_1 as usize] = "FSTORE_1"; - opcodes[FSTORE_2 as usize] = "FSTORE_2"; - opcodes[FSTORE_3 as usize] = "FSTORE_3"; - opcodes[DSTORE_0 as usize] = "DSTORE_0"; - opcodes[DSTORE_1 as usize] = "DSTORE_1"; - opcodes[DSTORE_2 as usize] = "DSTORE_2"; - opcodes[DSTORE_3 as usize] = "DSTORE_3"; - opcodes[ASTORE_0 as usize] = "ASTORE_0"; - opcodes[ASTORE_1 as usize] = "ASTORE_1"; - opcodes[ASTORE_2 as usize] = "ASTORE_2"; - opcodes[ASTORE_3 as usize] = "ASTORE_3"; - opcodes[IASTORE as usize] = "IASTORE"; - opcodes[LASTORE as usize] = "LASTORE"; - opcodes[FASTORE as usize] = "FASTORE"; - opcodes[DASTORE as usize] = "DASTORE"; - opcodes[AASTORE as usize] = "AASTORE"; - opcodes[BASTORE as usize] = "BASTORE"; - opcodes[CASTORE as usize] = "CASTORE"; - opcodes[SASTORE as usize] = "SASTORE"; - opcodes[POP as usize] = "POP"; - opcodes[DUP as usize] = "DUP"; - opcodes[_DUP_X1 as usize] = "_DUP_X1"; - opcodes[_DUP_X2 as usize] = "_DUP_X2"; - opcodes[_DUP2 as usize] = "_DUP2"; - opcodes[_DUP2_X1 as usize] = "_DUP2_X1"; - opcodes[_DUP2_X2 as usize] = "_DUP2_X2"; - opcodes[IADD as usize] = "IADD"; - opcodes[_FADD as usize] = "_FADD"; - opcodes[_DADD as usize] = "_DADD"; - opcodes[ISUB as usize] = "ISUB"; - opcodes[_DSUB as usize] = "_DSUB"; - opcodes[_FMUL as usize] = "_FMUL"; - opcodes[_DMUL as usize] = "_DMUL"; - opcodes[IDIV as usize] = "IDIV"; - opcodes[_FDIV as usize] = "_FDIV"; - opcodes[_DDIV as usize] = "_DDIV"; - opcodes[_FREM as usize] = "_FREM"; - opcodes[_DREM as usize] = "_DREM"; - opcodes[_FNEG as usize] = "_FNEG"; - opcodes[_DNEG as usize] = "_DNEG"; - opcodes[ISHL as usize] = "ISHL"; - opcodes[ISHR as usize] = "ISHR"; - opcodes[_F2I as usize] = "_F2I"; - opcodes[_F2L as usize] = "_F2L"; - opcodes[_F2D as usize] = "_F2D"; - opcodes[_D2I as usize] = "_D2I"; - opcodes[_D2L as usize] = "_D2L"; - opcodes[_D2F as usize] = "_D2F"; - opcodes[_FCMPL as usize] = "_FCMPL"; - opcodes[_FCMPG as usize] = "_FCMPG"; - opcodes[_DCMPL as usize] = "_DCMPL"; - opcodes[_DCMPG as usize] = "_DCMPG"; - opcodes[IFEQ as usize] = "IFEQ"; - opcodes[IFNE as usize] = "IFNE"; - opcodes[IFLT as usize] = "IFLT"; - opcodes[IFGE as usize] = "IFGE"; - opcodes[IFGT as usize] = "IFGT"; - opcodes[IFLE as usize] = "IFLE"; - opcodes[IF_ICMPEQ as usize] = "IF_ICMPEQ"; - opcodes[IF_ICMPNE as usize] = "IF_ICMPNE"; - opcodes[IF_ICMPLT as usize] = "IF_ICMPLT"; - opcodes[IF_ICMPGE as usize] = "IF_ICMPGE"; - opcodes[IF_ICMPGT as usize] = "IF_ICMPGT"; - opcodes[IF_ICMPLE as usize] = "IF_ICMPLE"; - opcodes[GOTO as usize] = "GOTO"; - opcodes[IRETURN as usize] = "IRETURN"; - opcodes[FRETURN as usize] = "FRETURN"; - opcodes[DRETURN as usize] = "DRETURN"; - opcodes[ARETURN as usize] = "ARETURN"; - opcodes[RETURN_VOID as usize] = "RETURN_VOID"; - opcodes[GETSTATIC as usize] = "GETSTATIC"; - opcodes[PUTSTATIC as usize] = "PUTSTATIC"; - opcodes[GETFIELD as usize] = "GETFIELD"; - opcodes[PUTFIELD as usize] = "PUTFIELD"; - opcodes[INVOKEVIRTUAL as usize] = "INVOKEVIRTUAL"; - opcodes[INVOKESPECIAL as usize] = "INVOKESPECIAL"; - opcodes[INVOKESTATIC as usize] = "INVOKESTATIC"; - opcodes[NEW as usize] = "NEW"; - opcodes[NEWARRAY as usize] = "NEWARRAY"; - opcodes[ANEWARRAY as usize] = "ANEWARRAY"; - opcodes[ARRAYLENGTH as usize] = "ARRAYLENGTH"; - opcodes[_ATHROW as usize] = "_ATHROW"; - opcodes[_CHECKCAST as usize] = "_CHECKCAST"; - opcodes[MONITORENTER as usize] = "MONITORENTER"; - opcodes[MONITOREXIT as usize] = "MONITOREXIT"; - opcodes[IFNULL as usize] = "IFNULL"; - opcodes[IFNONNULL as usize] = "IFNONNULL"; - opcodes -}); \ No newline at end of file diff --git a/src/vm/operations.rs b/src/vm/operations.rs deleted file mode 100644 index 0bf8e89..0000000 --- a/src/vm/operations.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::cell::RefCell; -use std::collections::HashMap; -use std::rc::Rc; - -use anyhow::Error; -use log::debug; - -use crate::{class, classmanager}; -use crate::class::{Class, ClassId, ObjectRef, Value}; -use crate::class::ObjectRef::Object; -use crate::class::Value::{I32, Ref}; -use crate::classloader::classdef::{CpEntry, Method}; -use crate::vm::stack::StackFrame; -use crate::vm::vm::{current_frame, Invocation, MethodSignature}; - -/// the place for opcode implementations that are a bit long - -// GET_STATIC opcode -pub(crate) fn get_static(this_class: ClassId, field_index: u16) -> Result { - let classdef = classmanager::get_classdef(this_class); - let (class_index, field_name_and_type_index) = - classdef.cp_field_ref(&field_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 field_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); - classmanager::load_class_by_name(that_class_name); - let that_class = classmanager::get_class_by_name(that_class_name).unwrap(); - - let type_index = that_class - .static_field_mapping - .get(that_class_name) - .unwrap()// safe because class for static field must be there - .get(field_name) - .unwrap(); // safe because field must be there - - debug!("get_static {}.{}", that_class_name, field_name); - Ok(classmanager::get_static(&that_class.id, type_index.index)) -} - -pub(crate) fn get_name_and_type(cp: &HashMap, index: u16) -> Option { - if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() { - if let CpEntry::Utf8(method_name) = cp.get(method_name_index).unwrap() { - if let CpEntry::Utf8(signature) = cp.get(signature_index).unwrap() { - let mut method_signature: String = method_name.into(); - let num_args = get_num_args(signature); - method_signature.push_str(signature); - return Some(MethodSignature::new(method_signature, num_args)); - } - } - } - None -} - -pub(crate) fn get_signature_for_invoke(cp: &HashMap, index: u16) -> Option { - if let CpEntry::MethodRef(class_index, name_and_type_index) - | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap() - { - if let Some(method_signature) = get_name_and_type(cp, *name_and_type_index) { - if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() { - if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() { - return Some(Invocation::new( - class_name.into(), - method_signature) - ); - } - } - } - } - None -} - -/// LDC in all varieties (LDC, LDC_W, LDC2_W) -pub(crate) fn load_constant(cp_index: &u16, method: &Method, stackframes: &mut Vec, this_class: ClassId){ - 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).cp_utf8(utf8); - let string: Vec = 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 = class::Object::new(stringclass); - stringinstance.set(stringclass, "java/lang/String", "value", Ref(ObjectRef::new_byte_array(string))); - - debug!("new string \"{}\"", utf8); - - current_frame(stackframes).push(Ref(Object(Rc::new(RefCell::new(stringinstance))))); - } - CpEntry::Long(l) => { - current_frame(stackframes).push(Value::I64(*l)); - } - CpEntry::ClassRef(utf8_index) => { - let classdef = classmanager::get_classdef(this_class); - let class_name = classdef.cp_utf8(utf8_index); - 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) - } - } -} - - -fn get_num_args(signature: &str) -> usize { - let mut num = 0; - let mut i = 1; - let chars: Vec = signature.chars().collect(); - - while i < chars.len() { - if chars[i] == 'L' { - i += 1; - while chars[i] != ';' { - i += 1; - } - i += 1; - num += 1; - } else if chars[i] == ')' { - break; - } else if chars[i] == '[' { - i += 1; - } else { - i += 1; - num += 1; - } - } - num -} - -#[cfg(test)] -mod test { - use super::*; - - #[test] - fn num_args() { - let n = get_num_args("(Ljava/nio/charset/Charset;[BII)V"); - assert_eq!(n, 4) - } -} \ No newline at end of file diff --git a/src/vm/runtime.rs b/src/vm/runtime.rs new file mode 100644 index 0000000..3376d91 --- /dev/null +++ b/src/vm/runtime.rs @@ -0,0 +1,531 @@ +use std::cell::RefCell; +use std::collections::HashMap; +use std::rc::Rc; + +use anyhow::Error; +use log::debug; + +use crate::class::ClassId; +use crate::classloader::classdef::CpEntry; +use crate::classloader::io::PATH_SEPARATOR; +use crate::classmanager::ClassManager; +use crate::value::Value::{self, *}; +use crate::vm::array::array_load; +use crate::vm::object; +use crate::vm::object::ObjectRef; +use crate::vm::object::ObjectRef::Object; +use crate::vm::opcodes::Opcode; +use crate::vm::opcodes::Opcode::*; +use std::io::Write; +pub struct Vm { + pub stack: Vec, +} + +impl Vm { + pub fn new() -> Self { + env_logger::builder() + .format(|buf, record| { + writeln!(buf, "{}: {}", record.level(), record.args()) + }) + .try_init().unwrap(); + Self { + stack: vec![] + } + } + + pub fn run(self, classpath: &str, class_name: &str, method_name: &str) { + + let classpath = classpath.split(PATH_SEPARATOR).map(|s| s.into()) + .collect(); + let mut class_manager = ClassManager::new(classpath); + + class_manager.load_class_by_name("java/lang/Class"); + class_manager.load_class_by_name("java/lang/String"); + class_manager.load_class_by_name("java/util/Collections"); + + class_manager.load_class_by_name(class_name); + let class_id = *class_manager.get_classid(class_name); + self.run2(&mut class_manager, class_id, method_name); + } + + pub(crate) fn run2(self, class_manager: &mut ClassManager, class_id: ClassId, method_name: &str) { + Stackframe::new().run(class_manager, class_id, method_name); + } +} + +pub struct Stackframe { + pc: usize, + locals: Vec, + stack: Vec, +} + +impl Stackframe { + pub fn new() -> Self { + Self { + pc: 0, + locals: vec![], + stack: vec![], + } + } + + pub fn default() -> Self { + Self { + pc: 0, + locals: vec![], + stack: vec![], + } + } + + fn push(&mut self, val: Value) { + self.stack.push(val); + } + + fn pop(&mut self) -> Value { + self.stack.pop().unwrap() + } + + pub fn run(&mut self, class_manager: &mut ClassManager, class_id: ClassId, method_name: &str) { + let classname = class_manager.get_class_by_id(&class_id).unwrap().name.to_owned(); + let code = class_manager.get_classdef(&class_id).get_method(method_name).unwrap().code.clone(); + let constant_pool = class_manager.get_classdef(&class_id).get_method(method_name).unwrap().constant_pool.clone(); + + let len = code.len(); + while self.pc < len { + let opcode: &Opcode = code.get(self.pc).unwrap(); + debug!("\tat {}.{}: {} #{:?} {:?}", classname, method_name, self.pc, opcode, self.stack); + match opcode { + NOP => {} + ACONST_NULL => { + self.push(Null); + } + ICONST_M1 => { + self.push(I32(-1)); + } + ICONST_0 => { + self.push(I32(0)); + } + ICONST_1 => { + self.push(I32(1)); + } + ICONST_2 => { + self.push(I32(2)); + } + ICONST_3 => { + self.push(I32(3)); + } + ICONST_4 => { + self.push(I32(4)); + } + ICONST_5 => { + self.push(I32(5)); + } + LCONST_0 => { + self.push(I64(0)); + } + LCONST_1 => { + self.push(I64(1)); + } + FCONST_0 => { + self.push(F32(0.0)); + } + FCONST_1 => { + self.push(F32(1.0)); + } + FCONST_2 => { + self.push(F32(2.0)); + } + DCONST_0 => { + self.push(F64(0.0)); + } + DCONST_1 => { + self.push(F64(1.0)); + } + SIPUSH(si) => { + self.push(I32(*si as i32)); + } + BIPUSH(bi) => { + self.push(I32(*bi as i32)); + } + LDC(c) | LDC_W(c) | LDC2_W(c) => { + let c = constant_pool.get(&c).unwrap(); + match c { + CpEntry::Integer(i) => { + self.push(I32(*i)); + } + CpEntry::Float(f) => { + self.push(F32(*f)); + } + CpEntry::Double(d) => { + self.push(F64(*d)); + } + CpEntry::StringRef(utf8) => { + //TODO + let string = class_manager.get_classdef(&class_id).cp_utf8(&utf8); + let string: Vec = string.as_bytes().into(); + class_manager.load_class_by_name("java/lang/String"); + let stringclass = class_manager.get_class_by_name("java/lang/String").unwrap(); + let mut stringinstance = object::Object::new(stringclass); + stringinstance.set(stringclass, "java/lang/String", "value", Ref(ObjectRef::new_byte_array(string))); + + self.push(Ref(Object(Rc::new(RefCell::new(stringinstance))))); + } + CpEntry::Long(l) => { + self.push(I64(*l)); + } + CpEntry::ClassRef(utf8_index) => { + let class_name = class_manager.get_classdef(&class_id).cp_utf8(&utf8_index).to_owned(); + class_manager.load_class_by_name(&class_name); + let klass_id = class_manager.get_classid(&class_name); + if let Some(class) = class_manager.get_classobject(klass_id) { + self.push(class.clone()); + } else { + unreachable!("should not be here"); + } + } + _ => { + panic!("add variant {:?}", c) + } + } + } + ILOAD(n) | LLOAD(n) | FLOAD(n) | DLOAD(n) | ALOAD(n) => { + // omitting the type checks so far + self.push(self.locals[*n as usize].clone()); + } + ILOAD_0 | LLOAD_0 | FLOAD_0 | DLOAD_0 | ALOAD_0 => { + self.push(self.locals[0].clone()); + } + ILOAD_1 | LLOAD_1 | FLOAD_1 | DLOAD_1 | ALOAD_1 => { + self.push(self.locals[1].clone()); + } + ILOAD_2 | LLOAD_2 | FLOAD_2 | DLOAD_2 | ALOAD_2 => { + self.push(self.locals[2].clone()); + } + ILOAD_3 | LLOAD_3 | FLOAD_3 | DLOAD_3 | ALOAD_3 => { + self.push(self.locals[3].clone()); + } + IALOAD | LALOAD | FALOAD | DALOAD | AALOAD | BALOAD | CALOAD | SALOAD => { + let index = self.pop(); + let arrayref = self.pop(); + self.push(array_load(index, arrayref).unwrap()); //TODO errorhandling + } + ISTORE(c) | LSTORE(c) | FSTORE(c) | DSTORE(c) | ASTORE(c) => { + self.store(*c).unwrap(); + } + ISTORE_0 | LSTORE_0 | DSTORE_0 | ASTORE_0 | FSTORE_0 => { + self.store(0).unwrap(); + } + ISTORE_1 | LSTORE_1 | DSTORE_1 | ASTORE_1 | FSTORE_1 => { + self.store(1).unwrap(); + } + ISTORE_2 | LSTORE_2 | DSTORE_2 | ASTORE_2 | FSTORE_2 => { + self.store(2).unwrap(); + } + ISTORE_3 | LSTORE_3 | DSTORE_3 | ASTORE_3 | FSTORE_3 => { + self.store(3).unwrap(); + } + + INVOKEVIRTUAL(c) => { + if let Some(invocation) = + get_signature_for_invoke(&constant_pool, *c) + { + // debug!("invoke {:?}", invocation); + let mut invoke_class: Option = None; + if let Ref(this) = &self.locals[0] { + if let Object(this) = this { + let invoke_classdef = class_manager.get_classdef(&class_id); + let invoke_method = invoke_classdef.get_method(&invocation.method.name); + if invoke_method.is_some() { + class_manager.load_class_by_name(&invocation.class_name); + invoke_class = Some(*class_manager.get_classid(&invocation.class_name)); + } else { + let name = class_manager.classdef_name(&this.borrow().class_id); + if let Some(name) = name { + class_manager.load_class_by_name(&name); + let class = class_manager.get_class_by_name(&name).unwrap(); + + for parent_id in &class.parents { + let classdef = class_manager.get_classdef(parent_id); + if classdef.has_method(method_name) { + invoke_class = Some(*parent_id); + break; + } + } + } else { + panic!("ClassNotFound"); + } + } + } else if let ObjectRef::Class(_class) = this { // special case for Class ? + invoke_class = Some(*class_manager.get_classid("java/lang/Class")); + } + } + if invoke_class.is_none() { + panic!(); + } + let mut args = Vec::with_capacity(invocation.method.num_args); + for _ in 0..invocation.method.num_args { + args.insert(0, self.pop().clone()); + } + args.insert(0, self.pop()); + + let mut new_stackframe = Stackframe { + pc: 0, + locals: args, + stack: vec![], + }; + new_stackframe.run(class_manager, invoke_class.unwrap(), &invocation.method.name); + } else { + unreachable!() + } + } + INVOKESPECIAL(c) | INVOKESTATIC(c) => { + if let Some(invocation) = + get_signature_for_invoke(&constant_pool, *c) + { + let mut args = Vec::with_capacity(invocation.method.num_args); + for _ in 0..invocation.method.num_args { + args.insert(0, self.pop().clone()); + } + let mut new_stackframe = Stackframe { + pc: 0, + locals: args, + stack: vec![], + }; + let invoke_class = class_manager.get_classid(invocation.class_name.as_str()); + new_stackframe.run(class_manager, *invoke_class, &invocation.method.name); + } else { + unreachable!() + } + } + GETSTATIC(field_index) => { + let classdef = class_manager.get_classdef(&class_id); + let (class_index, field_name_and_type_index) = + classdef.cp_field_ref(&field_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 field_name = classdef.cp_utf8(name_index).to_owned(); + let that_class_name = classdef.cp_utf8(classdef.cp_class_ref(class_index)).to_owned(); + class_manager.load_class_by_name(&that_class_name); + let that_class = class_manager.get_class_by_name(&that_class_name).unwrap(); + + let type_index = that_class + .static_field_mapping + .get(&that_class_name) + .unwrap()// safe because class for static field must be there + .get(&field_name) + .unwrap(); // safe because field must be there + + debug!("get_static {}.{}", that_class_name, field_name); + let field_value = class_manager.get_static(&that_class.id, type_index.index); + self.push(field_value); + } + PUTSTATIC(field_index) => { + let classdef = class_manager.get_classdef(&class_id); + let (class_index, field_name_and_type_index) = + classdef.cp_field_ref(&field_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 = class_manager.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; + class_manager.set_static(class_id, val_index, self.pop()); + } + GETFIELD(field_index) => { + let classdef = class_manager.get_classdef(&class_id); + let (class_index, field_name_and_type_index) = + classdef.cp_field_ref(&field_index); + let (field_name_index, _) = + classdef.cp_name_and_type(field_name_and_type_index); + let declared_type = classdef.cp_utf8(classdef.cp_class_ref(class_index)).to_owned(); + + let field_name = classdef.cp_utf8(field_name_index).to_owned(); + debug!("get field {}.{}",declared_type, field_name); + let objectref = self.pop(); + if let Ref(instance) = objectref { + if let Object(object) = instance { + let runtime_type = class_manager.get_class_by_id(&object.borrow().class_id).unwrap(); + let object = object.borrow(); + let value = object.get(runtime_type, &declared_type, &field_name); + self.push(value.clone()); + } else { + unreachable!() + } + } else { + unreachable!("objectref {:?}", objectref) + } + } + PUTFIELD(field_index) => { + let classdef = class_manager.get_classdef(&class_id); + let (class_index, field_name_and_type_index) = + classdef.cp_field_ref(&field_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).to_owned(); + let field_name = classdef.cp_utf8(field_name_index).to_owned(); + + let value = self.pop(); + let objectref = self.pop(); + if let Ref(instance) = objectref { + if let Object(object) = instance { + let runtime_type = class_manager.get_class_by_id(&object.borrow().class_id).unwrap(); + object.borrow_mut().set(runtime_type, &declared_type, &field_name, value); + } + } else { + unreachable!() + } + } + NEW(class_index) => { + let class_name_index = *class_manager.get_classdef(&class_id).cp_class_ref(class_index); + let class_name = class_manager.get_classdef(&class_id).cp_utf8(&class_name_index).to_owned(); + class_manager.load_class_by_name(&class_name); + let class_to_instantiate = class_manager.get_class_by_name(&class_name).unwrap(); + + let object = Object(Rc::new(RefCell::new(object::Object::new( + class_to_instantiate, + )))); + self.push(Ref(object)); + } + NEWARRAY(arraytype) => { + let count = self.pop(); + let array = ObjectRef::new_array(*arraytype, count.into_i32() as usize); + self.push(Ref(array)); + } + ANEWARRAY(class_index) => { + let class_name_index = *class_manager.get_classdef(&class_id).cp_class_ref(class_index); + let class_name = class_manager.get_classdef(&class_id).cp_utf8(&class_name_index).to_owned(); + class_manager.load_class_by_name(&class_name); + let arraytype = class_manager.get_class_by_name(&class_name).unwrap(); + let count = self.pop().into_i32(); + let array = ObjectRef::new_object_array(arraytype, count as usize); + self.push(Ref(array)); + } + ARRAYLENGTH => { + let val = self.pop(); + if let Ref(val) = val { + self.push(I32(val.get_array_length() as i32)); + } else { + unreachable!("array length {:?}", val); + } + } + MONITORENTER | MONITOREXIT => { + self.pop(); + } //TODO implement + IFNULL(_) | IFNONNULL(_) => { + let value = self.pop(); + match value { + Null => if let IFNULL(goto) = opcode { self.pc = *goto as usize; } + _ => if let IFNONNULL(goto) = opcode { self.pc = *goto as usize; } + }; + } + _ => { panic!("opcode not implemented") } + } + } + } + + fn store( + &mut self, + index: u8, + ) -> Result<(), Error> { + let index = index as usize; + let value = self.pop(); + while self.locals.len() < index + 1 { + self.locals.push(Null); //ensure capacity + } + self.locals[index] = value; + Ok(()) + } +} + +pub(crate) fn get_signature_for_invoke(cp: &HashMap, index: u16) -> Option { + if let CpEntry::MethodRef(class_index, name_and_type_index) + | CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap() + { + if let Some(method_signature) = get_name_and_type(cp, *name_and_type_index) { + if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() { + if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() { + return Some(Invocation::new( + class_name.into(), + method_signature) + ); + } + } + } + } + None +} + +pub(crate) fn get_name_and_type(cp: &HashMap, index: u16) -> Option { + if let CpEntry::NameAndType(method_name_index, signature_index) = cp.get(&index).unwrap() { + if let CpEntry::Utf8(method_name) = cp.get(method_name_index).unwrap() { + if let CpEntry::Utf8(signature) = cp.get(signature_index).unwrap() { + let mut method_signature: String = method_name.into(); + let num_args = get_num_args(signature); + method_signature.push_str(signature); + return Some(MethodSignature::new(method_signature, num_args)); + } + } + } + None +} + +fn get_num_args(signature: &str) -> usize { + let mut num = 0; + let mut i = 1; + let chars: Vec = signature.chars().collect(); + + while i < chars.len() { + if chars[i] == 'L' { + i += 1; + while chars[i] != ';' { + i += 1; + } + i += 1; + num += 1; + } else if chars[i] == ')' { + break; + } else if chars[i] == '[' { + i += 1; + } else { + i += 1; + num += 1; + } + } + num +} + +#[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, + } + } +} \ No newline at end of file diff --git a/src/vm/stack.rs b/src/vm/stack.rs deleted file mode 100644 index 9a2dfbe..0000000 --- a/src/vm/stack.rs +++ /dev/null @@ -1,29 +0,0 @@ -use anyhow::Error; -use log::debug; - -use crate::class::Value; - -#[derive(Debug)] -pub struct StackFrame { - pub(crate) at: String, - pub(crate) data: Vec, -} - -// maybe just call frame -impl StackFrame { - pub(crate) fn new(at_class: &str, at_method: &str) -> Self { - let mut at: String = at_class.into(); - at.push('.'); - at.push_str(at_method); - Self { at, data: vec![] } - } - - pub(crate) fn push(&mut self, val: Value) { - debug!("push {:?}", val); - self.data.push(val); - } - - pub(crate) fn pop(&mut self) -> Result { - Ok(self.data.pop().unwrap()) - } -} \ No newline at end of file diff --git a/src/vm/vm.rs b/src/vm/vm.rs deleted file mode 100644 index 51e71ff..0000000 --- a/src/vm/vm.rs +++ /dev/null @@ -1,657 +0,0 @@ -use std::cell::RefCell; -use std::io::Write; -use std::rc::Rc; - -use anyhow::Error; -use log::debug; - -use crate::class::{Class, ClassId, Object, ObjectRef, Value}; -use crate::class::Value::{F32, F64, I32, I64, Null, Ref, Void}; -use crate::classloader::classdef::{AttributeType, Modifier}; -use crate::classloader::io::{read_u16, read_u8}; -use crate::classmanager; -use crate::classmanager::{get_class_by_id, get_classid}; -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, load_constant}; -use crate::vm::stack::StackFrame; - -pub struct Vm {} - - -#[cfg(target_family = "unix")] -const PATH_SEPARATOR: char = ':'; - -#[cfg(target_family = "windows")] -const PATH_SEPARATOR: char = ';'; - -const MASK_LOWER_5BITS: i32 = 0b00011111; - -/// 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) -> 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) { - classmanager::load_class_by_name("java/lang/Class"); - classmanager::load_class_by_name("jdk/internal/misc/Unsafe"); - vm.execute_static(stack, "java/lang/System", "initPhase1()V", vec![]).expect("cannot create VM"); - } - - /// execute the bytecode - pub fn execute_virtual( - &mut self, - stack: &mut Vec, - class_name: &str, - method_name: &str, - args: Vec, - ) -> Result { - // this can't be null - 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.id, 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); - if classdef.has_method(method_name) { - return self.execute_class(stack, *parent_id, method_name, args.clone()); - } - } - } else { - panic!("ClassNotFound"); - } - } - } else if let ObjectRef::Class(_class) = this { // special case for Class ? - let klazz = classmanager::get_class_by_name("java/lang/Class").unwrap(); - return self.execute_class(stack, klazz.id, method_name, args); - } - } - panic!("Method {} not found in class {}", method_name, class_name); - } - - pub fn execute_special( - &mut self, - stack: &mut Vec, - class_name: &str, - method_name: &str, - args: Vec, - ) -> Result { - classmanager::load_class_by_name(class_name); - let classid = classmanager::get_classid(class_name); - self.execute_class(stack, classid, method_name, args) - } - - pub fn execute_static( - &mut self, - stack: &mut Vec, - class_name: &str, - method_name: &str, - args: Vec, - ) -> Result { - classmanager::load_class_by_name(class_name); - self.execute_class(stack, get_classid(class_name), method_name, args) - } - - pub fn execute_class( - &mut self, - stackframes: &mut Vec, - this_class: ClassId, - method_name: &str, - args: Vec, - ) -> Result { - let this_class_name = &get_class_by_id(this_class).unwrap().name; - debug!("execute {}.{}", this_class_name, method_name); - - //TODO implement dynamic dispatch -> get method from instance - let method = classmanager::get_classdef(this_class).get_method(method_name).unwrap(); - let mut local_params: Vec> = - 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); - debug!("id {}", this_class); - 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; - load_constant(&cp_index, method, stackframes, this_class); - } - LDC_W => { - let cp_index = read_u16(&code.opcodes, pc); - load_constant(&cp_index, method, stackframes, this_class); - } - LDC2_W => { - let cp_index = read_u16(&code.opcodes, pc); - load_constant(&cp_index, method, stackframes, this_class); - } - 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())); - } - ISUB => { - 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())); - } - ISHL => { - let value2 = current_frame(stackframes).pop()?; - let value1 = current_frame(stackframes).pop()?; - debug!("{:?} shl {:?}", value1, value2); - current_frame(stackframes).push(I32(value1.into_i32() << (value2.into_i32() & MASK_LOWER_5BITS))); - } - 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() & MASK_LOWER_5BITS))); - } - 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); - 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, val_index, value); - } - GETFIELD => { - let classdef = classmanager::get_classdef(this_class); - 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); - 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!() - } - } - 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!() - } - } - 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!("id before new {}", this_class); - 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()?); - debug!("invoke special{:?}", invocation); - - let return_value = self.execute_special(stackframes, - &invocation.class_name, - &invocation.method.name, - args, - )?; - // let return_value = Null; - debug!("id after new {}", this_class); - 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); - 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(Object::new( - 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); - 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, - local_params: &mut Vec>, - 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, - } - } -} - -pub(crate) fn current_frame(stackframes: &mut Vec) -> &mut StackFrame { - let i = stackframes.len() - 1; - stackframes.get_mut(i).unwrap() -} \ No newline at end of file diff --git a/tests/class_tests.rs b/tests/class_tests.rs index 68d881b..29f8a97 100644 --- a/tests/class_tests.rs +++ b/tests/class_tests.rs @@ -1,39 +1,18 @@ mod test { - use java_rs::class::{ObjectRef, Value}; - use java_rs::classmanager::set_classpath; - use java_rs::vm::Vm; + use java_rs::vm::runtime::Vm; + + #[test] fn if_cmp() { - let mut stackframes = Vec::new(); - let mut vm = Vm::new(&mut stackframes); - set_classpath("/Users/Shautvast/dev/java/tests"); - let ret = vm.execute_virtual(&mut stackframes, "testclasses.IfCmp", "i_is_1()Z", vec![]).unwrap(); - if let Value::I32(b) = ret { - // internally a boolean is an int - assert_eq!(0, b); - } else { - println!("{:?}", ret); - assert!(false) - } + let vm = Vm::new(); + vm.run("/Users/Shautvast/dev/java/tests", "testclasses.IfCmp", "i_is_1()Z"); } #[test] fn consts() { - let mut stackframes = Vec::new(); - let mut vm = Vm::new(&mut stackframes); - set_classpath("/Users/Shautvast/dev/java/tests"); - let ret = vm - .execute_static(&mut stackframes, "testclasses.Const", "hello()Ljava/lang/String;", vec![]) - .unwrap(); - if let Value::Ref(s) = ret { - // internally a boolean is an int - if let ObjectRef::Object(a) = s { - println!("{:?}", a); - } - } else { - println!("{:?}", ret); - assert!(false) - } + + let vm = Vm::new(); + vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Const", "hello()Ljava/lang/String;") } }