Updated design for stackframes. Unsafe totally removed. Overhauled class loading. still WIP
This commit is contained in:
parent
f2dc2d2938
commit
d51f6627b3
20 changed files with 1355 additions and 1822 deletions
203
src/class.rs
203
src/class.rs
|
|
@ -1,14 +1,5 @@
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::{HashMap, LinkedList};
|
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;
|
pub type ClassId = usize;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
|
@ -29,6 +20,7 @@ impl TypeIndex {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Class {
|
pub struct Class {
|
||||||
pub id: ClassId,
|
pub id: ClassId,
|
||||||
|
pub initialized: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub superclass: Option<ClassId>,
|
pub superclass: Option<ClassId>,
|
||||||
pub parents: LinkedList<ClassId>,
|
pub parents: LinkedList<ClassId>,
|
||||||
|
|
@ -49,196 +41,3 @@ impl Class {
|
||||||
.unwrap_or(0)
|
.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<i8>),
|
|
||||||
ShortArray(Vec<i16>),
|
|
||||||
IntArray(Vec<i32>),
|
|
||||||
LongArray(Vec<i64>),
|
|
||||||
FloatArray(Vec<f32>),
|
|
||||||
DoubleArray(Vec<f64>),
|
|
||||||
BooleanArray(Vec<bool>),
|
|
||||||
CharArray(Vec<char>),
|
|
||||||
StringArray(Vec<String>),
|
|
||||||
ObjectArray(ClassId, Vec<ObjectRef>),
|
|
||||||
Object(Rc<RefCell<Object>>),
|
|
||||||
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<u8>) -> Self {
|
|
||||||
ByteArray(into_vec_i8(d))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn into_vec_i8(v: Vec<u8>) -> Vec<i8> {
|
|
||||||
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<Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<Value> {
|
|
||||||
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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,11 @@ use std::fmt::{Debug, Formatter};
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::classloader::io::read_u16;
|
use crate::classloader::io::read_u16;
|
||||||
|
use crate::vm::opcodes::Opcode;
|
||||||
|
|
||||||
/// This is the class representation when the bytecode had just been loaded.
|
/// This is the class representation when the bytecode had just been loaded.
|
||||||
|
|
||||||
pub struct ClassDef {
|
pub(crate) struct ClassDef {
|
||||||
pub minor_version: u16,
|
pub minor_version: u16,
|
||||||
pub major_version: u16,
|
pub major_version: u16,
|
||||||
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
|
pub constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||||
|
|
@ -317,16 +318,17 @@ impl Field {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Method {
|
pub(crate) struct Method {
|
||||||
pub(crate) constant_pool: Rc<HashMap<u16, CpEntry>>,
|
pub(crate) constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||||
access_flags: u16,
|
access_flags: u16,
|
||||||
name_index: u16,
|
name_index: u16,
|
||||||
descriptor_index: u16,
|
descriptor_index: u16,
|
||||||
pub(crate) attributes: HashMap<String, AttributeType>,
|
pub(crate) attributes: HashMap<String, AttributeType>,
|
||||||
|
pub code: Vec<Opcode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Method {
|
impl Debug for Method {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
f,
|
f,
|
||||||
"Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
|
"Method {{access_flags: {}, name_index: {}, descriptor_index: {}, attributes: {:?} }}",
|
||||||
|
|
@ -342,6 +344,7 @@ impl Method {
|
||||||
name_index: u16,
|
name_index: u16,
|
||||||
descriptor_index: u16,
|
descriptor_index: u16,
|
||||||
attributes: HashMap<String, AttributeType>,
|
attributes: HashMap<String, AttributeType>,
|
||||||
|
code: Vec<Opcode>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Method {
|
Method {
|
||||||
constant_pool,
|
constant_pool,
|
||||||
|
|
@ -349,6 +352,7 @@ impl Method {
|
||||||
name_index,
|
name_index,
|
||||||
descriptor_index,
|
descriptor_index,
|
||||||
attributes,
|
attributes,
|
||||||
|
code,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
262
src/classloader/code_parser.rs
Normal file
262
src/classloader/code_parser.rs
Normal file
|
|
@ -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<Opcode> {
|
||||||
|
let mut code: HashMap<u16, (u16, Opcode)> = 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
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
use std::fs::{self};
|
use std::fs::{self};
|
||||||
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
|
use crate::vm::opcodes::Opcode;
|
||||||
|
use crate::vm::opcodes::Opcode::*;
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
pub const PATH_SEPARATOR: char = ':';
|
pub const PATH_SEPARATOR: char = ':';
|
||||||
|
|
@ -44,7 +46,6 @@ pub fn find_class(classpath: &Vec<String>, class_name: &str) -> Result<String, E
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// methods to read values from big-endian binary data
|
// methods to read values from big-endian binary data
|
||||||
|
|
||||||
pub(crate) fn read_u8(data: &[u8], pos: &mut usize) -> u8 {
|
pub(crate) fn read_u8(data: &[u8], pos: &mut usize) -> u8 {
|
||||||
|
|
@ -116,3 +117,65 @@ pub(crate) fn read_f64(data: &[u8], pos: &mut usize) -> f64 {
|
||||||
.expect("slice with incorrect length"),
|
.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<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub(crate) struct Lookupswitch {
|
||||||
|
default: i32,
|
||||||
|
match_offset_pairs: Vec<(i32, i32)>,
|
||||||
|
}
|
||||||
|
|
@ -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::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::classdef::{AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode};
|
||||||
|
use crate::classloader::code_parser::parse_code;
|
||||||
|
|
||||||
pub mod classdef;
|
pub mod classdef;
|
||||||
pub(crate) mod io;
|
pub(crate) mod io;
|
||||||
|
mod code_parser;
|
||||||
|
|
||||||
pub(crate) fn get_classdef(classpath: &Vec<String>, class_name: &str) -> Result<ClassDef,Error> {
|
pub(crate) fn get_classdef(classpath: &Vec<String>, class_name: &str) -> Result<ClassDef,Error> {
|
||||||
debug!("read class {} ", class_name);
|
debug!("read class {} ", class_name);
|
||||||
|
|
@ -94,6 +96,8 @@ fn load_class(bytecode: Vec<u8>) -> Result<ClassDef, Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Ok(ClassDef::new(
|
Ok(ClassDef::new(
|
||||||
minor_version,
|
minor_version,
|
||||||
major_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(
|
Method::new(
|
||||||
constant_pool,
|
constant_pool,
|
||||||
access_flags,
|
access_flags,
|
||||||
name_index,
|
name_index,
|
||||||
descriptor_index,
|
descriptor_index,
|
||||||
attributes,
|
attributes,
|
||||||
|
code
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,93 +5,16 @@ use std::rc::Rc;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
use crate::class::{Class, ClassId, ObjectRef, TypeIndex, Value::*, Value};
|
use crate::class::{Class, ClassId, TypeIndex};
|
||||||
use crate::class::Object;
|
|
||||||
use crate::classloader;
|
use crate::classloader;
|
||||||
use crate::classloader::classdef::{ClassDef, Modifier};
|
use crate::classloader::classdef::{ClassDef, Method, Modifier};
|
||||||
use crate::classloader::io::PATH_SEPARATOR;
|
use crate::vm::object::{Object, ObjectRef};
|
||||||
use crate::vm::Vm;
|
use crate::value::Value;
|
||||||
|
use crate::value::Value::*;
|
||||||
|
use crate::vm::runtime::Vm;
|
||||||
|
|
||||||
static mut CLASSMANAGER: Lazy<ClassManager> = Lazy::new(|| ClassManager::new());
|
|
||||||
static PRIMITIVES: Lazy<Vec<&str>> = Lazy::new(|| vec!["B", "S", "I", "J", "F", "D", "Z", "J", "C"]);
|
static PRIMITIVES: Lazy<Vec<&str>> = 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<String> {
|
|
||||||
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
|
//TODO less pubs
|
||||||
pub struct ClassManager {
|
pub struct ClassManager {
|
||||||
static_class_data: HashMap<ClassId, Vec<Value>>,
|
static_class_data: HashMap<ClassId, Vec<Value>>,
|
||||||
|
|
@ -101,18 +24,17 @@ pub struct ClassManager {
|
||||||
classpath: Vec<String>,
|
classpath: Vec<String>,
|
||||||
|
|
||||||
//references to classdefs, ie the static class info
|
//references to classdefs, ie the static class info
|
||||||
pub classdefs: HashMap<ClassId, ClassDef>,
|
pub(crate) classdefs: HashMap<ClassId, ClassDef>,
|
||||||
|
|
||||||
// references to the runtime class
|
// references to the runtime class
|
||||||
pub classes: HashMap<ClassId, Class>,
|
pub classes: HashMap<ClassId, Class>,
|
||||||
|
|
||||||
pub names: HashMap<String, ClassId>,
|
pub names: HashMap<String, ClassId>,
|
||||||
pub class_objects: HashMap<ClassId, Value>,
|
pub class_objects: HashMap<ClassId, Value>,
|
||||||
vm: Vm,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ClassManager {
|
impl ClassManager {
|
||||||
pub fn new() -> Self {
|
pub fn new(classpath: Vec<String>) -> Self {
|
||||||
Self {
|
Self {
|
||||||
static_class_data: HashMap::new(),
|
static_class_data: HashMap::new(),
|
||||||
current_id: 0,
|
current_id: 0,
|
||||||
|
|
@ -120,19 +42,23 @@ impl ClassManager {
|
||||||
classes: HashMap::new(),
|
classes: HashMap::new(),
|
||||||
class_objects: HashMap::new(),
|
class_objects: HashMap::new(),
|
||||||
names: HashMap::new(),
|
names: HashMap::new(),
|
||||||
classpath: vec![],
|
classpath,
|
||||||
vm: Vm::new_internal(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_classpath(&mut self, classpath: &str) {
|
pub fn get_static(&self, id: &ClassId, index: usize) -> Value {
|
||||||
self.classpath = classpath
|
self.static_class_data.get(id).unwrap()[index].clone()
|
||||||
.split(PATH_SEPARATOR)
|
|
||||||
.map(|s| s.into())
|
|
||||||
.collect();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
if !self.classes.contains_key(id) {
|
||||||
let name = self.classdef_name(id);
|
let name = self.classdef_name(id);
|
||||||
if name.is_some() {
|
if name.is_some() {
|
||||||
|
|
@ -142,20 +68,20 @@ impl ClassManager {
|
||||||
self.classes.get(id)
|
self.classes.get(id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn classdef_name(&self, id: &ClassId) -> Option<String> {
|
pub fn classdef_name(&self, id: &ClassId) -> Option<String> {
|
||||||
self.classdefs.get(id).map(|c| c.name().to_owned()) //drops borrow to self here
|
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()
|
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()
|
self.classdefs.get(&id).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// loads the class if not already there
|
/// 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);
|
debug!("load class {}", name);
|
||||||
// determine no of dimensions and get type of array if any
|
// determine no of dimensions and get type of array if any
|
||||||
let mut chars = name.chars();
|
let mut chars = name.chars();
|
||||||
|
|
@ -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
|
/// 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 id = self.names.get(name);
|
||||||
let t = self.classes.get(id.unwrap());
|
self.classes.get_mut(id.unwrap())
|
||||||
t
|
}
|
||||||
|
|
||||||
|
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)
|
/// 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 {
|
self.classes.insert(this_classid, Class {
|
||||||
id: this_classid,
|
id: this_classid,
|
||||||
|
initialized: false,
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
superclass: superclass_id,
|
superclass: superclass_id,
|
||||||
parents,
|
parents,
|
||||||
|
|
@ -272,7 +209,7 @@ impl ClassManager {
|
||||||
|
|
||||||
// run static init
|
// run static init
|
||||||
if this_classdef.methods.contains_key("<clinit>()V") {
|
if this_classdef.methods.contains_key("<clinit>()V") {
|
||||||
self.vm.execute_special(&mut vec![], name, "<clinit>()V", vec![]).unwrap();
|
Vm { stack: Vec::new()}.run2(self, this_classid,"<clinit>()V");
|
||||||
}
|
}
|
||||||
|
|
||||||
this_classid
|
this_classid
|
||||||
|
|
@ -316,6 +253,7 @@ impl ClassManager {
|
||||||
|
|
||||||
/// loads the class and returns it's dependencies
|
/// loads the class and returns it's dependencies
|
||||||
fn load_class_and_deps(&mut self, name: &str) -> (ClassId, Vec<String>) {
|
fn load_class_and_deps(&mut self, name: &str) -> (ClassId, Vec<String>) {
|
||||||
|
debug!("load {}", name);
|
||||||
let id = self.get_or_new_id(name);
|
let id = self.get_or_new_id(name);
|
||||||
|
|
||||||
let classdef = self.classdefs
|
let classdef = self.classdefs
|
||||||
|
|
@ -426,7 +364,7 @@ mod test {
|
||||||
let mut fields_declared_by_java_lang_class = HashMap::new();
|
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 });
|
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);
|
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 {
|
let mut cm = ClassManager {
|
||||||
static_class_data: HashMap::new(),
|
static_class_data: HashMap::new(),
|
||||||
|
|
@ -436,7 +374,6 @@ mod test {
|
||||||
current_id: 1,
|
current_id: 1,
|
||||||
names,
|
names,
|
||||||
classpath: Vec::new(),
|
classpath: Vec::new(),
|
||||||
vm: Vm::new(&mut vec![]),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let c_id = cm.add_class("C");
|
let c_id = cm.add_class("C");
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
pub mod classloader;
|
pub mod classloader;
|
||||||
pub mod classmanager;
|
pub mod classmanager;
|
||||||
pub mod class;
|
mod value;
|
||||||
|
mod class;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
pub mod heap;
|
|
||||||
12
src/main.rs
12
src/main.rs
|
|
@ -1,11 +1,7 @@
|
||||||
use java_rs::classmanager::set_classpath;
|
use java_rs::vm::runtime::Vm;
|
||||||
use java_rs::vm::Vm;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut stackframes = Vec::new();
|
let vm = Vm::new();
|
||||||
let mut vm = Vm::new(&mut stackframes);
|
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Main", "main([Ljava/lang/String;)V");
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
39
src/value.rs
Normal file
39
src/value.rs
Normal file
|
|
@ -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!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
|
use crate::vm::object::ObjectRef::*;
|
||||||
|
|
||||||
use crate::class::ObjectRef::*;
|
use crate::value::Value;
|
||||||
use crate::class::Value::{self, *};
|
use crate::value::Value::*;
|
||||||
|
|
||||||
|
|
||||||
pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error> {
|
pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error> {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,4 @@
|
||||||
mod vm;
|
|
||||||
pub use vm::Vm;
|
|
||||||
mod operations;
|
|
||||||
mod stack;
|
|
||||||
mod array;
|
mod array;
|
||||||
mod opcodes;
|
pub(crate) mod object;
|
||||||
mod native;
|
pub(crate) mod opcodes;
|
||||||
|
pub mod runtime;
|
||||||
161
src/vm/native.rs
161
src/vm/native.rs
|
|
@ -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<StackFrame>, class_name: &str, method_name: &str, _args: Vec<Value>) -> Result<Value, Error> {
|
|
||||||
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<Value, Error> {
|
|
||||||
Ok(match method_name {
|
|
||||||
"desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false),
|
|
||||||
_ => Void
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn java_lang_System(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
|
|
||||||
Ok(match method_name {
|
|
||||||
_ => Void
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jdk_internal_misc_Unsafe(_vm: &Vm, method_name: &str) -> Result<Value, Error> {
|
|
||||||
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<StackFrame>, method_name: &str) -> Result<Value, Error> {
|
|
||||||
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<StackFrame>) -> Result<Value, Error> {
|
|
||||||
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", "<init>()V", vec![hashmap.clone()])?;
|
|
||||||
Ok(hashmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vmProperties(_vm: &mut Vm, _stackframes: &mut [StackFrame]) -> Result<Value, Error> {
|
|
||||||
let props: Lazy<Vec<String>> = Lazy::new(|| {
|
|
||||||
let vec: Vec<String> = Vec::new();
|
|
||||||
//TODO insert some values
|
|
||||||
vec
|
|
||||||
});
|
|
||||||
Ok(Value::Ref(ObjectRef::StringArray(props.to_vec())))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn platformProperties() -> Result<Value, Error> {
|
|
||||||
let props: Lazy<Vec<String>> = Lazy::new(|| {
|
|
||||||
let mut vec: Vec<String> = 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())))
|
|
||||||
}
|
|
||||||
160
src/vm/object.rs
Normal file
160
src/vm/object.rs
Normal file
|
|
@ -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<i8>),
|
||||||
|
ShortArray(Vec<i16>),
|
||||||
|
IntArray(Vec<i32>),
|
||||||
|
LongArray(Vec<i64>),
|
||||||
|
FloatArray(Vec<f32>),
|
||||||
|
DoubleArray(Vec<f64>),
|
||||||
|
BooleanArray(Vec<bool>),
|
||||||
|
CharArray(Vec<char>),
|
||||||
|
StringArray(Vec<String>),
|
||||||
|
ObjectArray(ClassId, Vec<ObjectRef>),
|
||||||
|
Object(Rc<RefCell<Object>>),
|
||||||
|
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<u8>) -> Self {
|
||||||
|
ByteArray(into_vec_i8(d))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn into_vec_i8(v: Vec<u8>) -> Vec<i8> {
|
||||||
|
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<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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<Value> {
|
||||||
|
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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Opcode>),
|
||||||
|
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<Vec<&str>> = 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
|
|
||||||
});
|
|
||||||
|
|
@ -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<Value, Error> {
|
|
||||||
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<u16, CpEntry>, index: u16) -> Option<MethodSignature> {
|
|
||||||
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<u16, CpEntry>, index: u16) -> Option<Invocation> {
|
|
||||||
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<StackFrame>, 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<u8> = string.as_bytes().into();
|
|
||||||
classmanager::load_class_by_name("java/lang/String");
|
|
||||||
let stringclass = classmanager::get_class_by_name("java/lang/String").unwrap();
|
|
||||||
let mut stringinstance = 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<char> = 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
531
src/vm/runtime.rs
Normal file
531
src/vm/runtime.rs
Normal file
|
|
@ -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<Stackframe>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<Value>,
|
||||||
|
stack: Vec<Value>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u8> = 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<ClassId> = 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<u16, CpEntry>, index: u16) -> Option<Invocation> {
|
||||||
|
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<u16, CpEntry>, index: u16) -> Option<MethodSignature> {
|
||||||
|
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<char> = 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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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<Value>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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<Value, Error> {
|
|
||||||
Ok(self.data.pop().unwrap())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
657
src/vm/vm.rs
657
src/vm/vm.rs
|
|
@ -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<StackFrame>) -> Self {
|
|
||||||
env_logger::builder()
|
|
||||||
.format(|buf, record| {
|
|
||||||
writeln!(buf, "{}: {}", record.level(), record.args())
|
|
||||||
})
|
|
||||||
.try_init().unwrap();
|
|
||||||
let mut vm_instance = Self {};
|
|
||||||
classmanager::init();
|
|
||||||
Vm::init(&mut vm_instance, stack);
|
|
||||||
vm_instance
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init(vm: &mut Vm, stack: &mut Vec<StackFrame>) {
|
|
||||||
classmanager::load_class_by_name("java/lang/Class");
|
|
||||||
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<StackFrame>,
|
|
||||||
class_name: &str,
|
|
||||||
method_name: &str,
|
|
||||||
args: Vec<Value>,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
// 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<StackFrame>,
|
|
||||||
class_name: &str,
|
|
||||||
method_name: &str,
|
|
||||||
args: Vec<Value>,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
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<StackFrame>,
|
|
||||||
class_name: &str,
|
|
||||||
method_name: &str,
|
|
||||||
args: Vec<Value>,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
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<StackFrame>,
|
|
||||||
this_class: ClassId,
|
|
||||||
method_name: &str,
|
|
||||||
args: Vec<Value>,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
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<Option<Value>> =
|
|
||||||
args.clone().iter().map(|e| Some(e.clone())).collect();
|
|
||||||
if method.is(Modifier::Native) {
|
|
||||||
return invoke_native(self, stackframes, this_class_name, method_name, args);
|
|
||||||
}
|
|
||||||
if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() {
|
|
||||||
let stackframe = StackFrame::new(&this_class_name, method_name);
|
|
||||||
stackframes.push(stackframe);
|
|
||||||
|
|
||||||
let pc = &mut 0;
|
|
||||||
while *pc < code.opcodes.len() {
|
|
||||||
let opcode = read_u8(&code.opcodes, pc);
|
|
||||||
let cur_frame = current_frame(stackframes);
|
|
||||||
debug!("\t{} #{} {} - {:?}", &cur_frame.at, &*pc - 1, opcodes::OPCODES[opcode as usize], cur_frame.data);
|
|
||||||
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<StackFrame>,
|
|
||||||
local_params: &mut Vec<Option<Value>>,
|
|
||||||
index: usize,
|
|
||||||
) -> Result<(), Error> {
|
|
||||||
let value = current_frame(stack).pop()?;
|
|
||||||
while local_params.len() < index + 1 {
|
|
||||||
local_params.push(None);
|
|
||||||
}
|
|
||||||
local_params[index] = Some(value);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct Invocation {
|
|
||||||
class_name: String,
|
|
||||||
method: MethodSignature,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Invocation {
|
|
||||||
pub fn new(class_name: String, method: MethodSignature) -> Self {
|
|
||||||
Self {
|
|
||||||
class_name,
|
|
||||||
method,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub(crate) struct MethodSignature {
|
|
||||||
name: String,
|
|
||||||
num_args: usize,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl MethodSignature {
|
|
||||||
pub(crate) fn new(name: String, num_args: usize) -> Self {
|
|
||||||
MethodSignature {
|
|
||||||
name,
|
|
||||||
num_args,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn current_frame(stackframes: &mut Vec<StackFrame>) -> &mut StackFrame {
|
|
||||||
let i = stackframes.len() - 1;
|
|
||||||
stackframes.get_mut(i).unwrap()
|
|
||||||
}
|
|
||||||
|
|
@ -1,39 +1,18 @@
|
||||||
mod test {
|
mod test {
|
||||||
use java_rs::class::{ObjectRef, Value};
|
use java_rs::vm::runtime::Vm;
|
||||||
use java_rs::classmanager::set_classpath;
|
|
||||||
use java_rs::vm::Vm;
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_cmp() {
|
fn if_cmp() {
|
||||||
let mut stackframes = Vec::new();
|
let vm = Vm::new();
|
||||||
let mut vm = Vm::new(&mut stackframes);
|
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.IfCmp", "i_is_1()Z");
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn consts() {
|
fn consts() {
|
||||||
let mut stackframes = Vec::new();
|
|
||||||
let mut vm = Vm::new(&mut stackframes);
|
let vm = Vm::new();
|
||||||
set_classpath("/Users/Shautvast/dev/java/tests");
|
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Const", "hello()Ljava/lang/String;")
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue