Compare commits

...

10 commits

Author SHA1 Message Date
Shautvast
ccf7426086 finally picked it up again, one bytecode at a time 2024-05-16 17:42:04 +02:00
Shautvast
c271881a79 busy implementing hashcode 2023-12-07 17:20:19 +01:00
Shautvast
b714827f24 bugfix increment needs to be i16 not u16 2023-12-07 15:27:30 +01:00
Shautvast
a7d772c093 added opcodes 2023-12-06 17:52:59 +01:00
Shautvast
2149c8d3de bugfix pop order 2023-12-06 14:39:33 +01:00
Shautvast
7b6a01dfda added more dups 2023-12-02 11:54:18 +01:00
Shautvast
bab0e0d8c2 some arithmetic and some bitwise opcodes 2023-12-02 09:28:17 +01:00
Shautvast
d9985df822 primitiveClasses, fix arrays 2023-11-27 22:12:31 +01:00
Shautvast
dae02cec32 fixed jumping 2023-11-26 19:08:52 +01:00
Shautvast
715fa29cfd almost there, it seems... 2023-11-26 15:57:54 +01:00
17 changed files with 1514 additions and 308 deletions

View file

@ -10,10 +10,7 @@ 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 { Self { type_name, index }
type_name,
index,
}
} }
} }
@ -23,14 +20,21 @@ pub struct Class {
pub initialized: bool, pub initialized: bool,
pub name: String, pub name: String,
pub superclass: Option<ClassId>, pub superclass: Option<ClassId>,
pub parents: LinkedList<ClassId>, pub all_superclasses: LinkedList<ClassId>, // all superclasses in a flat list
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 {
@ -40,4 +44,4 @@ impl Class {
.reduce(|acc, e| acc + e) .reduce(|acc, e| acc + e)
.unwrap_or(0) .unwrap_or(0)
} }
} }

View file

@ -21,7 +21,7 @@ pub(crate) struct ClassDef {
pub attributes: HashMap<String, AttributeType>, pub attributes: HashMap<String, AttributeType>,
} }
impl Debug for ClassDef{ impl Debug for ClassDef {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.cp_class_name(&self.this_class)) write!(f, "{}", self.cp_class_name(&self.this_class))
} }
@ -94,6 +94,7 @@ 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)
@ -318,13 +319,13 @@ impl Field {
} }
} }
pub(crate) struct Method { pub struct Method {
pub(crate) constant_pool: Rc<HashMap<u16, CpEntry>>, pub(crate) constant_pool: Rc<HashMap<u16, CpEntry>>,
access_flags: u16, pub 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>, pub(crate) code: Vec<Opcode>,
} }
impl Debug for Method { impl Debug for Method {

View file

@ -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,7 +17,6 @@ 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/")
@ -45,7 +44,6 @@ 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 {
@ -73,6 +71,15 @@ 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(
@ -119,8 +126,9 @@ 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 read_u8(data, pos) == 0 {} while *pos % 4 != 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);
@ -128,19 +136,28 @@ 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 { default, low, high, offsets } Tableswitch {
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 read_u8(data, pos) == 0 {} while *pos % 4 != 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 { default, match_offset_pairs } Lookupswitch {
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 {
@ -161,7 +178,9 @@ 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")
}
} }
} }
} }
@ -178,4 +197,4 @@ pub(crate) struct Tableswitch {
pub(crate) struct Lookupswitch { pub(crate) struct Lookupswitch {
default: i32, default: i32,
match_offset_pairs: Vec<(i32, i32)>, match_offset_pairs: Vec<(i32, i32)>,
} }

View file

@ -7,15 +7,19 @@ use std::rc::Rc;
use anyhow::Error; use anyhow::Error;
use log::debug; use log::debug;
use crate::classloader::io::{find_class, read_bytes, read_f32, read_f64, read_i32, read_i64, read_u16, read_u32, read_u8}; use crate::classloader::classdef::{
use crate::classloader::classdef::{AttributeType, ClassDef, CpEntry, Exception, Field, Method, MethodCode}; 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 code_parser; mod opcode_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);
let resolved_path = find_class(classpath, class_name)?; let resolved_path = find_class(classpath, class_name)?;
let bytecode = read_bytecode(resolved_path)?; let bytecode = read_bytecode(resolved_path)?;
@ -64,7 +68,11 @@ 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 { Some(super_class) } else { None }; let super_class = if super_class != 0 {
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 {
@ -96,8 +104,6 @@ fn load_class(bytecode: Vec<u8>) -> Result<ClassDef, Error> {
} }
} }
Ok(ClassDef::new( Ok(ClassDef::new(
minor_version, minor_version,
major_version, major_version,
@ -232,6 +238,7 @@ 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);
@ -242,10 +249,9 @@ fn read_method(
} }
} }
let code = let code = if let Some(AttributeType::Code(code)) = attributes.get("Code") {
if let Some(AttributeType::Code(code)) = attributes.get("Code") { parse_opcodes(&code.opcodes)
parse_code(&code.opcodes) } else {
} else{
vec![] vec![]
}; };
@ -255,7 +261,7 @@ fn read_method(
name_index, name_index,
descriptor_index, descriptor_index,
attributes, attributes,
code code,
) )
} }
@ -318,12 +324,11 @@ fn read_attribute(
"InnerClasses" => Some(("".into(), AttributeType::InnerClasses)), //stub "InnerClasses" => Some(("".into(), AttributeType::InnerClasses)), //stub
"Signature" => Some(("".into(), AttributeType::Signature)), //stub "Signature" => Some(("".into(), AttributeType::Signature)), //stub
"NestHost" => Some(("".into(), AttributeType::NestHost)), //stub "NestHost" => Some(("".into(), AttributeType::NestHost)), //stub
"EnclosingMethod" => Some(("".into(), AttributeType::EnclosingMethod)), //stub "EnclosingMethod" => Some(("".into(), AttributeType::EnclosingMethod)), //stub
"PermittedSubclasses" => Some(("".into(), AttributeType::PermittedSubclasses)), //stub "PermittedSubclasses" => Some(("".into(), AttributeType::PermittedSubclasses)), //stub
//TODO more actual attribute implementations //TODO more actual attribute implementations
_ => None, _ => None,
}; };
} }
None None
} }

View file

@ -1,39 +1,50 @@
use std::collections::HashMap;
use log::debug; use log::debug;
use std::collections::BTreeMap;
use crate::classloader::io::{read_i32, read_lookupswitch, read_tableswitch, read_u16, read_u8, read_wide_opcode}; use crate::classloader::io::{
read_i16, 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_code(opcodes: &[u8]) -> Vec<Opcode> { pub(crate) fn parse_opcodes(opcodes: &[u8]) -> Vec<Opcode> {
let mut code: HashMap<u16, (u16, Opcode)> = HashMap::new(); let mut code: BTreeMap<u16, (u16, Opcode)> = BTreeMap::new();
let mut c = 0; let mut c = 0;
let mut opcode_index: u16 = 0; let mut opcode_index: u16 = 0;
// debug!("len {:?}", opcodes.len()); let mut index_before_read: u16;
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(c as u16, (opcode_index, opcode)); code.insert(index_before_read, (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
code.into_iter().map(|(_, (_, opcode))| debug!("{:?}", code);
match opcode { code.into_iter()
IFNULL(goto) => { .map(|(_, (_, opcode))| match opcode {
debug!("goto {:?}", goto); IFNULL(goto) => IFNULL(code2.get(&goto).unwrap().0),
debug!("{:?}", code2); IFNONNULL(goto) => IFNONNULL(code2.get(&goto).unwrap().0),
IFNULL(code2.get(&goto).unwrap().0)
} IF_ICMPEQ(goto) => IF_ICMPEQ(code2.get(&goto).unwrap().0),
IFNONNULL(goto) => { IF_ICMPNE(goto) => IF_ICMPNE(code2.get(&goto).unwrap().0),
debug!("goto {:?}", &goto); IF_ICMPGT(goto) => IF_ICMPGT(code2.get(&goto).unwrap().0),
debug!("{:?}", code2); IF_ICMPGE(goto) => IF_ICMPGE(code2.get(&goto).unwrap().0),
IFNONNULL(code2.get(&goto).unwrap().0) IF_ICMPLT(goto) => IF_ICMPLT(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 {
@ -162,6 +173,8 @@ 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,
@ -189,26 +202,27 @@ fn get_opcode(opcodes: &[u8], c: &mut usize) -> Opcode {
150 => FCMPG, 150 => FCMPG,
151 => DCMPL, 151 => DCMPL,
152 => DCMPG, 152 => DCMPG,
153 => IFEQ(read_u16(opcodes, c)), 153 => IFEQ(offset(opcodes, c)),
154 => IFNE(read_u16(opcodes, c)), 154 => IFNE(offset(opcodes, c)),
155 => IFLT(read_u16(opcodes, c)), 155 => IFLT(offset(opcodes, c)),
156 => IFGE(read_u16(opcodes, c)), 156 => IFGE(offset(opcodes, c)),
157 => IFGT(read_u16(opcodes, c)), 157 => IFGT(offset(opcodes, c)),
158 => IFLE(read_u16(opcodes, c)), 158 => IFLE(offset(opcodes, c)),
159 => IF_ICMPEQ(read_u16(opcodes, c)), 159 => IF_ICMPEQ(offset(opcodes, c)),
160 => IF_ICMPNE(read_u16(opcodes, c)), 160 => IF_ICMPNE(offset(opcodes, c)),
161 => IF_ICMPLT(read_u16(opcodes, c)), 161 => IF_ICMPLT(offset(opcodes, c)),
162 => IF_ICMPGE(read_u16(opcodes, c)), 162 => IF_ICMPGE(offset(opcodes, c)),
163 => IF_ICMPGT(read_u16(opcodes, c)), 163 => IF_ICMPGT(offset(opcodes, c)),
164 => IF_ICMPLE(read_u16(opcodes, c)), 164 => IF_ICMPLE(offset(opcodes, c)),
165 => IF_ACMPEQ(read_u16(opcodes, c)), 165 => IF_ACMPEQ(offset(opcodes, c)),
166 => IF_ACMPNE(read_u16(opcodes, c)), 166 => IF_ACMPNE(offset(opcodes, c)),
167 => GOTO(read_u16(opcodes, c)), 167 => GOTO(offset(opcodes, c)),
168 => JSR(read_u16(opcodes, c)), 168 => JSR(offset(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,
@ -242,21 +256,19 @@ 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 => { 198 => IFNULL(offset(opcodes, c)),
let j = read_u16(opcodes, c); 199 => IFNONNULL(offset(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
}

View file

@ -8,12 +8,13 @@ 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>> = 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"]);
//TODO less pubs //TODO less pubs
pub struct ClassManager { pub struct ClassManager {
@ -99,11 +100,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); let id = self.get_or_new_id(name.into());
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", Value::Utf8(name.into())); instance.set(cls, "java/lang/Class", "name", 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);
@ -112,8 +113,10 @@ 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) => if self.classes.get(id).is_none() { Some(id) => {
self.add_class(&type_name); if self.classes.get(id).is_none() {
self.add_class(&type_name);
}
} }
None => { None => {
self.add_class(&type_name); self.add_class(&type_name);
@ -135,6 +138,7 @@ 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())
} }
@ -166,42 +170,62 @@ 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(&mut object_field_mapping, &mut static_field_mapping, object_field_map_index, static_field_map_index, current_classdef); 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,
);
current_id = current_classdef.super_class.as_ref() current_id = current_classdef
.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.super_class.as_ref() let superclass_id = this_classdef
.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.interfaces.iter() let interface_ids: Vec<ClassId> = this_classdef
.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();
// initial values for static fields (before static init) let mut all_interfaces = Vec::new();
self.static_class_data.insert(this_classid, Self::set_field_data(&static_field_mapping)); self.get_all_interfaces(&interface_ids, &mut all_interfaces);
self.classes.insert(this_classid, Class { // initial values for static fields (before static init)
id: this_classid, self.static_class_data
initialized: false, .insert(this_classid, Self::set_field_data(&static_field_mapping));
name: name.into(),
superclass: superclass_id, self.classes.insert(
parents, this_classid,
interfaces: interface_ids, Class {
object_field_mapping, id: this_classid,
static_field_mapping, initialized: false,
}); name: name.into(),
superclass: superclass_id,
all_superclasses: parents,
interfaces: interface_ids,
all_interfaces,
object_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", Value::Utf8(name.into())); instance.set(cls, "java/lang/Class", "name", 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);
@ -209,28 +233,44 @@ impl ClassManager {
// run static init // run static init
if this_classdef.methods.contains_key("<clinit>()V") { if this_classdef.methods.contains_key("<clinit>()V") {
Vm { stack: Vec::new()}.run2(self, this_classid,"<clinit>()V"); Vm { stack: Vec::new() }.run2(self, this_classid, "<clinit>()V");
} }
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(object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>, fn add_fields_for_this_or_parents(
static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>, object_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
object_field_map_index: &mut usize, static_field_mapping: &mut HashMap<String, HashMap<String, TypeIndex>>,
static_field_map_index: &mut usize, object_field_map_index: &mut usize,
current_classdef: &ClassDef) { static_field_map_index: &mut usize,
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 &current_classdef.fields { for (field_name, field) in &current_classdef.fields {
if !field.is(Modifier::Static) { if !field.is(Modifier::Static) {
instance_field_mappings.insert(field_name.to_owned(), instance_field_mappings.insert(
TypeIndex::new(field.type_of().to_owned(), *object_field_map_index)); field_name.to_owned(),
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(field_name.to_owned(), static_field_mappings.insert(
TypeIndex::new(field.type_of().to_owned(), *static_field_map_index)); field_name.to_owned(),
TypeIndex::new(field.type_of().to_owned(), *static_field_map_index),
);
*static_field_map_index += 1; *static_field_map_index += 1;
} }
} }
@ -252,25 +292,28 @@ 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, class_name: &str) -> (ClassId, Vec<String>) {
debug!("load {}", name); debug!("load {}", class_name);
let id = self.get_or_new_id(name); let class_name = class_name.to_owned().replace(".", "/");
let id = self.get_or_new_id(class_name.clone());
let classdef = self.classdefs let classdef = self.classdefs.entry(id).or_insert_with(|| {
.entry(id) classloader::get_classdef(&self.classpath, class_name.as_str()).expect("ClassNotFound")
.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: &str) -> ClassId { fn get_or_new_id(&mut self, name: String) -> ClassId {
let id = *self.names.entry(name.to_string()).or_insert_with(|| { let id = *self.names.entry(name).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(field_mapping: &HashMap<String, HashMap<String, TypeIndex>>) -> Vec<Value> { pub(crate) fn set_field_data(
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 {
@ -344,27 +387,100 @@ 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("value1".to_owned(), Field::new(constant_pool.clone(), 0, 9, 5, HashMap::new(), 0)); c_fields.insert(
c_fields.insert("value2".to_owned(), Field::new(constant_pool.clone(), 0, 10, 5, HashMap::new(), 0)); "value1".to_owned(),
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("name".to_owned(), Field::new(constant_pool.clone(), 1, 2, 5, HashMap::new(), 0)); class_fields.insert(
"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(1, ClassDef::new(0, 0, constant_pool.clone(), 0, 0, None, vec![], c_fields, HashMap::new(), HashMap::new())); classdefs.insert(
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(2, ClassDef::new(0, 0, constant_pool.clone(), 0, 6, None, vec![], HashMap::new(), HashMap::new(), HashMap::new())); classdefs.insert(
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(3, ClassDef::new(0, 0, constant_pool, 0, 8, None, vec![], class_fields, HashMap::new(), HashMap::new())); classdefs.insert(
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("name".to_owned(), TypeIndex { type_name: "java/lang/String".into(), index: 0 }); fields_declared_by_java_lang_class.insert(
class_field_mapping.insert("java/lang/Class".to_owned(), fields_declared_by_java_lang_class); "name".to_owned(),
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() }); TypeIndex {
type_name: "java/lang/String".into(),
index: 0,
},
);
class_field_mapping.insert(
"java/lang/Class".to_owned(),
fields_declared_by_java_lang_class,
);
classes.insert(
3,
Class {
id: 3,
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(),
@ -379,7 +495,25 @@ 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!(0, loaded_class.object_field_mapping.get("C").unwrap().get("value1").unwrap().index); assert_eq!(
assert_eq!(1, loaded_class.object_field_mapping.get("C").unwrap().get("value2").unwrap().index); 0,
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
);
} }
} }

View file

@ -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;

View file

@ -1,7 +1,10 @@
use java_rs::vm::runtime::Vm; use java_rs::vm::runtime::Vm;
fn main() { fn main() {
let vm = Vm::new(); let mut vm = Vm::new();
vm.run("/Users/Shautvast/dev/java/tests", "testclasses.Main", "main([Ljava/lang/String;)V"); vm.run(
"/Users/Shautvast/dev/java.rs/tests",
"testclasses/Main",
"main([Ljava/lang/String;)V",
);
} }

View file

@ -1,4 +1,5 @@
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 {
@ -12,14 +13,36 @@ pub enum Value {
F32(f32), F32(f32),
F64(f64), F64(f64),
BOOL(bool), BOOL(bool),
CHAR(char), CHAR(i32),
// 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 {
@ -29,11 +52,45 @@ 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::Ref(v) = self { if let Value::Null = 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!();
} }
} }
} }

View file

@ -1,15 +1,15 @@
use anyhow::{anyhow, Error};
use crate::vm::object::ObjectRef::*; use crate::vm::object::ObjectRef::*;
use anyhow::{anyhow, Error};
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 Null = arrayref { if let Value::Null = arrayref {
return Err(anyhow!("NullpointerException")); return Err(anyhow!("NullpointerException"));
} }
if let Ref(objectref) = arrayref { if let Ref(objectref) = arrayref {
@ -50,6 +50,9 @@ 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);
}
} }
} }
} }
@ -57,7 +60,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 Null = arrayref { if let Value::Null = arrayref {
return Err(anyhow!("NullpointerException")); return Err(anyhow!("NullpointerException"));
} }
@ -94,9 +97,9 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
unreachable!() unreachable!()
} }
} }
CharArray(ref mut array) => unsafe { CharArray(ref mut array) => {
if let I32(value) = value { if let I32(value) = value {
array[index as usize] = char::from_u32_unchecked(value as u32); array[index as usize] = value
} else { } else {
unreachable!() unreachable!()
} }
@ -136,11 +139,11 @@ pub(crate) fn array_store(value: Value, index: Value, arrayref: Value) -> Result
unreachable!() unreachable!()
} }
} }
Object(_) | Class(_) => {} //throw error? Object(_) | Class(_) | ObjectRef::Null => {} //throw error?
} }
} }
} else { } else {
unreachable!() unreachable!()
} }
Ok(()) Ok(())
} }

View file

@ -1,4 +1,5 @@
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 Normal file
View file

@ -0,0 +1,243 @@
#![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(),
))))
}

View file

@ -1,25 +1,100 @@
use std::cell::RefCell; use crate::class::{Class, ClassId};
use std::rc::Rc; use crate::value::Value;
use crate::vm::object::ObjectRef::*;
use log::debug; use log::debug;
use rand::random; use rand::random;
use crate::class::{Class, ClassId}; use std::cell::RefCell;
use crate::vm::object::ObjectRef::*; use std::fmt::{Debug, Formatter, Pointer};
use crate::value::Value; use std::rc::Rc;
#[derive(Debug, Clone)] #[derive(Clone, PartialEq)]
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(Vec<i8>), ByteArray(Array<i8>),
ShortArray(Vec<i16>), //maybe use arrays?
IntArray(Vec<i32>), ShortArray(Array<i16>),
LongArray(Vec<i64>), IntArray(Array<i32>),
FloatArray(Vec<f32>), LongArray(Array<i64>),
DoubleArray(Vec<f64>), FloatArray(Array<f32>),
BooleanArray(Vec<bool>), DoubleArray(Array<f64>),
CharArray(Vec<char>), BooleanArray(Array<bool>),
StringArray(Vec<String>), CharArray(Array<i32>),
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 {
@ -35,7 +110,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),
} }
} }
} }
@ -53,29 +128,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::with_capacity(size)) ObjectArray(class.id, vec![ObjectRef::Null; 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(Vec::with_capacity(size)), 8 => ByteArray(Array::new(0, size)),
9 => ShortArray(Vec::with_capacity(size)), 9 => ShortArray(Array::new(0, size)),
10 => IntArray(Vec::with_capacity(size)), 10 => IntArray(Array::new(0, size)),
11 => LongArray(Vec::with_capacity(size)), 11 => LongArray(Array::new(0, size)),
6 => FloatArray(Vec::with_capacity(size)), 6 => FloatArray(Array::new(0_f32, size)),
7 => DoubleArray(Vec::with_capacity(size)), 7 => DoubleArray(Array::new(0_f64, size)),
4 => BooleanArray(Vec::with_capacity(size)), 4 => BooleanArray(Array::new(false, size)),
5 => CharArray(Vec::with_capacity(size)), 5 => CharArray(Array::new(0, 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(Vec::with_capacity(size)) IntArray(Array::new(0, size))
} }
pub fn new_byte_array(d: Vec<u8>) -> Self { pub fn new_byte_array(d: Vec<u8>) -> Self {
ByteArray(into_vec_i8(d)) ByteArray(Array::from_vec(into_vec_i8(d)))
} }
} }
@ -101,12 +176,18 @@ 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(), id: random::<u32>() >> 1,
class_id: class.id, class_id: class.id,
data: instance_data, data: instance_data,
} }
@ -135,7 +216,13 @@ impl Object {
field_data field_data
} }
pub fn set(&mut self, runtime_type: &Class, declared_type: &str, field_name: &str, value: Value) { pub fn set(
&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
@ -153,8 +240,11 @@ impl Object {
.unwrap() .unwrap()
.get(field_name) .get(field_name)
.unwrap(); .unwrap();
debug!("get {:?}:{}.{}:{} @{}", runtime_type, declared_type, field_name, type_index.type_name, type_index.index); debug!(
"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()
} }
} }

View file

@ -85,20 +85,22 @@ pub(crate) enum Opcode {
LSHL, LSHL,
ISHR, ISHR,
LSHR, LSHR,
IUSHR,
LUSHR,
IAND, IAND,
LAND, LAND,
IOR, IOR,
LOR, LOR,
IXOR, IXOR,
LXOR, LXOR,
IINC(u8,u8), IINC(u8, u8),
I2L, I2L,
I2F, I2F,
I2D, I2D,
L2I, L2I,
L2F, L2F,
L2D, L2D,
WIDE_IINC(u16,u16), WIDE_IINC(u16, u16),
F2I, F2I,
F2L, F2L,
F2D, F2D,
@ -134,6 +136,7 @@ pub(crate) enum Opcode {
TABLESWITCH(Tableswitch), TABLESWITCH(Tableswitch),
LOOKUPSWITCH(Lookupswitch), LOOKUPSWITCH(Lookupswitch),
IRETURN, IRETURN,
LRETURN,
FRETURN, FRETURN,
DRETURN, DRETURN,
ARETURN, ARETURN,
@ -162,7 +165,4 @@ 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

View file

@ -1,18 +1,23 @@
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("/Users/Shautvast/dev/java/tests", "testclasses.IfCmp", "i_is_1()Z"); vm.run(
"/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("/Users/Shautvast/dev/java/tests", "testclasses.Const", "hello()Ljava/lang/String;") vm.run(
"/Users/Shautvast/dev/java/tests",
"testclasses.Const",
"hello()Ljava/lang/String;",
)
} }
} }

View file

@ -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,13 +11,10 @@ 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));
} }
} }