stub native invoke, sugar, fmt
This commit is contained in:
parent
03732a3b73
commit
22a5ee8346
11 changed files with 348 additions and 187 deletions
120
src/class.rs
120
src/class.rs
|
|
@ -32,7 +32,8 @@ pub struct Class {
|
|||
}
|
||||
|
||||
impl Class {
|
||||
pub fn new(minor_version: u16,
|
||||
pub fn new(
|
||||
minor_version: u16,
|
||||
major_version: u16,
|
||||
constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||
access_flags: u16,
|
||||
|
|
@ -41,7 +42,8 @@ impl Class {
|
|||
interface_indices: Vec<u16>,
|
||||
fields: HashMap<String, Field>,
|
||||
methods: HashMap<String, Method>,
|
||||
attributes: HashMap<String, AttributeType>) -> Self {
|
||||
attributes: HashMap<String, AttributeType>,
|
||||
) -> Self {
|
||||
let name = Class::class_name(this_class, constant_pool.clone()).unwrap();
|
||||
let super_class_name = Class::class_name(super_class_index, constant_pool.clone());
|
||||
|
||||
|
|
@ -88,10 +90,17 @@ impl Class {
|
|||
}
|
||||
|
||||
// part of the initialize procedure
|
||||
fn map_fields(field_mapping: &mut HashMap<String, HashMap<String, (String, usize)>>, class: &Class, field_map_index: &mut usize) {
|
||||
fn map_fields(
|
||||
field_mapping: &mut HashMap<String, HashMap<String, (String, usize)>>,
|
||||
class: &Class,
|
||||
field_map_index: &mut usize,
|
||||
) {
|
||||
let mut this_fields = HashMap::new(); //fields in class are stored per class and every superclass.
|
||||
for field in &class.fields {
|
||||
this_fields.insert(field.0.to_owned(), (field.1.type_of().to_owned(), *field_map_index)); //name => (type,index)
|
||||
this_fields.insert(
|
||||
field.0.to_owned(),
|
||||
(field.1.type_of().to_owned(), *field_map_index),
|
||||
); //name => (type,index)
|
||||
*field_map_index += 1;
|
||||
}
|
||||
let this_name = class.name.to_owned();
|
||||
|
|
@ -108,10 +117,14 @@ impl Class {
|
|||
.ok_or(anyhow!("Method {} not found", name))
|
||||
}
|
||||
|
||||
fn class_name(super_class_index: u16, constant_pool: Rc<HashMap<u16, CpEntry>>) -> Option<String> {
|
||||
fn class_name(
|
||||
super_class_index: u16,
|
||||
constant_pool: Rc<HashMap<u16, CpEntry>>,
|
||||
) -> Option<String> {
|
||||
if super_class_index == 0 {
|
||||
None
|
||||
} else if let CpEntry::ClassRef(name_index) = constant_pool.get(&super_class_index).unwrap() {
|
||||
} else if let CpEntry::ClassRef(name_index) = constant_pool.get(&super_class_index).unwrap()
|
||||
{
|
||||
if let CpEntry::Utf8(name) = constant_pool.get(name_index).unwrap() {
|
||||
Some(name.to_owned())
|
||||
} else {
|
||||
|
|
@ -124,15 +137,30 @@ impl Class {
|
|||
|
||||
// convienence methods for data from the constantpool
|
||||
|
||||
pub fn get_field_ref(&self, index: &u16) -> Option<(&u16, &u16)> {
|
||||
if let CpEntry::Fieldref(class_index, name_and_type_index) = self.constant_pool.get(index).unwrap() {
|
||||
pub fn cp_field_ref(&self, index: &u16) -> Option<(&u16, &u16)> {
|
||||
if let CpEntry::Fieldref(class_index, name_and_type_index) =
|
||||
self.constant_pool.get(index).unwrap()
|
||||
{
|
||||
Some((class_index, name_and_type_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_class_ref(&self, index: &u16) -> Option<&u16> {
|
||||
/// both methodRef and InterfaceMethodRef
|
||||
/// returns (class_index, name_and_type_index)
|
||||
pub fn cp_method_ref(&self, index: &u16) -> Option<(&u16, &u16)> {
|
||||
if let CpEntry::MethodRef(class_index, name_and_type_index)
|
||||
| CpEntry::InterfaceMethodref(class_index, name_and_type_index) =
|
||||
self.constant_pool.get(index).unwrap()
|
||||
{
|
||||
Some((class_index, name_and_type_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn cp_class_ref(&self, index: &u16) -> Option<&u16> {
|
||||
if let CpEntry::ClassRef(name_index) = self.constant_pool.get(index).unwrap() {
|
||||
Some(name_index)
|
||||
} else {
|
||||
|
|
@ -140,7 +168,7 @@ impl Class {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_utf8(&self, index: &u16) -> Option<&String> {
|
||||
pub fn cp_utf8(&self, index: &u16) -> Option<&String> {
|
||||
if let CpEntry::Utf8(utf8) = self.constant_pool.get(index).unwrap() {
|
||||
Some(utf8)
|
||||
} else {
|
||||
|
|
@ -148,14 +176,14 @@ impl Class {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_name_and_type(&self, index: &u16) -> Option<(&u16, &u16)> {
|
||||
if let CpEntry::NameAndType(name_index, type_index) = self.constant_pool.get(index).unwrap(){
|
||||
pub fn cp_name_and_type(&self, index: &u16) -> Option<(&u16, &u16)> {
|
||||
if let CpEntry::NameAndType(name_index, type_index) = self.constant_pool.get(index).unwrap()
|
||||
{
|
||||
Some((name_index, type_index))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
unsafe impl Send for Class {}
|
||||
|
|
@ -208,6 +236,11 @@ impl Method {
|
|||
|
||||
full_name
|
||||
}
|
||||
|
||||
pub fn is(&self, modifier: Modifier) -> bool {
|
||||
let m = modifier as u16;
|
||||
(self.access_flags & m) == m
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Field {
|
||||
|
|
@ -263,29 +296,34 @@ impl Field {
|
|||
}
|
||||
}
|
||||
|
||||
const MODIFIERS: [(u16, &str); 12] = [
|
||||
(0x0001, "public "),
|
||||
(0x0002, "private "),
|
||||
(0x0004, "protected "),
|
||||
(0x0008, "static "),
|
||||
(0x0010, "final "),
|
||||
(0x0020, "synchronized "),
|
||||
(0x0040, "volatile "),
|
||||
(0x0080, "transient "),
|
||||
(0x0100, "native "),
|
||||
(0x0200, "interface "),
|
||||
(0x0400, "interface "),
|
||||
(0x0800, "strict "),
|
||||
const MODIFIERS: [(Modifier, &str); 12] = [
|
||||
(Modifier::Public, "public "),
|
||||
(Modifier::Private, "private "),
|
||||
(Modifier::Protected, "protected "),
|
||||
(Modifier::Static, "static "),
|
||||
(Modifier::Final, "final "),
|
||||
(Modifier::Synchronized, "synchronized "),
|
||||
(Modifier::Volatile, "volatile "),
|
||||
(Modifier::Transient, "transient "),
|
||||
(Modifier::Native, "native "),
|
||||
(Modifier::Abstract, "abstract"),
|
||||
(Modifier::Strict, "strict"),
|
||||
(Modifier::Synthetic, "synthetic"),
|
||||
];
|
||||
|
||||
pub fn get_modifier(modifier: u16) -> String {
|
||||
let mut output = String::new();
|
||||
for m in MODIFIERS {
|
||||
if modifier & m.0 == m.0 {
|
||||
output.push_str(m.1)
|
||||
}
|
||||
}
|
||||
output
|
||||
pub enum Modifier {
|
||||
Public = 0x0001,
|
||||
Private = 0x0002,
|
||||
Protected = 0x0004,
|
||||
Static = 0x0008,
|
||||
Final = 0x0010,
|
||||
Synchronized = 0x0020,
|
||||
Volatile = 0x0040,
|
||||
Transient = 0x0080,
|
||||
Native = 0x0100,
|
||||
Abstract = 0x0400,
|
||||
Strict = 0x0800,
|
||||
Synthetic = 0x1000,
|
||||
}
|
||||
|
||||
//TODO implement more types
|
||||
|
|
@ -384,6 +422,20 @@ pub enum Value {
|
|||
Ref(Arc<UnsafeCell<ObjectRef>>),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn void() -> UnsafeValue {
|
||||
Arc::new(UnsafeCell::new(Value::Void))
|
||||
}
|
||||
}
|
||||
|
||||
impl Into<UnsafeValue> for Value {
|
||||
fn into(self) -> UnsafeValue {
|
||||
Arc::new(UnsafeCell::new(self))
|
||||
}
|
||||
}
|
||||
|
||||
pub type UnsafeValue = Arc<UnsafeCell<Value>>;
|
||||
|
||||
unsafe impl Send for Value {}
|
||||
|
||||
unsafe impl Sync for Value {}
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ use anyhow::Error;
|
|||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
|
||||
// The native classoader
|
||||
pub fn load_class(bytecode: Vec<u8>) -> Result<Class, Error> {
|
||||
let pos = &mut 0;
|
||||
|
|
@ -227,7 +226,7 @@ fn read_attribute(
|
|||
*index += attribute_length;
|
||||
|
||||
if let CpEntry::Utf8(s) = &constant_pool.get(&attribute_name_index).unwrap() {
|
||||
println!("Att [{}]", s);
|
||||
// println!("Att [{}]", s);
|
||||
return match s.as_str() {
|
||||
"ConstantValue" => {
|
||||
assert_eq!(info.len(), 2);
|
||||
|
|
@ -251,8 +250,7 @@ fn read_attribute(
|
|||
let attribute_count = read_u16(&info, ci);
|
||||
let mut code_attributes = HashMap::new();
|
||||
for _ in 0..attribute_count {
|
||||
if let Some(att) = read_attribute(constant_pool.clone(), &info, ci)
|
||||
{
|
||||
if let Some(att) = read_attribute(constant_pool.clone(), &info, ci) {
|
||||
code_attributes.insert(att.0, att.1);
|
||||
}
|
||||
}
|
||||
|
|
@ -267,9 +265,11 @@ fn read_attribute(
|
|||
))),
|
||||
))
|
||||
}
|
||||
"SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)),
|
||||
"LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)),
|
||||
"RuntimeVisibleAnnotations" => Some(("".into(), AttributeType::RuntimeInvisibleAnnotations)), //stub
|
||||
"SourceFile" => Some(("SourceFile".into(), AttributeType::SourceFile)), //stub
|
||||
"LineNumberTable" => Some(("SourceFile".into(), AttributeType::LineNumberTable)), //stub
|
||||
"RuntimeVisibleAnnotations" => {
|
||||
Some(("".into(), AttributeType::RuntimeInvisibleAnnotations))
|
||||
} //stub
|
||||
"NestMembers" => Some(("".into(), AttributeType::NestMembers)), //stub
|
||||
"BootstrapMethods" => Some(("".into(), AttributeType::BootstrapMethods)), //stub
|
||||
"InnerClasses" => Some(("".into(), AttributeType::InnerClasses)), //stub
|
||||
|
|
@ -287,12 +287,12 @@ pub enum CpEntry {
|
|||
Float(f32),
|
||||
Long(i64),
|
||||
Double(f64),
|
||||
ClassRef(u16),
|
||||
StringRef(u16),
|
||||
Fieldref(u16, u16),
|
||||
MethodRef(u16, u16),
|
||||
InterfaceMethodref(u16, u16),
|
||||
NameAndType(u16, u16),
|
||||
ClassRef(u16), // (utf8)
|
||||
StringRef(u16), // (utf8)
|
||||
Fieldref(u16, u16), // (class, name_and_type)
|
||||
MethodRef(u16, u16), // (class, name_and_type)
|
||||
InterfaceMethodref(u16, u16), // (class, name_and_type)
|
||||
NameAndType(u16, u16), // (name, descriptor)
|
||||
MethodHandle(u8, u16),
|
||||
MethodType(u16),
|
||||
InvokeDynamic(u16, u16),
|
||||
|
|
|
|||
43
src/heap.rs
43
src/heap.rs
|
|
@ -2,7 +2,7 @@ use std::cell::UnsafeCell;
|
|||
use std::fmt;
|
||||
use std::sync::Arc;
|
||||
|
||||
use crate::class::{Class, Value};
|
||||
use crate::class::{Class, UnsafeValue, Value};
|
||||
use crate::classloader::CpEntry;
|
||||
|
||||
// trying to implement efficient object instance storage
|
||||
|
|
@ -10,7 +10,7 @@ pub struct Object {
|
|||
// locked: bool,
|
||||
// hashcode: i32,
|
||||
pub class: Arc<Class>,
|
||||
pub data: Vec<Arc<UnsafeCell<Value>>>,
|
||||
pub data: Vec<UnsafeValue>,
|
||||
} //arrays
|
||||
|
||||
// can contain object or array
|
||||
|
|
@ -36,11 +36,14 @@ unsafe impl Sync for Object {}
|
|||
impl Object {
|
||||
pub fn new(class: Arc<Class>) -> Self {
|
||||
let instance_data = Object::init_fields(&class);
|
||||
Self { class, data: instance_data}
|
||||
Self {
|
||||
class,
|
||||
data: instance_data,
|
||||
}
|
||||
}
|
||||
|
||||
// initializes all non-static fields to their default values
|
||||
pub(crate) fn init_fields(class: &Class) -> Vec<Arc<UnsafeCell<Value>>>{
|
||||
pub(crate) fn init_fields(class: &Class) -> Vec<UnsafeValue> {
|
||||
let mut field_data = Vec::with_capacity(class.n_fields());
|
||||
|
||||
for (_, fields) in class.field_mapping.as_ref().unwrap() {
|
||||
|
|
@ -56,20 +59,36 @@ impl Object {
|
|||
"L" => Value::Null,
|
||||
_ => Value::Void,
|
||||
};
|
||||
field_data.push(Arc::new(UnsafeCell::new(value)));
|
||||
field_data.push(value.into());
|
||||
}
|
||||
}
|
||||
|
||||
field_data
|
||||
}
|
||||
|
||||
pub fn set(&mut self, class_name: &String, field_name: &String, value: Arc<UnsafeCell<Value>>) {
|
||||
let (_type, index) = self.class.field_mapping.as_ref().unwrap().get(class_name).unwrap().get(field_name).unwrap();
|
||||
pub fn set(&mut self, class_name: &String, field_name: &String, value: UnsafeValue) {
|
||||
let (_type, index) = self
|
||||
.class
|
||||
.field_mapping
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(class_name)
|
||||
.unwrap()
|
||||
.get(field_name)
|
||||
.unwrap();
|
||||
self.data[*index] = value;
|
||||
}
|
||||
|
||||
pub fn get(&mut self, class_name: &String, field_name: &String) -> &Arc<UnsafeCell<Value>> {
|
||||
let (_type, index) = &self.class.field_mapping.as_ref().unwrap().get(class_name).unwrap().get(field_name).unwrap();
|
||||
pub fn get(&mut self, class_name: &String, field_name: &String) -> &UnsafeValue {
|
||||
let (_type, index) = &self
|
||||
.class
|
||||
.field_mapping
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.get(class_name)
|
||||
.unwrap()
|
||||
.get(field_name)
|
||||
.unwrap();
|
||||
&self.data[*index]
|
||||
}
|
||||
|
||||
|
|
@ -90,11 +109,7 @@ impl fmt::Debug for Object {
|
|||
// // r
|
||||
// }
|
||||
// ).collect();
|
||||
write!(
|
||||
f,
|
||||
"{}",
|
||||
self.class.name
|
||||
)
|
||||
write!(f, "{}", self.class.name)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,7 +53,6 @@ pub fn read_bytecode(name: String) -> Result<Vec<u8>, Error> {
|
|||
Ok(buffer)
|
||||
}
|
||||
|
||||
|
||||
// methods to read values from big-endian binary data
|
||||
|
||||
pub(crate) fn read_u8(data: &[u8], pos: &mut usize) -> u8 {
|
||||
|
|
@ -125,4 +124,3 @@ pub(crate) fn read_f64(data: &[u8], pos: &mut usize) -> f64 {
|
|||
.expect("slice with incorrect length"),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,3 +4,5 @@ mod heap;
|
|||
pub mod io;
|
||||
pub mod opcodes;
|
||||
pub mod vm;
|
||||
|
||||
pub mod native;
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ fn main() -> Result<(), Error> {
|
|||
// TODO build index for package -> jarfile?
|
||||
|
||||
let mut vm = Vm::new("tests");
|
||||
let main_class = "Main";
|
||||
let main_class = "Inheritance";
|
||||
|
||||
vm.execute(main_class, "main([Ljava/lang/String;)V", vec![])
|
||||
.unwrap();
|
||||
|
|
|
|||
8
src/native.rs
Normal file
8
src/native.rs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::class::{Class, Method, UnsafeValue, Value};
|
||||
|
||||
pub fn invoke_native(class: Arc<Class>, method: &Method) -> UnsafeValue {
|
||||
println!("invoke native {:?}.{:?}", class.name, method.name());
|
||||
Value::void()
|
||||
}
|
||||
|
|
@ -87,51 +87,51 @@ pub const CASTORE: u8 = 85; // (0x55) Store into char array
|
|||
pub const SASTORE: u8 = 86; // (0x56) Store into short array
|
||||
pub const POP: u8 = 87; // (0x57) Pop the top operand stack value
|
||||
pub const DUP: u8 = 89; // (0x59) duplicate the top operand stack value
|
||||
// pub const dup_x1: u8 = 90; // (0x5a) Duplicate the top operand stack value and insert two values down
|
||||
// pub const dup_x2: u8 = 91; // (0x5b) Duplicate the top operand stack value and insert two or three values down
|
||||
// pub const dup2: u8 = 92; // (0x5c) Duplicate the top one or two operand stack values
|
||||
// pub const dup2_x1: u8 = 93; //(0x5d) Duplicate the top one or two operand stack values and insert two or three values down
|
||||
// pub const dup2_x2:u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down
|
||||
// pub const fadd: u8 = 98; // (0x62) Add float
|
||||
// pub const dadd: u8 = 99; // (0x63) add double
|
||||
//
|
||||
// pub const dsub:u8 = 103; // (0x67) subtract double
|
||||
// pub const fmul: u8 = 106; // (0x6a) Multiply float
|
||||
// pub const dmul: u8 = 107; // (0x6b) Multiply double
|
||||
//
|
||||
// pub const fdiv: u8 = 110; // (0x6e) Divide float
|
||||
// pub const ddiv:u8 = 111; // (0x6f) divide double
|
||||
// pub const frem: u8 = 114; // (0x72) Remainder float
|
||||
// pub const drem: u8 = 115; // (0x73) remainder double
|
||||
// pub const fneg: u8 = 118; // (0x76) Negate float
|
||||
// pub const dneg: u8 = 119; // (0x77) Negate double
|
||||
// pub const f2i: u8 = 139; // (0x8b) Convert float to int
|
||||
// pub const f2l: u8 = 140; // (0x8c) Convert float to long
|
||||
// pub const f2d: u8 = 141; // (0x8d) Convert float to double
|
||||
// pub const d2i:u8 = 142; // (0x8e) double to int
|
||||
// pub const d2l:u8 = 143; // (0x8f) double to long
|
||||
// pub const d2f: u8 = 144; // (0x90) double to float
|
||||
// pub const fcmpl:u8 = 149; // (0x95) Compare float (less than)
|
||||
// pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than)
|
||||
// pub const dcmpl:u8 = 151; // (0x97) compare double (less than)
|
||||
// pub const dcmpg:u8 = 152; // (0x98) compare double (greater than)
|
||||
//
|
||||
pub const dup_x1: u8 = 90; // (0x5a) Duplicate the top operand stack value and insert two values down
|
||||
pub const dup_x2: u8 = 91; // (0x5b) Duplicate the top operand stack value and insert two or three values down
|
||||
pub const dup2: u8 = 92; // (0x5c) Duplicate the top one or two operand stack values
|
||||
pub const dup2_x1: u8 = 93; //(0x5d) Duplicate the top one or two operand stack values and insert two or three values down
|
||||
pub const dup2_x2: u8 = 94; // (0x5e) Duplicate the top one or two operand stack values and insert two, three, or four values down
|
||||
|
||||
pub const fadd: u8 = 98; // (0x62) Add float
|
||||
pub const dadd: u8 = 99; // (0x63) add double
|
||||
|
||||
pub const dsub: u8 = 103; // (0x67) subtract double
|
||||
pub const fmul: u8 = 106; // (0x6a) Multiply float
|
||||
pub const dmul: u8 = 107; // (0x6b) Multiply double
|
||||
|
||||
pub const fdiv: u8 = 110; // (0x6e) Divide float
|
||||
pub const ddiv: u8 = 111; // (0x6f) divide double
|
||||
pub const frem: u8 = 114; // (0x72) Remainder float
|
||||
pub const drem: u8 = 115; // (0x73) remainder double
|
||||
pub const fneg: u8 = 118; // (0x76) Negate float
|
||||
pub const dneg: u8 = 119; // (0x77) Negate double
|
||||
|
||||
pub const f2i: u8 = 139; // (0x8b) Convert float to int
|
||||
pub const f2l: u8 = 140; // (0x8c) Convert float to long
|
||||
pub const f2d: u8 = 141; // (0x8d) Convert float to double
|
||||
pub const d2i: u8 = 142; // (0x8e) double to int
|
||||
pub const d2l: u8 = 143; // (0x8f) double to long
|
||||
pub const d2f: u8 = 144; // (0x90) double to float
|
||||
|
||||
pub const fcmpl: u8 = 149; // (0x95) Compare float (less than)
|
||||
pub const fcmpg: u8 = 150; // (0x96) Compare float (greater than)
|
||||
pub const dcmpl: u8 = 151; // (0x97) compare double (less than)
|
||||
pub const dcmpg: u8 = 152; // (0x98) compare double (greater than)
|
||||
|
||||
pub const IRETURN: u8 = 172; // (0xac) ireturn
|
||||
pub const FRETURN: u8 = 174; // (0xae) Return float from method
|
||||
pub const DRETURN: u8 = 175; // (0xaf) Return double from method
|
||||
// pub const areturn: u8 = 176; //(0xb0) return reference
|
||||
pub const areturn: u8 = 176; //(0xb0) return reference
|
||||
pub const RETURN_VOID: u8 = 177; // (0xb1) Return void from method (actually 'return' but that's a keyword)
|
||||
pub const GETSTATIC: u8 = 178; // (0xb2) Get static field from class
|
||||
pub const GETFIELD: u8 = 180; // (0xb4) Fetch field from object3
|
||||
pub const PUTFIELD: u8 = 181; // (0xb5) Set field in object
|
||||
pub const INVOKEVIRTUAL: u8 = 182; // (0xb6) Invoke instance method; dispatch based on class
|
||||
pub const NEW: u8 = 187; // (0xbb) Create new object
|
||||
//
|
||||
pub const INVOKESPECIAL: u8 = 183; // (0xb7) // nvoke instance method; direct invocation of instance initialization methods and methods of the current class and its supertypes
|
||||
// pub const anewarray: u8 = 189; // (0xbd)
|
||||
//
|
||||
// pub const arraylength: u8 = 190; // (0xbe)
|
||||
//
|
||||
// pub const athrow: u8 = 191; // (0xbf)
|
||||
//
|
||||
// pub const checkcast: u8 = 192; // (0xc0)
|
||||
pub const INVOKESTATIC: u8 = 184; // (0xb8) Invoke a class (static) method
|
||||
pub const NEW: u8 = 187; // (0xbb) Create new object
|
||||
pub const anewarray: u8 = 189; // (0xbd)
|
||||
pub const arraylength: u8 = 190; // (0xbe)
|
||||
pub const athrow: u8 = 191; // (0xbf)
|
||||
pub const checkcast: u8 = 192; // (0xc0)
|
||||
|
|
|
|||
175
src/vm.rs
175
src/vm.rs
|
|
@ -6,17 +6,18 @@ use std::sync::Arc;
|
|||
use anyhow::{anyhow, Error};
|
||||
use once_cell::unsync::Lazy;
|
||||
|
||||
use crate::class::{AttributeType, Class, Value};
|
||||
use crate::class::Value::Void;
|
||||
use crate::classloader::{CpEntry, load_class};
|
||||
use crate::class::{AttributeType, Class, Modifier, UnsafeValue, Value};
|
||||
use crate::classloader::{load_class, CpEntry};
|
||||
use crate::heap::{Heap, Object, ObjectRef};
|
||||
use crate::io::*;
|
||||
use crate::native::invoke_native;
|
||||
use crate::opcodes::*;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct StackFrame {
|
||||
at: String,
|
||||
data: Vec<Arc<UnsafeCell<Value>>>,
|
||||
data: Vec<UnsafeValue>,
|
||||
}
|
||||
|
||||
// maybe just call frame
|
||||
|
|
@ -32,11 +33,11 @@ impl StackFrame {
|
|||
self.data.push(Arc::new(UnsafeCell::new(val)));
|
||||
}
|
||||
|
||||
fn push_arc(&mut self, val: Arc<UnsafeCell<Value>>) {
|
||||
fn push_arc(&mut self, val: UnsafeValue) {
|
||||
self.data.push(val);
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Result<Arc<UnsafeCell<Value>>, Error> {
|
||||
fn pop(&mut self) -> Result<UnsafeValue, Error> {
|
||||
Ok(self.data.pop().unwrap())
|
||||
}
|
||||
}
|
||||
|
|
@ -67,7 +68,10 @@ impl Vm {
|
|||
|
||||
pub fn new(classpath: &'static str) -> Self {
|
||||
Self {
|
||||
classpath: classpath.split(PATH_SEPARATOR).map(|s| s.to_owned()).collect(),
|
||||
classpath: classpath
|
||||
.split(PATH_SEPARATOR)
|
||||
.map(|s| s.to_owned())
|
||||
.collect(),
|
||||
heap: Heap::new(),
|
||||
stack: vec![],
|
||||
}
|
||||
|
|
@ -81,7 +85,7 @@ impl Vm {
|
|||
unsafe {
|
||||
let entry = CLASSDEFS.entry(class_name.into());
|
||||
let entry = entry.or_insert_with(|| {
|
||||
// print!("read class {} ", class_name);
|
||||
println!("read class {} ", class_name);
|
||||
let resolved_path = find_class(&self.classpath, class_name).unwrap();
|
||||
// println!("full path {}", resolved_path);
|
||||
let bytecode = read_bytecode(resolved_path).unwrap();
|
||||
|
|
@ -106,19 +110,31 @@ impl Vm {
|
|||
instance
|
||||
}
|
||||
|
||||
|
||||
/// execute the bytecode
|
||||
/// contains unsafe, as I think that mimics not-synchronized memory access in the original JVM
|
||||
pub fn execute(
|
||||
&mut self,
|
||||
class_name: &str,
|
||||
method_name: &str,
|
||||
args: Vec<Arc<UnsafeCell<Value>>>,
|
||||
) -> Result<Arc<UnsafeCell<Value>>, Error> {
|
||||
let mut local_params: Vec<Option<Arc<UnsafeCell<Value>>>> = args.clone().iter().map(|e| Some(e.clone())).collect();
|
||||
args: Vec<UnsafeValue>,
|
||||
) -> Result<UnsafeValue, Error> {
|
||||
let mut local_params: Vec<Option<UnsafeValue>> =
|
||||
args.clone().iter().map(|e| Some(e.clone())).collect();
|
||||
println!("execute {}.{}", class_name, method_name);
|
||||
let class = self.get_class(class_name)?;
|
||||
let method = class.get_method(method_name)?;
|
||||
if method.is(Modifier::Native) {
|
||||
let return_value = invoke_native(class.clone(), method);
|
||||
unsafe {
|
||||
match *return_value.get() {
|
||||
Void => {}
|
||||
_ => {
|
||||
self.local_stack().push_arc(return_value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let AttributeType::Code(code) = method.attributes.get("Code").unwrap() {
|
||||
let stackframe = StackFrame::new(class_name, method_name);
|
||||
self.stack.push(stackframe);
|
||||
|
|
@ -221,25 +237,31 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
}
|
||||
ILOAD | LLOAD | FLOAD | DLOAD | ALOAD => { // omitting the type checks so far
|
||||
ILOAD | LLOAD | FLOAD | DLOAD | ALOAD => {
|
||||
// omitting the type checks so far
|
||||
let n = read_u8(&code.opcodes, pc) as usize;
|
||||
self.local_stack().push_arc(local_params[n].as_ref().unwrap().clone());
|
||||
self.local_stack()
|
||||
.push_arc(local_params[n].as_ref().unwrap().clone());
|
||||
}
|
||||
ILOAD_0 | LLOAD_0 | FLOAD_0 | DLOAD_0 | ALOAD_0 => {
|
||||
self.local_stack().push_arc(local_params[0].as_ref().unwrap().clone());
|
||||
self.local_stack()
|
||||
.push_arc(local_params[0].as_ref().unwrap().clone());
|
||||
}
|
||||
ILOAD_1 | LLOAD_1 | FLOAD_1 | DLOAD_1 | ALOAD_1 => {
|
||||
self.local_stack().push_arc(local_params[1].as_ref().unwrap().clone());
|
||||
self.local_stack()
|
||||
.push_arc(local_params[1].as_ref().unwrap().clone());
|
||||
}
|
||||
ILOAD_2 | LLOAD_2 | FLOAD_2 | DLOAD_2 | ALOAD_2 => {
|
||||
self.local_stack().push_arc(local_params[2].as_ref().unwrap().clone());
|
||||
self.local_stack()
|
||||
.push_arc(local_params[2].as_ref().unwrap().clone());
|
||||
}
|
||||
ILOAD_3 | LLOAD_3 | FLOAD_3 | DLOAD_3 | ALOAD_3 => {
|
||||
self.local_stack().push_arc(local_params[3].as_ref().unwrap().clone());
|
||||
self.local_stack()
|
||||
.push_arc(local_params[3].as_ref().unwrap().clone());
|
||||
}
|
||||
IALOAD | LALOAD | FALOAD | DALOAD | AALOAD | BALOAD | CALOAD | SALOAD => unsafe {
|
||||
self.array_load()?;
|
||||
}
|
||||
},
|
||||
ISTORE | LSTORE | FSTORE | DSTORE | ASTORE => {
|
||||
let index = read_u8(&code.opcodes, pc) as usize;
|
||||
self.store(&mut local_params, index)?;
|
||||
|
|
@ -256,7 +278,8 @@ impl Vm {
|
|||
ISTORE_3 | LSTORE_3 | DSTORE_3 | ASTORE_3 | FSTORE_3 => {
|
||||
self.store(&mut local_params, 3)?;
|
||||
}
|
||||
BASTORE | IASTORE | LASTORE | CASTORE | SASTORE | FASTORE | DASTORE | AASTORE => unsafe { self.array_store()? }
|
||||
BASTORE | IASTORE | LASTORE | CASTORE | SASTORE | FASTORE | DASTORE
|
||||
| AASTORE => unsafe { self.array_store()? },
|
||||
POP => {
|
||||
self.local_stack().pop()?;
|
||||
}
|
||||
|
|
@ -271,24 +294,26 @@ impl Vm {
|
|||
}
|
||||
RETURN_VOID => {
|
||||
self.stack.pop(); // Void is also returned as a value
|
||||
return Ok(Arc::new(UnsafeCell::new(Void)));
|
||||
return Ok(Value::void());
|
||||
}
|
||||
GETSTATIC => {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
let (class_index, _field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let (class_index, _field_name_and_type_index) =
|
||||
class.cp_field_ref(&cp_index).unwrap(); // all these unwraps are safe as long as the class is valid
|
||||
let class_name_index = class.cp_class_ref(class_index).unwrap();
|
||||
let class_name = class.cp_utf8(class_name_index).unwrap();
|
||||
let class = self.get_class(class_name.as_str())?;
|
||||
println!("{:?}", class); //TODO
|
||||
// println!("{:?}", class); //TODO
|
||||
}
|
||||
GETFIELD => {
|
||||
unsafe {
|
||||
GETFIELD => unsafe {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||
let (field_name_index, _) = class.get_name_and_type(field_name_and_type_index).unwrap();
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let field_name = class.get_utf8(field_name_index).unwrap();
|
||||
let (class_index, field_name_and_type_index) =
|
||||
class.cp_field_ref(&cp_index).unwrap();
|
||||
let (field_name_index, _) =
|
||||
class.cp_name_and_type(field_name_and_type_index).unwrap();
|
||||
let class_name_index = class.cp_class_ref(class_index).unwrap();
|
||||
let class_name = class.cp_utf8(class_name_index).unwrap();
|
||||
let field_name = class.cp_utf8(field_name_index).unwrap();
|
||||
|
||||
let mut objectref = self.local_stack().pop()?;
|
||||
if let Value::Ref(instance) = &mut *objectref.get() {
|
||||
|
|
@ -297,15 +322,16 @@ impl Vm {
|
|||
self.local_stack().push_arc(Arc::clone(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
PUTFIELD => unsafe {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
let (class_index, field_name_and_type_index) = class.get_field_ref(&cp_index).unwrap();
|
||||
let (field_name_index, _) = class.get_name_and_type(field_name_and_type_index).unwrap();
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let field_name = class.get_utf8(field_name_index).unwrap();
|
||||
let (class_index, field_name_and_type_index) =
|
||||
class.cp_field_ref(&cp_index).unwrap();
|
||||
let (field_name_index, _) =
|
||||
class.cp_name_and_type(field_name_and_type_index).unwrap();
|
||||
let class_name_index = class.cp_class_ref(class_index).unwrap();
|
||||
let class_name = class.cp_utf8(class_name_index).unwrap();
|
||||
let field_name = class.cp_utf8(field_name_index).unwrap();
|
||||
|
||||
let value = self.local_stack().pop()?;
|
||||
let mut objectref = self.local_stack().pop()?;
|
||||
|
|
@ -314,29 +340,61 @@ impl Vm {
|
|||
object.set(class_name, field_name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
INVOKEVIRTUAL | INVOKESPECIAL => unsafe {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
if let Some(invocation) = get_signature_for_invoke(&method.constant_pool, cp_index) {
|
||||
if let Some(invocation) =
|
||||
get_signature_for_invoke(&method.constant_pool, cp_index)
|
||||
{
|
||||
let mut args = Vec::with_capacity(invocation.method.num_args);
|
||||
for _ in 0..invocation.method.num_args {
|
||||
args.insert(0, self.local_stack().pop()?);
|
||||
}
|
||||
args.insert(0, self.local_stack().pop()?);
|
||||
let mut returnvalue = self.execute(&invocation.class_name, &invocation.method.name, args)?;
|
||||
let mut return_value = self.execute(
|
||||
&invocation.class_name,
|
||||
&invocation.method.name,
|
||||
args,
|
||||
)?;
|
||||
match *return_value.get() {
|
||||
Void => {}
|
||||
_ => {
|
||||
self.local_stack().push_arc(return_value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
INVOKESTATIC => unsafe {
|
||||
let cp_index = read_u16(&code.opcodes, pc);
|
||||
if let Some(invocation) =
|
||||
get_signature_for_invoke(&method.constant_pool, cp_index)
|
||||
{
|
||||
let mut args = Vec::with_capacity(invocation.method.num_args);
|
||||
for _ in 0..invocation.method.num_args {
|
||||
args.insert(0, self.local_stack().pop()?);
|
||||
}
|
||||
let mut returnvalue = self.execute(
|
||||
&invocation.class_name,
|
||||
&invocation.method.name,
|
||||
args,
|
||||
)?;
|
||||
match *returnvalue.get() {
|
||||
Void => {}
|
||||
_ => { self.local_stack().push_arc(returnvalue.clone()); }
|
||||
_ => {
|
||||
self.local_stack().push_arc(returnvalue.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
NEW => {
|
||||
let class_index = &read_u16(&code.opcodes, pc);
|
||||
let class_name_index = class.get_class_ref(class_index).unwrap();
|
||||
let class_name = class.get_utf8(class_name_index).unwrap();
|
||||
let class_name_index = class.cp_class_ref(class_index).unwrap();
|
||||
let class_name = class.cp_utf8(class_name_index).unwrap();
|
||||
let class = self.get_class(class_name)?;
|
||||
|
||||
let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(Vm::new_instance(class)))));
|
||||
let object = Arc::new(UnsafeCell::new(ObjectRef::Object(Box::new(
|
||||
Vm::new_instance(class),
|
||||
))));
|
||||
self.local_stack().push(Value::Ref(Arc::clone(&object)));
|
||||
self.heap.new_object(object);
|
||||
}
|
||||
|
|
@ -386,7 +444,8 @@ impl Vm {
|
|||
self.local_stack().push(Value::F64(array[index]));
|
||||
}
|
||||
ObjectRef::ObjectArray(ref array) => {
|
||||
self.local_stack().push(Value::Ref(array.get(index).unwrap().clone()));
|
||||
self.local_stack()
|
||||
.push(Value::Ref(array.get(index).unwrap().clone()));
|
||||
}
|
||||
ObjectRef::Object(_) => {} //throw error?
|
||||
}
|
||||
|
|
@ -408,12 +467,14 @@ impl Vm {
|
|||
if let Value::Ref(ref mut objectref) = arrayref {
|
||||
match &mut *objectref.get() {
|
||||
ObjectRef::ByteArray(ref mut array) => {
|
||||
if let Value::I32(value) = *value.get() { // is i32 correct?
|
||||
if let Value::I32(value) = *value.get() {
|
||||
// is i32 correct?
|
||||
array[*index as usize] = value as i8;
|
||||
}
|
||||
}
|
||||
ObjectRef::ShortArray(ref mut array) => {
|
||||
if let Value::I32(value) = *value.get() { // is i32 correct?
|
||||
if let Value::I32(value) = *value.get() {
|
||||
// is i32 correct?
|
||||
array[*index as usize] = value as i16;
|
||||
}
|
||||
}
|
||||
|
|
@ -459,7 +520,11 @@ impl Vm {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn store(&mut self, local_params: &mut Vec<Option<Arc<UnsafeCell<Value>>>>, index: usize) -> Result<(), Error> {
|
||||
fn store(
|
||||
&mut self,
|
||||
local_params: &mut Vec<Option<UnsafeValue>>,
|
||||
index: usize,
|
||||
) -> Result<(), Error> {
|
||||
let value = self.local_stack().pop()?;
|
||||
while local_params.len() < index + 1 {
|
||||
local_params.push(None);
|
||||
|
|
@ -469,7 +534,6 @@ impl Vm {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
struct Invocation {
|
||||
class_name: String,
|
||||
method: MethodSignature,
|
||||
|
|
@ -480,8 +544,11 @@ struct MethodSignature {
|
|||
num_args: usize,
|
||||
}
|
||||
|
||||
// TODO can be simplified now, using cp_ methods in Class
|
||||
fn get_signature_for_invoke(cp: &Rc<HashMap<u16, CpEntry>>, index: u16) -> Option<Invocation> {
|
||||
if let CpEntry::MethodRef(class_index, name_and_type_index) = cp.get(&index).unwrap() {
|
||||
if let CpEntry::MethodRef(class_index, name_and_type_index)
|
||||
| CpEntry::InterfaceMethodref(class_index, name_and_type_index) = cp.get(&index).unwrap()
|
||||
{
|
||||
if let Some(method_signature) = get_name_and_type(Rc::clone(&cp), *name_and_type_index) {
|
||||
if let CpEntry::ClassRef(class_name_index) = cp.get(class_index).unwrap() {
|
||||
if let CpEntry::Utf8(class_name) = cp.get(class_name_index).unwrap() {
|
||||
|
|
@ -503,14 +570,16 @@ fn get_name_and_type(cp: Rc<HashMap<u16, CpEntry>>, index: u16) -> Option<Method
|
|||
let mut method_signature: String = method_name.into();
|
||||
let num_args = get_hum_args(signature);
|
||||
method_signature.push_str(signature);
|
||||
return Some(MethodSignature { name: method_signature, num_args });
|
||||
return Some(MethodSignature {
|
||||
name: method_signature,
|
||||
num_args,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
fn get_hum_args(signature: &str) -> usize {
|
||||
let mut num = 0;
|
||||
let mut i = 1;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
mod test {
|
||||
use classfile_reader::class::Value;
|
||||
use classfile_reader::vm::Vm;
|
||||
use classfile_reader::{classloader::load_class, io};
|
||||
use std::rc::Rc;
|
||||
use java_rs::class::Value;
|
||||
use java_rs::vm::Vm;
|
||||
use std::sync::Arc;
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
19
tests/method_tests.rs
Normal file
19
tests/method_tests.rs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
mod test {
|
||||
use java_rs::class::{Method, Modifier};
|
||||
use std::collections::HashMap;
|
||||
use std::rc::Rc;
|
||||
|
||||
#[test]
|
||||
fn access_flags() {
|
||||
let m = Method::new(
|
||||
Rc::new(HashMap::new()),
|
||||
Modifier::Public as u16 | Modifier::Static as u16,
|
||||
0,
|
||||
0,
|
||||
HashMap::new(),
|
||||
);
|
||||
assert!(m.is(Modifier::Public));
|
||||
assert!(m.is(Modifier::Static));
|
||||
assert!(!m.is(Modifier::Private));
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue