Compare commits
No commits in common. "ccf7426086152edd2c12ac9e8b29c2f805274b87" and "98300a153294230af925439258137585512e5fae" have entirely different histories.
ccf7426086
...
98300a1532
17 changed files with 308 additions and 1514 deletions
14
src/class.rs
14
src/class.rs
|
|
@ -10,7 +10,10 @@ pub(crate) struct TypeIndex {
|
||||||
|
|
||||||
impl TypeIndex {
|
impl TypeIndex {
|
||||||
pub(crate) fn new(type_name: String, index: usize) -> Self {
|
pub(crate) fn new(type_name: String, index: usize) -> Self {
|
||||||
Self { type_name, index }
|
Self {
|
||||||
|
type_name,
|
||||||
|
index,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -20,21 +23,14 @@ pub struct Class {
|
||||||
pub initialized: bool,
|
pub initialized: bool,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub superclass: Option<ClassId>,
|
pub superclass: Option<ClassId>,
|
||||||
pub all_superclasses: LinkedList<ClassId>, // all superclasses in a flat list
|
pub parents: LinkedList<ClassId>,
|
||||||
pub interfaces: Vec<ClassId>,
|
pub interfaces: Vec<ClassId>,
|
||||||
pub all_interfaces: Vec<ClassId>, // all interfaces and their parents in a flat list
|
|
||||||
// lookup index and type from the name of the declared class and then field
|
// lookup index and type from the name of the declared class and then field
|
||||||
pub(crate) object_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
|
pub(crate) object_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
|
||||||
pub(crate) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
|
pub(crate) static_field_mapping: HashMap<String, HashMap<String, TypeIndex>>,
|
||||||
// pub(crate) static_field_data: Vec<Value> // moved to classmanager
|
// pub(crate) static_field_data: Vec<Value> // moved to classmanager
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Class {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Class {
|
impl Class {
|
||||||
/// gets the number of non-static fields on the class
|
/// gets the number of non-static fields on the class
|
||||||
pub(crate) fn n_object_fields(&self) -> usize {
|
pub(crate) fn n_object_fields(&self) -> usize {
|
||||||
|
|
|
||||||
|
|
@ -94,7 +94,6 @@ impl ClassDef {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// gets the class name if the index is a class ref index
|
|
||||||
pub fn cp_class_name(&self, index: &u16) -> &String {
|
pub fn cp_class_name(&self, index: &u16) -> &String {
|
||||||
let cr = self.cp_class_ref(index);
|
let cr = self.cp_class_ref(index);
|
||||||
self.cp_utf8(cr)
|
self.cp_utf8(cr)
|
||||||
|
|
@ -319,13 +318,13 @@ 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>>,
|
||||||
pub 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(crate) code: Vec<Opcode>,
|
pub code: Vec<Opcode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Debug for Method {
|
impl Debug for Method {
|
||||||
|
|
|
||||||
|
|
@ -1,50 +1,39 @@
|
||||||
use log::debug;
|
use std::collections::HashMap;
|
||||||
use std::collections::BTreeMap;
|
|
||||||
|
|
||||||
use crate::classloader::io::{
|
use log::debug;
|
||||||
read_i16, read_i32, read_lookupswitch, read_tableswitch, read_u16, read_u8, read_wide_opcode,
|
|
||||||
};
|
use crate::classloader::io::{read_i32, read_lookupswitch, read_tableswitch, read_u16, read_u8, read_wide_opcode};
|
||||||
use crate::vm::opcodes::Opcode::{self, *};
|
use crate::vm::opcodes::Opcode::{self, *};
|
||||||
|
|
||||||
pub(crate) fn parse_opcodes(opcodes: &[u8]) -> Vec<Opcode> {
|
pub(crate) fn parse_code(opcodes: &[u8]) -> Vec<Opcode> {
|
||||||
let mut code: BTreeMap<u16, (u16, Opcode)> = BTreeMap::new();
|
let mut code: HashMap<u16, (u16, Opcode)> = HashMap::new();
|
||||||
let mut c = 0;
|
let mut c = 0;
|
||||||
let mut opcode_index: u16 = 0;
|
let mut opcode_index: u16 = 0;
|
||||||
let mut index_before_read: u16;
|
// debug!("len {:?}", opcodes.len());
|
||||||
while c < opcodes.len() {
|
while c < opcodes.len() {
|
||||||
index_before_read = c as u16;
|
|
||||||
let opcode = get_opcode(opcodes, &mut c);
|
let opcode = get_opcode(opcodes, &mut c);
|
||||||
code.insert(index_before_read, (opcode_index, opcode));
|
code.insert(c as u16, (opcode_index, opcode));
|
||||||
opcode_index += 1;
|
opcode_index += 1;
|
||||||
}
|
}
|
||||||
let code2 = code.clone(); //clone to look up
|
let code2 = code.clone(); //clone to look up
|
||||||
|
|
||||||
// for jumps, map index of opcode as u8 to index of opcode as enum
|
// for jumps, map index of opcode as u8 to index of opcode as enum
|
||||||
debug!("{:?}", code);
|
code.into_iter().map(|(_, (_, opcode))|
|
||||||
code.into_iter()
|
match opcode {
|
||||||
.map(|(_, (_, opcode))| match opcode {
|
IFNULL(goto) => {
|
||||||
IFNULL(goto) => IFNULL(code2.get(&goto).unwrap().0),
|
debug!("goto {:?}", goto);
|
||||||
IFNONNULL(goto) => IFNONNULL(code2.get(&goto).unwrap().0),
|
debug!("{:?}", code2);
|
||||||
|
IFNULL(code2.get(&goto).unwrap().0)
|
||||||
IF_ICMPEQ(goto) => IF_ICMPEQ(code2.get(&goto).unwrap().0),
|
}
|
||||||
IF_ICMPNE(goto) => IF_ICMPNE(code2.get(&goto).unwrap().0),
|
IFNONNULL(goto) => {
|
||||||
IF_ICMPGT(goto) => IF_ICMPGT(code2.get(&goto).unwrap().0),
|
debug!("goto {:?}", &goto);
|
||||||
IF_ICMPGE(goto) => IF_ICMPGE(code2.get(&goto).unwrap().0),
|
debug!("{:?}", code2);
|
||||||
IF_ICMPLT(goto) => IF_ICMPLT(code2.get(&goto).unwrap().0),
|
IFNONNULL(code2.get(&goto).unwrap().0)
|
||||||
IF_ICMPLE(goto) => IF_ICMPLE(code2.get(&goto).unwrap().0),
|
}
|
||||||
IFEQ(goto) => IFEQ(code2.get(&goto).unwrap().0),
|
|
||||||
IFNE(goto) => IFNE(code2.get(&goto).unwrap().0),
|
|
||||||
IFGT(goto) => IFGT(code2.get(&goto).unwrap().0),
|
|
||||||
IFGE(goto) => IFGE(code2.get(&goto).unwrap().0),
|
|
||||||
IFLT(goto) => IFLT(code2.get(&goto).unwrap().0),
|
|
||||||
IFLE(goto) => IFLE(code2.get(&goto).unwrap().0),
|
|
||||||
GOTO(goto) => GOTO(code2.get(&goto).unwrap().0),
|
|
||||||
IF_ACMPEQ(goto) => GOTO(code2.get(&goto).unwrap().0),
|
|
||||||
IF_ACMPNE(goto) => GOTO(code2.get(&goto).unwrap().0),
|
|
||||||
//TODO more jump instructions
|
//TODO more jump instructions
|
||||||
_ => opcode,
|
_ => opcode
|
||||||
})
|
}
|
||||||
.collect()
|
).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode {
|
fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode {
|
||||||
|
|
@ -173,8 +162,6 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode {
|
||||||
121 => LSHL,
|
121 => LSHL,
|
||||||
122 => ISHR,
|
122 => ISHR,
|
||||||
123 => LSHR,
|
123 => LSHR,
|
||||||
124 => IUSHR,
|
|
||||||
125 => LUSHR,
|
|
||||||
126 => IAND,
|
126 => IAND,
|
||||||
127 => LAND,
|
127 => LAND,
|
||||||
128 => IOR,
|
128 => IOR,
|
||||||
|
|
@ -202,27 +189,26 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode {
|
||||||
150 => FCMPG,
|
150 => FCMPG,
|
||||||
151 => DCMPL,
|
151 => DCMPL,
|
||||||
152 => DCMPG,
|
152 => DCMPG,
|
||||||
153 => IFEQ(offset(opcodes, c)),
|
153 => IFEQ(read_u16(opcodes, c)),
|
||||||
154 => IFNE(offset(opcodes, c)),
|
154 => IFNE(read_u16(opcodes, c)),
|
||||||
155 => IFLT(offset(opcodes, c)),
|
155 => IFLT(read_u16(opcodes, c)),
|
||||||
156 => IFGE(offset(opcodes, c)),
|
156 => IFGE(read_u16(opcodes, c)),
|
||||||
157 => IFGT(offset(opcodes, c)),
|
157 => IFGT(read_u16(opcodes, c)),
|
||||||
158 => IFLE(offset(opcodes, c)),
|
158 => IFLE(read_u16(opcodes, c)),
|
||||||
159 => IF_ICMPEQ(offset(opcodes, c)),
|
159 => IF_ICMPEQ(read_u16(opcodes, c)),
|
||||||
160 => IF_ICMPNE(offset(opcodes, c)),
|
160 => IF_ICMPNE(read_u16(opcodes, c)),
|
||||||
161 => IF_ICMPLT(offset(opcodes, c)),
|
161 => IF_ICMPLT(read_u16(opcodes, c)),
|
||||||
162 => IF_ICMPGE(offset(opcodes, c)),
|
162 => IF_ICMPGE(read_u16(opcodes, c)),
|
||||||
163 => IF_ICMPGT(offset(opcodes, c)),
|
163 => IF_ICMPGT(read_u16(opcodes, c)),
|
||||||
164 => IF_ICMPLE(offset(opcodes, c)),
|
164 => IF_ICMPLE(read_u16(opcodes, c)),
|
||||||
165 => IF_ACMPEQ(offset(opcodes, c)),
|
165 => IF_ACMPEQ(read_u16(opcodes, c)),
|
||||||
166 => IF_ACMPNE(offset(opcodes, c)),
|
166 => IF_ACMPNE(read_u16(opcodes, c)),
|
||||||
167 => GOTO(offset(opcodes, c)),
|
167 => GOTO(read_u16(opcodes, c)),
|
||||||
168 => JSR(offset(opcodes, c)),
|
168 => JSR(read_u16(opcodes, c)),
|
||||||
169 => RET(read_u8(opcodes, c)),
|
169 => RET(read_u8(opcodes, c)),
|
||||||
170 => TABLESWITCH(read_tableswitch(opcodes, c)),
|
170 => TABLESWITCH(read_tableswitch(opcodes, c)),
|
||||||
171 => LOOKUPSWITCH(read_lookupswitch(opcodes, c)),
|
171 => LOOKUPSWITCH(read_lookupswitch(opcodes, c)),
|
||||||
172 => IRETURN,
|
172 => IRETURN,
|
||||||
173 => LRETURN,
|
|
||||||
174 => FRETURN,
|
174 => FRETURN,
|
||||||
175 => DRETURN,
|
175 => DRETURN,
|
||||||
176 => ARETURN,
|
176 => ARETURN,
|
||||||
|
|
@ -256,19 +242,21 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode {
|
||||||
195 => MONITOREXIT,
|
195 => MONITOREXIT,
|
||||||
196 => WIDE(Box::new(read_wide_opcode(opcodes, c))),
|
196 => WIDE(Box::new(read_wide_opcode(opcodes, c))),
|
||||||
197 => MULTIANEWARRAY(read_u16(opcodes, c), read_u8(opcodes, c)),
|
197 => MULTIANEWARRAY(read_u16(opcodes, c), read_u8(opcodes, c)),
|
||||||
198 => IFNULL(offset(opcodes, c)),
|
198 => {
|
||||||
199 => IFNONNULL(offset(opcodes, c)),
|
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)),
|
200 => GOTOW(read_i32(opcodes, c)),
|
||||||
201 => JSR_W(read_i32(opcodes, c)),
|
201 => JSR_W(read_i32(opcodes, c)),
|
||||||
|
|
||||||
|
|
||||||
_ => panic!("{}", opcode_u8),
|
_ => panic!("{}", opcode_u8),
|
||||||
};
|
};
|
||||||
// debug!("{}: {:?}", c, opcode);
|
|
||||||
opcode
|
opcode
|
||||||
}
|
}
|
||||||
|
|
||||||
fn offset(opcodes: &[u8], c: &mut usize) -> u16 {
|
|
||||||
let j = read_i16(opcodes, c);
|
|
||||||
// debug!("JUMP TO {} + {}",c, j);
|
|
||||||
(*c as i16 + j - 3) as u16
|
|
||||||
}
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
use std::fs::{self};
|
use std::fs::{self};
|
||||||
|
|
||||||
|
use anyhow::{anyhow, Error};
|
||||||
use crate::vm::opcodes::Opcode;
|
use crate::vm::opcodes::Opcode;
|
||||||
use crate::vm::opcodes::Opcode::*;
|
use crate::vm::opcodes::Opcode::*;
|
||||||
use anyhow::{anyhow, Error};
|
|
||||||
|
|
||||||
#[cfg(target_family = "unix")]
|
#[cfg(target_family = "unix")]
|
||||||
pub const PATH_SEPARATOR: char = ':';
|
pub const PATH_SEPARATOR: char = ':';
|
||||||
|
|
@ -17,6 +17,7 @@ pub const PATH_SEPARATOR: char = ';';
|
||||||
/// * [jar/zip]#[package_path]/[class].class
|
/// * [jar/zip]#[package_path]/[class].class
|
||||||
/// * [dir]/[package_path]/[class].class
|
/// * [dir]/[package_path]/[class].class
|
||||||
pub fn find_class(classpath: &Vec<String>, class_name: &str) -> Result<String, Error> {
|
pub fn find_class(classpath: &Vec<String>, class_name: &str) -> Result<String, Error> {
|
||||||
|
let class_name = &class_name.to_owned().replace(".", "/");
|
||||||
if class_name.starts_with("java")
|
if class_name.starts_with("java")
|
||||||
|| class_name.starts_with("sun/")
|
|| class_name.starts_with("sun/")
|
||||||
|| class_name.starts_with("com/sun/")
|
|| class_name.starts_with("com/sun/")
|
||||||
|
|
@ -44,6 +45,7 @@ pub fn find_class(classpath: &Vec<String>, class_name: &str) -> Result<String, E
|
||||||
Err(anyhow!("Class not found {}", class_name))
|
Err(anyhow!("Class not found {}", class_name))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 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 {
|
||||||
|
|
@ -71,15 +73,6 @@ pub(crate) fn read_u16(data: &[u8], pos: &mut usize) -> u16 {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_i16(data: &[u8], pos: &mut usize) -> i16 {
|
|
||||||
*pos += 2;
|
|
||||||
i16::from_be_bytes(
|
|
||||||
data[*pos - 2..*pos]
|
|
||||||
.try_into()
|
|
||||||
.expect("slice with incorrect length"),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn read_i32(data: &[u8], pos: &mut usize) -> i32 {
|
pub(crate) fn read_i32(data: &[u8], pos: &mut usize) -> i32 {
|
||||||
*pos += 4;
|
*pos += 4;
|
||||||
i32::from_be_bytes(
|
i32::from_be_bytes(
|
||||||
|
|
@ -126,9 +119,8 @@ pub(crate) fn read_f64(data: &[u8], pos: &mut usize) -> f64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_tableswitch(data: &[u8], pos: &mut usize) -> Tableswitch {
|
pub(crate) fn read_tableswitch(data: &[u8], pos: &mut usize) -> Tableswitch {
|
||||||
while *pos % 4 != 0 {
|
while read_u8(data, pos) == 0 {}
|
||||||
*pos += 1;
|
*pos -= 1;
|
||||||
}
|
|
||||||
let default = read_i32(data, pos);
|
let default = read_i32(data, pos);
|
||||||
let low = read_i32(data, pos);
|
let low = read_i32(data, pos);
|
||||||
let high = read_i32(data, pos);
|
let high = read_i32(data, pos);
|
||||||
|
|
@ -136,28 +128,19 @@ pub(crate) fn read_tableswitch(data: &[u8], pos: &mut usize) -> Tableswitch {
|
||||||
for _ in low..=high {
|
for _ in low..=high {
|
||||||
offsets.push(read_i32(data, pos));
|
offsets.push(read_i32(data, pos));
|
||||||
}
|
}
|
||||||
Tableswitch {
|
Tableswitch { default, low, high, offsets }
|
||||||
default,
|
|
||||||
low,
|
|
||||||
high,
|
|
||||||
offsets,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_lookupswitch(data: &[u8], pos: &mut usize) -> Lookupswitch {
|
pub(crate) fn read_lookupswitch(data: &[u8], pos: &mut usize) -> Lookupswitch {
|
||||||
while *pos % 4 != 0 {
|
while read_u8(data, pos) == 0 {}
|
||||||
*pos += 1;
|
*pos -= 1;
|
||||||
}
|
|
||||||
let default = read_i32(data, pos);
|
let default = read_i32(data, pos);
|
||||||
let npairs = read_i32(data, pos);
|
let npairs = read_i32(data, pos);
|
||||||
let mut match_offset_pairs = vec![];
|
let mut match_offset_pairs = vec![];
|
||||||
for _ in 0..npairs {
|
for _ in 0..npairs {
|
||||||
match_offset_pairs.push((read_i32(data, pos), read_i32(data, pos)));
|
match_offset_pairs.push((read_i32(data, pos), read_i32(data, pos)));
|
||||||
}
|
}
|
||||||
Lookupswitch {
|
Lookupswitch { default, match_offset_pairs }
|
||||||
default,
|
|
||||||
match_offset_pairs,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn read_wide_opcode(data: &[u8], pos: &mut usize) -> Opcode {
|
pub(crate) fn read_wide_opcode(data: &[u8], pos: &mut usize) -> Opcode {
|
||||||
|
|
@ -178,9 +161,7 @@ pub(crate) fn read_wide_opcode(data: &[u8], pos: &mut usize) -> Opcode {
|
||||||
57 => WIDE_DSTORE(index),
|
57 => WIDE_DSTORE(index),
|
||||||
58 => WIDE_ASTORE(index),
|
58 => WIDE_ASTORE(index),
|
||||||
169 => WIDE_RET(index),
|
169 => WIDE_RET(index),
|
||||||
_ => {
|
_ => { unreachable!("unknown opcode for WIDE") }
|
||||||
unreachable!("unknown opcode for WIDE")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -7,17 +7,13 @@ use std::rc::Rc;
|
||||||
use anyhow::Error;
|
use anyhow::Error;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
||||||
use crate::classloader::classdef::{
|
use crate::classloader::io::{find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8};
|
||||||
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;
|
||||||
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::opcode_parser::parse_opcodes;
|
|
||||||
|
|
||||||
pub mod classdef;
|
pub mod classdef;
|
||||||
pub(crate) mod io;
|
pub(crate) mod io;
|
||||||
mod opcode_parser;
|
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);
|
||||||
|
|
@ -68,11 +64,7 @@ fn load_class(bytecode: Vec<u8>) -> Result<ClassDef, Error> {
|
||||||
let access_flags = read_u16(&bytecode, pos);
|
let access_flags = read_u16(&bytecode, pos);
|
||||||
let this_class = read_u16(&bytecode, pos);
|
let this_class = read_u16(&bytecode, pos);
|
||||||
let super_class = read_u16(&bytecode, pos);
|
let super_class = read_u16(&bytecode, pos);
|
||||||
let super_class = if super_class != 0 {
|
let super_class = if super_class != 0 { Some(super_class) } else { None };
|
||||||
Some(super_class)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
let interfaces_count = read_u16(&bytecode, pos);
|
let interfaces_count = read_u16(&bytecode, pos);
|
||||||
let mut interfaces = vec![];
|
let mut interfaces = vec![];
|
||||||
for _ in 0..interfaces_count {
|
for _ in 0..interfaces_count {
|
||||||
|
|
@ -104,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,7 +232,6 @@ fn read_method(
|
||||||
) -> Method {
|
) -> Method {
|
||||||
let access_flags = read_u16(bytecode, index);
|
let access_flags = read_u16(bytecode, index);
|
||||||
let name_index = read_u16(bytecode, index);
|
let name_index = read_u16(bytecode, index);
|
||||||
let name = constant_pool.get(&name_index);
|
|
||||||
let descriptor_index = read_u16(bytecode, index);
|
let descriptor_index = read_u16(bytecode, index);
|
||||||
let attributes_count = read_u16(bytecode, index);
|
let attributes_count = read_u16(bytecode, index);
|
||||||
|
|
||||||
|
|
@ -249,8 +242,9 @@ fn read_method(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let code = if let Some(AttributeType::Code(code)) = attributes.get("Code") {
|
let code =
|
||||||
parse_opcodes(&code.opcodes)
|
if let Some(AttributeType::Code(code)) = attributes.get("Code") {
|
||||||
|
parse_code(&code.opcodes)
|
||||||
} else{
|
} else{
|
||||||
vec![]
|
vec![]
|
||||||
};
|
};
|
||||||
|
|
@ -261,7 +255,7 @@ fn read_method(
|
||||||
name_index,
|
name_index,
|
||||||
descriptor_index,
|
descriptor_index,
|
||||||
attributes,
|
attributes,
|
||||||
code,
|
code
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -332,3 +326,4 @@ fn read_attribute(
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,13 +8,12 @@ use once_cell::sync::Lazy;
|
||||||
use crate::class::{Class, ClassId, TypeIndex};
|
use crate::class::{Class, ClassId, TypeIndex};
|
||||||
use crate::classloader;
|
use crate::classloader;
|
||||||
use crate::classloader::classdef::{ClassDef, Method, Modifier};
|
use crate::classloader::classdef::{ClassDef, Method, Modifier};
|
||||||
|
use crate::vm::object::{Object, ObjectRef};
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::value::Value::*;
|
use crate::value::Value::*;
|
||||||
use crate::vm::object::{Object, ObjectRef};
|
|
||||||
use crate::vm::runtime::Vm;
|
use crate::vm::runtime::Vm;
|
||||||
|
|
||||||
static PRIMITIVES: Lazy<Vec<&str>> =
|
static PRIMITIVES: Lazy<Vec<&str>> = Lazy::new(|| vec!["B", "S", "I", "J", "F", "D", "Z", "J", "C"]);
|
||||||
Lazy::new(|| vec!["B", "S", "I", "J", "F", "D", "Z", "J", "C"]);
|
|
||||||
|
|
||||||
//TODO less pubs
|
//TODO less pubs
|
||||||
pub struct ClassManager {
|
pub struct ClassManager {
|
||||||
|
|
@ -100,11 +99,11 @@ impl ClassManager {
|
||||||
if !PRIMITIVES.contains(&type_name.as_str()) {
|
if !PRIMITIVES.contains(&type_name.as_str()) {
|
||||||
type_name = type_name[1..type_name.len()].to_owned();
|
type_name = type_name[1..type_name.len()].to_owned();
|
||||||
}
|
}
|
||||||
let id = self.get_or_new_id(name.into());
|
let id = self.get_or_new_id(name);
|
||||||
if !self.class_objects.contains_key(&id) {
|
if !self.class_objects.contains_key(&id) {
|
||||||
let cls = self.get_class_by_name("java/lang/Class").unwrap();
|
let cls = self.get_class_by_name("java/lang/Class").unwrap();
|
||||||
let mut instance = Object::new(cls);
|
let mut instance = Object::new(cls);
|
||||||
instance.set(cls, "java/lang/Class", "name", Utf8(name.into()));
|
instance.set(cls, "java/lang/Class", "name", Value::Utf8(name.into()));
|
||||||
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
|
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
|
||||||
|
|
||||||
self.class_objects.insert(id, instance);
|
self.class_objects.insert(id, instance);
|
||||||
|
|
@ -113,11 +112,9 @@ impl ClassManager {
|
||||||
// in cache?
|
// in cache?
|
||||||
let id = self.names.get(&type_name);
|
let id = self.names.get(&type_name);
|
||||||
match id {
|
match id {
|
||||||
Some(id) => {
|
Some(id) => if self.classes.get(id).is_none() {
|
||||||
if self.classes.get(id).is_none() {
|
|
||||||
self.add_class(&type_name);
|
self.add_class(&type_name);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
None => {
|
None => {
|
||||||
self.add_class(&type_name);
|
self.add_class(&type_name);
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +135,6 @@ impl ClassManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_class_by_name(&self, name: &str) -> Option<&Class> {
|
pub fn get_class_by_name(&self, name: &str) -> Option<&Class> {
|
||||||
debug!("get class by name {}", name);
|
|
||||||
let id = self.names.get(name);
|
let id = self.names.get(name);
|
||||||
self.classes.get(id.unwrap())
|
self.classes.get(id.unwrap())
|
||||||
}
|
}
|
||||||
|
|
@ -170,62 +166,42 @@ impl ClassManager {
|
||||||
while let Some(c) = current_id {
|
while let Some(c) = current_id {
|
||||||
parents.push_front(current_id.unwrap());
|
parents.push_front(current_id.unwrap());
|
||||||
current_classdef = self.classdefs.get(&c).unwrap();
|
current_classdef = self.classdefs.get(&c).unwrap();
|
||||||
Self::add_fields_for_this_or_parents(
|
Self::add_fields_for_this_or_parents(&mut object_field_mapping, &mut static_field_mapping, object_field_map_index, static_field_map_index, current_classdef);
|
||||||
&mut object_field_mapping,
|
|
||||||
&mut static_field_mapping,
|
|
||||||
object_field_map_index,
|
|
||||||
static_field_map_index,
|
|
||||||
current_classdef,
|
|
||||||
);
|
|
||||||
|
|
||||||
current_id = current_classdef
|
current_id = current_classdef.super_class.as_ref()
|
||||||
.super_class
|
|
||||||
.as_ref()
|
|
||||||
.map(|i| current_classdef.cp_class_name(i).to_owned())
|
.map(|i| current_classdef.cp_class_name(i).to_owned())
|
||||||
.map(|n| *self.names.get(&n).unwrap());
|
.map(|n| *self.names.get(&n).unwrap());
|
||||||
}
|
}
|
||||||
|
|
||||||
//handrolled references to superclass and interfaces
|
//handrolled references to superclass and interfaces
|
||||||
let superclass_id = this_classdef
|
let superclass_id = this_classdef.super_class.as_ref()
|
||||||
.super_class
|
|
||||||
.as_ref()
|
|
||||||
.map(|i| this_classdef.cp_class_name(i).to_owned())
|
.map(|i| this_classdef.cp_class_name(i).to_owned())
|
||||||
.map(|n| *self.names.get(&n).unwrap());
|
.map(|n| *self.names.get(&n).unwrap());
|
||||||
|
|
||||||
let interface_ids: Vec<ClassId> = this_classdef
|
let interface_ids: Vec<ClassId> = this_classdef.interfaces.iter()
|
||||||
.interfaces
|
|
||||||
.iter()
|
|
||||||
.map(|i| this_classdef.cp_class_name(i).to_owned())
|
.map(|i| this_classdef.cp_class_name(i).to_owned())
|
||||||
.map(|n| *self.names.get(n.as_str()).unwrap())
|
.map(|n| *self.names.get(n.as_str()).unwrap())
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
let mut all_interfaces = Vec::new();
|
|
||||||
self.get_all_interfaces(&interface_ids, &mut all_interfaces);
|
|
||||||
|
|
||||||
// initial values for static fields (before static init)
|
// initial values for static fields (before static init)
|
||||||
self.static_class_data
|
self.static_class_data.insert(this_classid, Self::set_field_data(&static_field_mapping));
|
||||||
.insert(this_classid, Self::set_field_data(&static_field_mapping));
|
|
||||||
|
|
||||||
self.classes.insert(
|
self.classes.insert(this_classid, Class {
|
||||||
this_classid,
|
|
||||||
Class {
|
|
||||||
id: this_classid,
|
id: this_classid,
|
||||||
initialized: false,
|
initialized: false,
|
||||||
name: name.into(),
|
name: name.into(),
|
||||||
superclass: superclass_id,
|
superclass: superclass_id,
|
||||||
all_superclasses: parents,
|
parents,
|
||||||
interfaces: interface_ids,
|
interfaces: interface_ids,
|
||||||
all_interfaces,
|
|
||||||
object_field_mapping,
|
object_field_mapping,
|
||||||
static_field_mapping,
|
static_field_mapping,
|
||||||
},
|
});
|
||||||
);
|
|
||||||
|
|
||||||
// add a new Class instance
|
// add a new Class instance
|
||||||
if name != "java/lang/Class" {
|
if name != "java/lang/Class" {
|
||||||
let cls = self.get_class_by_name("java/lang/Class").unwrap();
|
let cls = self.get_class_by_name("java/lang/Class").unwrap();
|
||||||
let mut instance = Object::new(cls);
|
let mut instance = Object::new(cls);
|
||||||
instance.set(cls, "java/lang/Class", "name", Utf8(name.into()));
|
instance.set(cls, "java/lang/Class", "name", Value::Utf8(name.into()));
|
||||||
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
|
let instance = Ref(ObjectRef::Object(Rc::new(RefCell::new(instance))));
|
||||||
|
|
||||||
self.class_objects.insert(this_classid, instance);
|
self.class_objects.insert(this_classid, instance);
|
||||||
|
|
@ -239,38 +215,22 @@ impl ClassManager {
|
||||||
this_classid
|
this_classid
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_all_interfaces(&self, ids: &[ClassId], output: &mut Vec<ClassId>) {
|
|
||||||
for id in ids {
|
|
||||||
output.push(*id);
|
|
||||||
let interfaces = self.classes.get(id).map(|c| c.interfaces.to_vec());
|
|
||||||
if let Some(intf) = interfaces {
|
|
||||||
self.get_all_interfaces(&intf, output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// like described above
|
/// like described above
|
||||||
fn add_fields_for_this_or_parents(
|
fn add_fields_for_this_or_parents(object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
|
||||||
object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
|
|
||||||
static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
|
static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
|
||||||
object_field_map_index: &mut usize,
|
object_field_map_index: &mut usize,
|
||||||
static_field_map_index: &mut usize,
|
static_field_map_index: &mut usize,
|
||||||
current_classdef: &ClassDef,
|
current_classdef: &ClassDef) {
|
||||||
) {
|
|
||||||
let mut instance_field_mappings: HashMap<String, TypeIndex> = HashMap::new();
|
let mut instance_field_mappings: HashMap<String, TypeIndex> = HashMap::new();
|
||||||
let mut static_field_mappings: HashMap<String, TypeIndex> = HashMap::new();
|
let mut static_field_mappings: HashMap<String, TypeIndex> = HashMap::new();
|
||||||
for (field_name, field) in ¤t_classdef.fields {
|
for (field_name, field) in ¤t_classdef.fields {
|
||||||
if !field.is(Modifier::Static) {
|
if !field.is(Modifier::Static) {
|
||||||
instance_field_mappings.insert(
|
instance_field_mappings.insert(field_name.to_owned(),
|
||||||
field_name.to_owned(),
|
TypeIndex::new(field.type_of().to_owned(), *object_field_map_index));
|
||||||
TypeIndex::new(field.type_of().to_owned(), *object_field_map_index),
|
|
||||||
);
|
|
||||||
*object_field_map_index += 1;
|
*object_field_map_index += 1;
|
||||||
} else {
|
} else {
|
||||||
static_field_mappings.insert(
|
static_field_mappings.insert(field_name.to_owned(),
|
||||||
field_name.to_owned(),
|
TypeIndex::new(field.type_of().to_owned(), *static_field_map_index));
|
||||||
TypeIndex::new(field.type_of().to_owned(), *static_field_map_index),
|
|
||||||
);
|
|
||||||
*static_field_map_index += 1;
|
*static_field_map_index += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -292,28 +252,25 @@ 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, class_name: &str) -> (ClassId, Vec<String>) {
|
fn load_class_and_deps(&mut self, name: &str) -> (ClassId, Vec<String>) {
|
||||||
debug!("load {}", class_name);
|
debug!("load {}", name);
|
||||||
let class_name = class_name.to_owned().replace(".", "/");
|
let id = self.get_or_new_id(name);
|
||||||
let id = self.get_or_new_id(class_name.clone());
|
|
||||||
|
|
||||||
let classdef = self.classdefs.entry(id).or_insert_with(|| {
|
let classdef = self.classdefs
|
||||||
classloader::get_classdef(&self.classpath, class_name.as_str()).expect("ClassNotFound")
|
.entry(id)
|
||||||
});
|
.or_insert_with(|| classloader::get_classdef(&self.classpath, name).expect("ClassNotFound"));
|
||||||
(id, inspect_dependencies(classdef))
|
(id, inspect_dependencies(classdef))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_or_new_id(&mut self, name: String) -> ClassId {
|
fn get_or_new_id(&mut self, name: &str) -> ClassId {
|
||||||
let id = *self.names.entry(name).or_insert_with(|| {
|
let id = *self.names.entry(name.to_string()).or_insert_with(|| {
|
||||||
self.current_id += 1;
|
self.current_id += 1;
|
||||||
self.current_id
|
self.current_id
|
||||||
});
|
});
|
||||||
id
|
id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn set_field_data(
|
pub(crate) fn set_field_data(field_mapping: &HashMap<String, HashMap<String, TypeIndex>>) -> Vec<Value> {
|
||||||
field_mapping: &HashMap<String, HashMap<String, TypeIndex>>,
|
|
||||||
) -> Vec<Value> {
|
|
||||||
let mut field_data = vec![Null; n_fields(field_mapping)];
|
let mut field_data = vec![Null; n_fields(field_mapping)];
|
||||||
|
|
||||||
for (_, this_class) in field_mapping {
|
for (_, this_class) in field_mapping {
|
||||||
|
|
@ -387,100 +344,27 @@ mod test {
|
||||||
|
|
||||||
// give class C a fields called value
|
// give class C a fields called value
|
||||||
let mut c_fields = HashMap::new();
|
let mut c_fields = HashMap::new();
|
||||||
c_fields.insert(
|
c_fields.insert("value1".to_owned(), Field::new(constant_pool.clone(), 0, 9, 5, HashMap::new(), 0));
|
||||||
"value1".to_owned(),
|
c_fields.insert("value2".to_owned(), Field::new(constant_pool.clone(), 0, 10, 5, HashMap::new(), 0));
|
||||||
Field::new(constant_pool.clone(), 0, 9, 5, HashMap::new(), 0),
|
|
||||||
);
|
|
||||||
c_fields.insert(
|
|
||||||
"value2".to_owned(),
|
|
||||||
Field::new(constant_pool.clone(), 0, 10, 5, HashMap::new(), 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Class needs a public (non-static) field called name
|
// Class needs a public (non-static) field called name
|
||||||
let mut class_fields = HashMap::new();
|
let mut class_fields = HashMap::new();
|
||||||
class_fields.insert(
|
class_fields.insert("name".to_owned(), Field::new(constant_pool.clone(), 1, 2, 5, HashMap::new(), 0));
|
||||||
"name".to_owned(),
|
|
||||||
Field::new(constant_pool.clone(), 1, 2, 5, HashMap::new(), 0),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut classdefs = HashMap::new();
|
let mut classdefs = HashMap::new();
|
||||||
classdefs.insert(
|
classdefs.insert(1, ClassDef::new(0, 0, constant_pool.clone(), 0, 0, None, vec![], c_fields, HashMap::new(), HashMap::new()));
|
||||||
1,
|
|
||||||
ClassDef::new(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
constant_pool.clone(),
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
None,
|
|
||||||
vec![],
|
|
||||||
c_fields,
|
|
||||||
HashMap::new(),
|
|
||||||
HashMap::new(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// preload java.lang.String
|
// preload java.lang.String
|
||||||
classdefs.insert(
|
classdefs.insert(2, ClassDef::new(0, 0, constant_pool.clone(), 0, 6, None, vec![], HashMap::new(), HashMap::new(), HashMap::new()));
|
||||||
2,
|
|
||||||
ClassDef::new(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
constant_pool.clone(),
|
|
||||||
0,
|
|
||||||
6,
|
|
||||||
None,
|
|
||||||
vec![],
|
|
||||||
HashMap::new(),
|
|
||||||
HashMap::new(),
|
|
||||||
HashMap::new(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
// preload java.lang.Class
|
// preload java.lang.Class
|
||||||
classdefs.insert(
|
classdefs.insert(3, ClassDef::new(0, 0, constant_pool, 0, 8, None, vec![], class_fields, HashMap::new(), HashMap::new()));
|
||||||
3,
|
|
||||||
ClassDef::new(
|
|
||||||
0,
|
|
||||||
0,
|
|
||||||
constant_pool,
|
|
||||||
0,
|
|
||||||
8,
|
|
||||||
None,
|
|
||||||
vec![],
|
|
||||||
class_fields,
|
|
||||||
HashMap::new(),
|
|
||||||
HashMap::new(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
let mut classes = HashMap::new();
|
let mut classes = HashMap::new();
|
||||||
let mut class_field_mapping = HashMap::new();
|
let mut class_field_mapping = HashMap::new();
|
||||||
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(
|
fields_declared_by_java_lang_class.insert("name".to_owned(), TypeIndex { type_name: "java/lang/String".into(), index: 0 });
|
||||||
"name".to_owned(),
|
class_field_mapping.insert("java/lang/Class".to_owned(), fields_declared_by_java_lang_class);
|
||||||
TypeIndex {
|
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() });
|
||||||
type_name: "java/lang/String".into(),
|
|
||||||
index: 0,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
class_field_mapping.insert(
|
|
||||||
"java/lang/Class".to_owned(),
|
|
||||||
fields_declared_by_java_lang_class,
|
|
||||||
);
|
|
||||||
classes.insert(
|
|
||||||
3,
|
|
||||||
Class {
|
|
||||||
id: 3,
|
|
||||||
initialized: false,
|
|
||||||
name: "".into(),
|
|
||||||
superclass: None,
|
|
||||||
all_superclasses: LinkedList::new(),
|
|
||||||
interfaces: vec![],
|
|
||||||
all_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(),
|
||||||
|
|
@ -495,25 +379,7 @@ mod test {
|
||||||
let c_id = cm.add_class("C");
|
let c_id = cm.add_class("C");
|
||||||
let loaded_class = cm.classes.get(&c_id).unwrap();
|
let loaded_class = cm.classes.get(&c_id).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(0, loaded_class.object_field_mapping.get("C").unwrap().get("value1").unwrap().index);
|
||||||
0,
|
assert_eq!(1, loaded_class.object_field_mapping.get("C").unwrap().get("value2").unwrap().index);
|
||||||
loaded_class
|
|
||||||
.object_field_mapping
|
|
||||||
.get("C")
|
|
||||||
.unwrap()
|
|
||||||
.get("value1")
|
|
||||||
.unwrap()
|
|
||||||
.index
|
|
||||||
);
|
|
||||||
assert_eq!(
|
|
||||||
1,
|
|
||||||
loaded_class
|
|
||||||
.object_field_mapping
|
|
||||||
.get("C")
|
|
||||||
.unwrap()
|
|
||||||
.get("value2")
|
|
||||||
.unwrap()
|
|
||||||
.index
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
mod class;
|
|
||||||
pub mod classloader;
|
pub mod classloader;
|
||||||
pub mod classmanager;
|
pub mod classmanager;
|
||||||
mod value;
|
mod value;
|
||||||
|
mod class;
|
||||||
pub mod vm;
|
pub mod vm;
|
||||||
|
|
@ -1,10 +1,7 @@
|
||||||
use java_rs::vm::runtime::Vm;
|
use java_rs::vm::runtime::Vm;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut vm = Vm::new();
|
let vm = Vm::new();
|
||||||
vm.run(
|
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Main", "main([Ljava/lang/String;)V");
|
||||||
"/Users/Shautvast/dev/java.rs/tests",
|
|
||||||
"testclasses/Main",
|
|
||||||
"main([Ljava/lang/String;)V",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
61
src/value.rs
61
src/value.rs
|
|
@ -1,5 +1,4 @@
|
||||||
use crate::vm::object::ObjectRef;
|
use crate::vm::object::ObjectRef;
|
||||||
use std::ops::{Add, AddAssign};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub enum Value {
|
pub enum Value {
|
||||||
|
|
@ -13,36 +12,14 @@ pub enum Value {
|
||||||
F32(f32),
|
F32(f32),
|
||||||
F64(f64),
|
F64(f64),
|
||||||
BOOL(bool),
|
BOOL(bool),
|
||||||
CHAR(i32),
|
CHAR(char),
|
||||||
// objects and arrays
|
// objects and arrays
|
||||||
Ref(ObjectRef),
|
Ref(ObjectRef),
|
||||||
// special object
|
// special object
|
||||||
Utf8(String),
|
Utf8(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum ComputationalType {
|
|
||||||
C1 = 1,
|
|
||||||
C2 = 2,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Value {
|
impl Value {
|
||||||
pub fn category_as_u8(&self) -> u8 {
|
|
||||||
self.category() as u8
|
|
||||||
}
|
|
||||||
pub fn category(&self) -> ComputationalType {
|
|
||||||
match self {
|
|
||||||
Value::Void
|
|
||||||
| Value::Null
|
|
||||||
| Value::I32(_)
|
|
||||||
| Value::F32(_)
|
|
||||||
| Value::BOOL(_)
|
|
||||||
| Value::CHAR(_)
|
|
||||||
| Value::Ref(_)
|
|
||||||
| Value::Utf8(_) => ComputationalType::C1,
|
|
||||||
Value::I64(_) | Value::F64(_) => ComputationalType::C2,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// panics if not correct type
|
// panics if not correct type
|
||||||
pub fn into_i32(self) -> i32 {
|
pub fn into_i32(self) -> i32 {
|
||||||
if let Value::I32(v) = self {
|
if let Value::I32(v) = self {
|
||||||
|
|
@ -52,42 +29,8 @@ impl Value {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_i64(self) -> i64 {
|
|
||||||
if let Value::I64(v) = self {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
panic!("{:?} is not I64", self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_f32(self) -> f32 {
|
|
||||||
if let Value::F32(v) = self {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
panic!("{:?} is not F32", self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_f64(self) -> f64 {
|
|
||||||
if let Value::F64(v) = self {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
panic!("{:?} is not F64", self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_object(self) -> ObjectRef {
|
pub fn into_object(self) -> ObjectRef {
|
||||||
if let Value::Null = self {
|
if let Value::Ref(v) = self {
|
||||||
ObjectRef::Null
|
|
||||||
} else if let Value::Ref(v) = self {
|
|
||||||
v
|
|
||||||
} else {
|
|
||||||
panic!("{:?}", self);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn into_utf8(self) -> String {
|
|
||||||
if let Value::Utf8(v) = self {
|
|
||||||
v
|
v
|
||||||
} else {
|
} else {
|
||||||
panic!();
|
panic!();
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,15 @@
|
||||||
use crate::vm::object::ObjectRef::*;
|
|
||||||
use anyhow::{anyhow, Error};
|
use anyhow::{anyhow, Error};
|
||||||
|
use crate::vm::object::ObjectRef::*;
|
||||||
|
|
||||||
use crate::value::Value;
|
use crate::value::Value;
|
||||||
use crate::value::Value::*;
|
use crate::value::Value::*;
|
||||||
use crate::vm::object::ObjectRef;
|
|
||||||
|
|
||||||
pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error> {
|
pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error> {
|
||||||
if let I32(index) = index {
|
if let I32(index) = index {
|
||||||
let index = index as usize;
|
let index = index as usize;
|
||||||
|
|
||||||
if let Value::Null = arrayref {
|
if let Null = arrayref {
|
||||||
return Err(anyhow!("NullpointerException"));
|
return Err(anyhow!("NullpointerException"));
|
||||||
}
|
}
|
||||||
if let Ref(objectref) = arrayref {
|
if let Ref(objectref) = arrayref {
|
||||||
|
|
@ -50,9 +50,6 @@ pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error>
|
||||||
Object(_) => {
|
Object(_) => {
|
||||||
panic!("should be array")
|
panic!("should be array")
|
||||||
} //throw error?
|
} //throw error?
|
||||||
ObjectRef::Null => {
|
|
||||||
return Ok(Value::Null);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -60,7 +57,7 @@ pub(crate) fn array_load(index: Value, arrayref: Value) -> Result<Value, Error>
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result<(), Error> {
|
pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result<(), Error> {
|
||||||
if let Value::Null = arrayref {
|
if let Null = arrayref {
|
||||||
return Err(anyhow!("NullpointerException"));
|
return Err(anyhow!("NullpointerException"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -97,9 +94,9 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
CharArray(ref mut array) => {
|
CharArray(ref mut array) => unsafe {
|
||||||
if let I32(value) = value {
|
if let I32(value) = value {
|
||||||
array[index as usize] = value
|
array[index as usize] = char::from_u32_unchecked(value as u32);
|
||||||
} else {
|
} else {
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
@ -139,7 +136,7 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Object(_) | Class(_) | ObjectRef::Null => {} //throw error?
|
Object(_) | Class(_) => {} //throw error?
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
mod array;
|
mod array;
|
||||||
mod native;
|
|
||||||
pub(crate) mod object;
|
pub(crate) mod object;
|
||||||
pub(crate) mod opcodes;
|
pub(crate) mod opcodes;
|
||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
243
src/vm/native.rs
243
src/vm/native.rs
|
|
@ -1,243 +0,0 @@
|
||||||
#![allow(non_snake_case)]
|
|
||||||
|
|
||||||
use std::cell::RefCell;
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use anyhow::Error;
|
|
||||||
use log::debug;
|
|
||||||
use once_cell::sync::Lazy;
|
|
||||||
|
|
||||||
use crate::classmanager::ClassManager;
|
|
||||||
use crate::value::Value;
|
|
||||||
use crate::value::Value::{Utf8, Void, I32};
|
|
||||||
use crate::vm::object::ObjectRef::Object;
|
|
||||||
use crate::vm::object::{self, Array, ObjectRef};
|
|
||||||
use crate::vm::runtime::Stackframe;
|
|
||||||
|
|
||||||
const primitive_name_classes: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
|
|
||||||
let mut mapping = HashMap::new();
|
|
||||||
mapping.insert("B", "byte");
|
|
||||||
mapping.insert("S", "short");
|
|
||||||
mapping.insert("I", "int");
|
|
||||||
mapping.insert("J", "long");
|
|
||||||
mapping.insert("F", "float");
|
|
||||||
mapping.insert("D", "double");
|
|
||||||
mapping.insert("C", "char");
|
|
||||||
mapping.insert("Z", "boolean");
|
|
||||||
mapping
|
|
||||||
});
|
|
||||||
static mut PRIMITIVE_CLASSES: Lazy<HashMap<String, Value>> = Lazy::new(|| HashMap::new());
|
|
||||||
|
|
||||||
pub fn invoke_native(
|
|
||||||
class_manager: &mut ClassManager,
|
|
||||||
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(class_manager, method_name, args),
|
|
||||||
"java/lang/Object" => java_lang_Object(method_name, args),
|
|
||||||
"java/lang/System" => java_lang_System(method_name),
|
|
||||||
"jdk/internal/misc/Unsafe" => jdk_internal_misc_Unsafe(method_name),
|
|
||||||
"jdk/internal/util/SystemProps$Raw" => {
|
|
||||||
jdk_internal_util_SystemProps_Raw(class_manager, method_name)
|
|
||||||
}
|
|
||||||
_ => unimplemented!("{}", class_name),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn java_lang_Class(
|
|
||||||
class_manager: &mut ClassManager,
|
|
||||||
method_name: &str,
|
|
||||||
args: Vec<Value>,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
Ok(match method_name {
|
|
||||||
"desiredAssertionStatus0(Ljava/lang/Class;)Z" => Value::BOOL(false),
|
|
||||||
"getPrimitiveClass(Ljava/lang/String;)Ljava/lang/Class;" => {
|
|
||||||
get_primitive_class(class_manager, args)
|
|
||||||
}
|
|
||||||
_ => Void,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn java_lang_Object(method_name: &str, args: Vec<Value>) -> Result<Value, Error> {
|
|
||||||
Ok(match method_name {
|
|
||||||
"hashCode()I" => {
|
|
||||||
if let Value::Ref(object_ref) = args.get(0).unwrap() {
|
|
||||||
if let Object(p) = object_ref {
|
|
||||||
I32(p.borrow().id as i32) // system hashcode
|
|
||||||
} else {
|
|
||||||
I32(0) //todo implement for arrays
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
unimplemented!("{}", method_name);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn java_lang_System(method_name: &str) -> Result<Value, Error> {
|
|
||||||
Ok(match method_name {
|
|
||||||
_ => Void,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_primitive_class(class_manager: &mut ClassManager, args: Vec<Value>) -> Value {
|
|
||||||
if let Utf8(primitive) = args.get(0).unwrap().to_owned() {
|
|
||||||
unsafe {
|
|
||||||
PRIMITIVE_CLASSES
|
|
||||||
.entry(primitive.clone())
|
|
||||||
.or_insert_with(|| {
|
|
||||||
let cls = class_manager.get_class_by_name("java/lang/Class").unwrap();
|
|
||||||
let mut instance = object::Object::new(cls);
|
|
||||||
instance.set(
|
|
||||||
cls,
|
|
||||||
"java/lang/Class",
|
|
||||||
primitive_name_classes.get(primitive.as_str()).unwrap(),
|
|
||||||
Utf8("name".into()),
|
|
||||||
);
|
|
||||||
Value::Ref(Object(Rc::new(RefCell::new(instance))))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Value::Null
|
|
||||||
}
|
|
||||||
|
|
||||||
fn jdk_internal_misc_Unsafe(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(
|
|
||||||
class_manager: &mut ClassManager,
|
|
||||||
method_name: &str,
|
|
||||||
) -> Result<Value, Error> {
|
|
||||||
match method_name {
|
|
||||||
"platformProperties()[Ljava/lang/String;" => platformProperties(),
|
|
||||||
"cmdProperties()Ljava/util/HashMap;" => cmdProps(class_manager), //TODO ability to instantiate classes here
|
|
||||||
"vmProperties()[Ljava/lang/String;" => vmProperties(),
|
|
||||||
_ => Ok(Void),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cmdProps(class_manager: &mut ClassManager) -> Result<Value, Error> {
|
|
||||||
class_manager.load_class_by_name("java/util/HashMap");
|
|
||||||
let hashmap_class = class_manager
|
|
||||||
.get_class_by_name("java/util/HashMap")
|
|
||||||
.unwrap();
|
|
||||||
let hashmap = Value::Ref(Object(Rc::new(RefCell::new(object::Object::new(
|
|
||||||
hashmap_class,
|
|
||||||
))))); // this is convoluted
|
|
||||||
Stackframe::new(vec![hashmap.clone()]).run(class_manager, hashmap_class.id, "<init>()V");
|
|
||||||
Ok(hashmap)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn vmProperties() -> 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(Array::from_vec(
|
|
||||||
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(Array::from_vec(
|
|
||||||
props.to_vec(),
|
|
||||||
))))
|
|
||||||
}
|
|
||||||
152
src/vm/object.rs
152
src/vm/object.rs
|
|
@ -1,100 +1,25 @@
|
||||||
use crate::class::{Class, ClassId};
|
use std::cell::RefCell;
|
||||||
use crate::value::Value;
|
use std::rc::Rc;
|
||||||
use crate::vm::object::ObjectRef::*;
|
|
||||||
use log::debug;
|
use log::debug;
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use std::cell::RefCell;
|
use crate::class::{Class, ClassId};
|
||||||
use std::fmt::{Debug, Formatter, Pointer};
|
use crate::vm::object::ObjectRef::*;
|
||||||
use std::rc::Rc;
|
use crate::value::Value;
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct Array<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
id: u32,
|
|
||||||
data: Vec<T>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Array<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
pub fn new(init: T, size: usize) -> Self {
|
|
||||||
Self {
|
|
||||||
id: random::<u32>() >> 1,
|
|
||||||
data: vec![init; size],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn from_vec(vec: Vec<T>) -> Self {
|
|
||||||
Self {
|
|
||||||
id: random::<u32>() >> 1,
|
|
||||||
data: vec,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn len(&self) -> usize {
|
|
||||||
self.data.len()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::Index<usize> for Array<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
type Output = T;
|
|
||||||
|
|
||||||
fn index(&self, index: usize) -> &Self::Output {
|
|
||||||
&self.data[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> std::ops::IndexMut<usize> for Array<T>
|
|
||||||
where
|
|
||||||
T: Clone,
|
|
||||||
{
|
|
||||||
fn index_mut(&mut self, index: usize) -> &mut Self::Output {
|
|
||||||
&mut self.data[index]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
pub enum ObjectRef {
|
pub enum ObjectRef {
|
||||||
ByteArray(Array<i8>),
|
ByteArray(Vec<i8>),
|
||||||
//maybe use arrays?
|
ShortArray(Vec<i16>),
|
||||||
ShortArray(Array<i16>),
|
IntArray(Vec<i32>),
|
||||||
IntArray(Array<i32>),
|
LongArray(Vec<i64>),
|
||||||
LongArray(Array<i64>),
|
FloatArray(Vec<f32>),
|
||||||
FloatArray(Array<f32>),
|
DoubleArray(Vec<f64>),
|
||||||
DoubleArray(Array<f64>),
|
BooleanArray(Vec<bool>),
|
||||||
BooleanArray(Array<bool>),
|
CharArray(Vec<char>),
|
||||||
CharArray(Array<i32>),
|
StringArray(Vec<String>),
|
||||||
StringArray(Array<String>),
|
|
||||||
ObjectArray(ClassId, Vec<ObjectRef>),
|
ObjectArray(ClassId, Vec<ObjectRef>),
|
||||||
Object(Rc<RefCell<Object>>),
|
Object(Rc<RefCell<Object>>),
|
||||||
Class(Class),
|
Class(Class),
|
||||||
Null,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Debug for ObjectRef {
|
|
||||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
||||||
let name = match self {
|
|
||||||
ByteArray(a) => format!("[B;{}", a.len()),
|
|
||||||
ShortArray(a) => format!("[S;{}", a.len()),
|
|
||||||
IntArray(a) => format!("[I;{}", a.len()),
|
|
||||||
LongArray(a) => format!("[J;{}", a.len()),
|
|
||||||
FloatArray(a) => format!("[F;{}", a.len()),
|
|
||||||
DoubleArray(a) => format!("[D;{}", a.len()),
|
|
||||||
BooleanArray(a) => format!("[Z;{}", a.len()),
|
|
||||||
CharArray(a) => format!("[C;{}", a.len()),
|
|
||||||
StringArray(a) => format!("[Ljava/lang/String;{}]", a.len()),
|
|
||||||
ObjectArray(_, a) => format!("[[Lx;{}]", a.len()),
|
|
||||||
Object(_) => "L".into(),
|
|
||||||
Class(_) => "Class".into(),
|
|
||||||
ObjectRef::Null => "null".into(),
|
|
||||||
};
|
|
||||||
write!(f, "{}", name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ObjectRef {
|
impl ObjectRef {
|
||||||
|
|
@ -110,7 +35,7 @@ impl ObjectRef {
|
||||||
CharArray(d) => d.len(),
|
CharArray(d) => d.len(),
|
||||||
StringArray(d) => d.len(),
|
StringArray(d) => d.len(),
|
||||||
ObjectArray(_, d) => d.len(),
|
ObjectArray(_, d) => d.len(),
|
||||||
_ => unreachable!("not an array {:?}", self),
|
_ => unreachable!("not an array {:?}", self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -128,29 +53,29 @@ pub enum ArrayType {
|
||||||
|
|
||||||
impl ObjectRef {
|
impl ObjectRef {
|
||||||
pub fn new_object_array(class: &Class, size: usize) -> Self {
|
pub fn new_object_array(class: &Class, size: usize) -> Self {
|
||||||
ObjectArray(class.id, vec![ObjectRef::Null; size])
|
ObjectArray(class.id, Vec::with_capacity(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_array(arraytype: u8, size: usize) -> Self {
|
pub fn new_array(arraytype: u8, size: usize) -> Self {
|
||||||
match arraytype {
|
match arraytype {
|
||||||
8 => ByteArray(Array::new(0, size)),
|
8 => ByteArray(Vec::with_capacity(size)),
|
||||||
9 => ShortArray(Array::new(0, size)),
|
9 => ShortArray(Vec::with_capacity(size)),
|
||||||
10 => IntArray(Array::new(0, size)),
|
10 => IntArray(Vec::with_capacity(size)),
|
||||||
11 => LongArray(Array::new(0, size)),
|
11 => LongArray(Vec::with_capacity(size)),
|
||||||
6 => FloatArray(Array::new(0_f32, size)),
|
6 => FloatArray(Vec::with_capacity(size)),
|
||||||
7 => DoubleArray(Array::new(0_f64, size)),
|
7 => DoubleArray(Vec::with_capacity(size)),
|
||||||
4 => BooleanArray(Array::new(false, size)),
|
4 => BooleanArray(Vec::with_capacity(size)),
|
||||||
5 => CharArray(Array::new(0, size)),
|
5 => CharArray(Vec::with_capacity(size)),
|
||||||
_ => unreachable!("impossible array type"),
|
_ => unreachable!("impossible array type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_int_array(size: usize) -> Self {
|
pub fn new_int_array(size: usize) -> Self {
|
||||||
IntArray(Array::new(0, size))
|
IntArray(Vec::with_capacity(size))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_byte_array(d: Vec<u8>) -> Self {
|
pub fn new_byte_array(d: Vec<u8>) -> Self {
|
||||||
ByteArray(Array::from_vec(into_vec_i8(d)))
|
ByteArray(into_vec_i8(d))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -176,18 +101,12 @@ pub struct Object {
|
||||||
pub data: Vec<Value>,
|
pub data: Vec<Value>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PartialEq for Object {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.id == other.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// object, not array
|
// object, not array
|
||||||
impl Object {
|
impl Object {
|
||||||
pub fn new(class: &Class) -> Self {
|
pub fn new(class: &Class) -> Self {
|
||||||
let instance_data = Object::init_fields(class);
|
let instance_data = Object::init_fields(class);
|
||||||
Self {
|
Self {
|
||||||
id: random::<u32>() >> 1,
|
id: random(),
|
||||||
class_id: class.id,
|
class_id: class.id,
|
||||||
data: instance_data,
|
data: instance_data,
|
||||||
}
|
}
|
||||||
|
|
@ -216,13 +135,7 @@ impl Object {
|
||||||
field_data
|
field_data
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set(
|
pub fn set(&mut self, runtime_type: &Class, declared_type: &str, field_name: &str, value: Value) {
|
||||||
&mut self,
|
|
||||||
runtime_type: &Class,
|
|
||||||
declared_type: &str,
|
|
||||||
field_name: &str,
|
|
||||||
value: Value,
|
|
||||||
) {
|
|
||||||
debug!("set {:?}.{}", runtime_type.name, field_name);
|
debug!("set {:?}.{}", runtime_type.name, field_name);
|
||||||
let type_index = runtime_type
|
let type_index = runtime_type
|
||||||
.object_field_mapping
|
.object_field_mapping
|
||||||
|
|
@ -240,10 +153,7 @@ impl Object {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.get(field_name)
|
.get(field_name)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
debug!(
|
debug!("get {:?}:{}.{}:{} @{}", runtime_type, declared_type, field_name, type_index.type_name, type_index.index);
|
||||||
"get {:?}:{}.{}:{} @{}",
|
|
||||||
runtime_type, declared_type, field_name, type_index.type_name, type_index.index
|
|
||||||
);
|
|
||||||
debug!("from data {:?}", self.data);
|
debug!("from data {:?}", self.data);
|
||||||
self.data.get(type_index.index).unwrap()
|
self.data.get(type_index.index).unwrap()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -85,8 +85,6 @@ pub(crate) enum Opcode {
|
||||||
LSHL,
|
LSHL,
|
||||||
ISHR,
|
ISHR,
|
||||||
LSHR,
|
LSHR,
|
||||||
IUSHR,
|
|
||||||
LUSHR,
|
|
||||||
IAND,
|
IAND,
|
||||||
LAND,
|
LAND,
|
||||||
IOR,
|
IOR,
|
||||||
|
|
@ -136,7 +134,6 @@ pub(crate) enum Opcode {
|
||||||
TABLESWITCH(Tableswitch),
|
TABLESWITCH(Tableswitch),
|
||||||
LOOKUPSWITCH(Lookupswitch),
|
LOOKUPSWITCH(Lookupswitch),
|
||||||
IRETURN,
|
IRETURN,
|
||||||
LRETURN,
|
|
||||||
FRETURN,
|
FRETURN,
|
||||||
DRETURN,
|
DRETURN,
|
||||||
ARETURN,
|
ARETURN,
|
||||||
|
|
@ -165,4 +162,7 @@ pub(crate) enum Opcode {
|
||||||
IFNONNULL(u16),
|
IFNONNULL(u16),
|
||||||
GOTOW(i32),
|
GOTOW(i32),
|
||||||
JSR_W(i32),
|
JSR_W(i32),
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load diff
|
|
@ -1,23 +1,18 @@
|
||||||
mod test {
|
mod test {
|
||||||
use java_rs::vm::runtime::Vm;
|
use java_rs::vm::runtime::Vm;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn if_cmp() {
|
fn if_cmp() {
|
||||||
let vm = Vm::new();
|
let vm = Vm::new();
|
||||||
vm.run(
|
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.IfCmp", "i_is_1()Z");
|
||||||
"/Users/Shautvast/dev/java/tests",
|
|
||||||
"testclasses.IfCmp",
|
|
||||||
"i_is_1()Z",
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn consts() {
|
fn consts() {
|
||||||
|
|
||||||
let vm = Vm::new();
|
let vm = Vm::new();
|
||||||
vm.run(
|
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Const", "hello()Ljava/lang/String;")
|
||||||
"/Users/Shautvast/dev/java/tests",
|
|
||||||
"testclasses.Const",
|
|
||||||
"hello()Ljava/lang/String;",
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
mod test {
|
mod test {
|
||||||
use java_rs::classloader::classdef::{Method, Modifier};
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
use java_rs::classloader::classdef::{Method, Modifier};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn access_flags() {
|
fn access_flags() {
|
||||||
|
|
@ -11,10 +11,13 @@ mod test {
|
||||||
0,
|
0,
|
||||||
0,
|
0,
|
||||||
HashMap::new(),
|
HashMap::new(),
|
||||||
vec![],
|
|
||||||
);
|
);
|
||||||
assert!(m.is(Modifier::Public));
|
assert!(m.is(Modifier::Public));
|
||||||
assert!(m.is(Modifier::Static));
|
assert!(m.is(Modifier::Static));
|
||||||
assert!(!m.is(Modifier::Private));
|
assert!(!m.is(Modifier::Private));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue