diff --git a/tests/integration_test.rs b/tests/integration_test.rs index 9cdec8fa..10e722a2 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -25,6 +25,13 @@ fn should_write_read_instance_fields() { assert_eq!(11022, last_frame_value.unwrap()) } +#[test] +fn should_write_read_static_fields() { + let vm = VM::new("tests/test_data/StaticFields.class", "tests/test_data/std").unwrap(); + let last_frame_value = vm.run().unwrap(); + assert_eq!(11022, last_frame_value.unwrap()) +} + #[test] fn should_do_extreme_stack_operations() { let vm = VM::new( diff --git a/tests/test_data/InstanceFields.class b/tests/test_data/InstanceFields.class index c49df36f..71da17fb 100644 Binary files a/tests/test_data/InstanceFields.class and b/tests/test_data/InstanceFields.class differ diff --git a/tests/test_data/InstanceFields.java b/tests/test_data/InstanceFields.java index 1d7303a8..4c1d8174 100644 --- a/tests/test_data/InstanceFields.java +++ b/tests/test_data/InstanceFields.java @@ -4,8 +4,6 @@ public class InstanceFields { private int resultSub; private int resultAdd; private int resultMul; - public InstanceFields() { - } public static void main(String[] args) { int first = 11; diff --git a/tests/test_data/StaticFields.class b/tests/test_data/StaticFields.class new file mode 100644 index 00000000..4ce9cb56 Binary files /dev/null and b/tests/test_data/StaticFields.class differ diff --git a/tests/test_data/StaticFields.java b/tests/test_data/StaticFields.java new file mode 100644 index 00000000..ca498434 --- /dev/null +++ b/tests/test_data/StaticFields.java @@ -0,0 +1,29 @@ + +public class StaticFields { + + private static int resultSub; + private static int resultAdd; + private static int resultMul; + + public static void main(String[] args) { + int first = 11; + int second = 1000; + sub(first, second); + add(first, second); + mul(first, second); + int result = resultSub + resultAdd + resultMul; + } + + public static void sub(int first, int second) { + resultSub = first - second; + } + + public static void add(int first, int second) { + resultAdd = first + second; + } + + public static void mul(int first, int second) { + resultMul = first * second; + } + +} diff --git a/vm/src/class_loader.rs b/vm/src/class_loader.rs index 1a26c19d..4f88708f 100644 --- a/vm/src/class_loader.rs +++ b/vm/src/class_loader.rs @@ -1,5 +1,6 @@ use crate::error::{Error, ErrorKind, Result}; -use crate::method_area::java_class::{JavaClass, Methods}; +use crate::method_area::field::Field; +use crate::method_area::java_class::{Fields, JavaClass, Methods}; use crate::method_area::java_method::JavaMethod; use crate::method_area::method_area::MethodArea; use crate::method_area::signature::Signature; @@ -7,6 +8,8 @@ use crate::util::{get_class_name_by_cpool_class_index, get_cpool_string}; use jclass::attributes::Attribute; use jclass::attributes::Attribute::Code; use jclass::class_file::{parse, ClassFile}; +use jclass::fields::FieldFlags; +use std::cell::RefCell; use std::collections::HashMap; use std::fs::File; use std::io::ErrorKind::Other; @@ -60,9 +63,13 @@ impl ClassLoader { fn to_java_class(class_file: ClassFile) -> Result<(String, JavaClass)> { let methods = Self::get_methods(&class_file)?; + let static_fields = Self::get_static_fields(&class_file)?; let class_name = Self::get_class_name(&class_file)?; - Ok((class_name.clone(), JavaClass::new(methods, class_file))) + Ok(( + class_name.clone(), + JavaClass::new(methods, static_fields, class_file), + )) } fn get_methods(class_file: &ClassFile) -> Result { @@ -94,6 +101,33 @@ impl ClassLoader { Ok(Methods::new(method_by_signature)) } + fn get_static_fields(class_file: &ClassFile) -> Result { + let field_by_name = class_file + .fields() + .iter() + .filter_map(|field| { + if field.access_flags().contains(FieldFlags::ACC_STATIC) { + let field_name = get_cpool_string(class_file, field.name_index() as usize) + .ok_or_else(|| Error::new_constant_pool("Error getting field name")) + .ok()?; + + let _field_signature = + get_cpool_string(class_file, field.descriptor_index() as usize) + .ok_or_else(|| { + Error::new_constant_pool("Error getting field signature") + }) + .ok()?; + + Some((field_name, RefCell::new(Field::new()))) + } else { + None + } + }) + .collect(); + + Ok(Fields::new(field_by_name)) + } + fn get_cpool_code_attribute(attributes: &Vec) -> Option<(u16, u16, Vec)> { attributes.iter().find_map(|item| { if let Code { diff --git a/vm/src/execution_engine/engine.rs b/vm/src/execution_engine/engine.rs index b26728c6..c6163a80 100644 --- a/vm/src/execution_engine/engine.rs +++ b/vm/src/execution_engine/engine.rs @@ -382,6 +382,74 @@ impl<'a> Engine<'a> { stack_frames.pop(); // Return from method, pop the current frame // add more logic here } + GETSTATIC => { + stack_frame.incr_pc(); + let high = stack_frame.get_bytecode_byte() as u16; + + stack_frame.incr_pc(); + let low = stack_frame.get_bytecode_byte() as u16; + let fieldref_constpool_index = (high << 8) | low; + + let java_class = self + .method_area + .loaded_classes + .get(main_class_name) + .unwrap(); + + let (class_name, field_name) = + self.method_area.get_fieldname_by_fieldref_cpool_index( + java_class, + fieldref_constpool_index, + )?; + + let value = self + .method_area + .loaded_classes + .get(&class_name) + .unwrap() + .fields + .field_by_name + .get(&field_name) + .unwrap() + .borrow() + .value(); + stack_frame.push(value); + + stack_frame.incr_pc(); + println!( + "GETSTATIC -> class_name={class_name}, field={field_name}, value={value}" + ); + } + PUTSTATIC => { + stack_frame.incr_pc(); + let high = stack_frame.get_bytecode_byte() as u16; + + stack_frame.incr_pc(); + let low = stack_frame.get_bytecode_byte() as u16; + let fieldref_constpool_index = (high << 8) | low; + + let java_class = self + .method_area + .loaded_classes + .get(main_class_name) + .unwrap(); + + let (class_name, field_name) = + self.method_area.get_fieldname_by_fieldref_cpool_index( + java_class, + fieldref_constpool_index, + )?; + + let value = stack_frame.pop(); + + self.method_area + .set_static_field_value(&class_name, &field_name, value)?; + + stack_frame.incr_pc(); + println!( + "PUTSTATIC -> class_name={class_name}, field={field_name}, value={value}" + ); + } GETFIELD => { stack_frame.incr_pc(); let high = stack_frame.get_bytecode_byte() as u16; @@ -397,10 +465,11 @@ impl<'a> Engine<'a> { .unwrap(); let objectref = stack_frame.pop(); - let field_name = self.method_area.get_fieldname_by_fieldref_cpool_index( - java_class, - fieldref_constpool_index, - )?; + let (class_name, field_name) = + self.method_area.get_fieldname_by_fieldref_cpool_index( + java_class, + fieldref_constpool_index, + )?; let value = self .heap @@ -409,7 +478,7 @@ impl<'a> Engine<'a> { stack_frame.push(value); stack_frame.incr_pc(); - println!("GETFIELD -> fieldref_constpool_index={fieldref_constpool_index}, objectref={objectref}, value={value}"); + println!("GETFIELD -> objectref={objectref}, class_name={class_name}, field_name={field_name}, value={value}"); } PUTFIELD => { stack_frame.incr_pc(); @@ -425,10 +494,11 @@ impl<'a> Engine<'a> { .get(main_class_name) .unwrap(); - let field_name = self.method_area.get_fieldname_by_fieldref_cpool_index( - java_class, - fieldref_constpool_index, - )?; + let (class_name, field_name) = + self.method_area.get_fieldname_by_fieldref_cpool_index( + java_class, + fieldref_constpool_index, + )?; let value = stack_frame.pop(); let objectref = stack_frame.pop(); @@ -436,7 +506,7 @@ impl<'a> Engine<'a> { .set_object_field_value(objectref, field_name.as_str(), value)?; stack_frame.incr_pc(); - println!("PUTFIELD -> fieldref_constpool_index={fieldref_constpool_index}, objectref={objectref}, value={value}"); + println!("PUTFIELD -> objectref={objectref}, class_name={class_name}, field_name={field_name} value={value}"); } INVOKEVIRTUAL => { println!( diff --git a/vm/src/heap/java_instance.rs b/vm/src/heap/java_instance.rs index b87507b9..62bef8b7 100644 --- a/vm/src/heap/java_instance.rs +++ b/vm/src/heap/java_instance.rs @@ -1,4 +1,5 @@ use crate::error::Error; +use crate::method_area::field::Field; use crate::method_area::java_class::JavaClass; use crate::util::get_fields; use std::collections::HashMap; @@ -28,22 +29,7 @@ impl<'a> JavaInstance<'a> { pub fn get_field_value(&self, fieldname: &str) -> crate::error::Result { self.fields .get(fieldname) - .and_then(|v| Some(v.value)) + .and_then(|v| Some(v.value())) .ok_or(Error::new_execution("error getting instance field value")) } } - -#[derive(Debug)] -pub(crate) struct Field { - value: i32, // todo: support other types -} - -impl Field { - pub fn new() -> Self { - Self { value: 0 } - } - - pub fn set_value(&mut self, value: i32) { - self.value = value; - } -} diff --git a/vm/src/method_area/field.rs b/vm/src/method_area/field.rs new file mode 100644 index 00000000..ff4dfbd0 --- /dev/null +++ b/vm/src/method_area/field.rs @@ -0,0 +1,18 @@ +#[derive(Debug)] +pub(crate) struct Field { + value: i32, // todo: support other types +} + +impl Field { + pub fn new() -> Self { + Self { value: 0 } + } + + pub fn set_value(&mut self, value: i32) { + self.value = value; + } + + pub fn value(&self) -> i32 { + self.value + } +} diff --git a/vm/src/method_area/java_class.rs b/vm/src/method_area/java_class.rs index b67fea3e..758300f3 100644 --- a/vm/src/method_area/java_class.rs +++ b/vm/src/method_area/java_class.rs @@ -1,10 +1,13 @@ +use crate::method_area::field::Field; use crate::method_area::java_method::JavaMethod; use jclass::class_file::ClassFile; +use std::cell::RefCell; use std::collections::HashMap; #[derive(Debug)] pub(crate) struct JavaClass { pub(crate) methods: Methods, + pub(crate) fields: Fields, pub(crate) class_file: ClassFile, } @@ -13,10 +16,22 @@ pub(crate) struct Methods { pub(crate) method_by_signature: HashMap, } +#[derive(Debug)] +pub(crate) struct Fields { + pub(crate) field_by_name: HashMap>, +} + +impl Fields { + pub fn new(field_by_name: HashMap>) -> Self { + Self { field_by_name } + } +} + impl JavaClass { - pub fn new(methods: Methods, class_file: ClassFile) -> Self { + pub fn new(methods: Methods, fields: Fields, class_file: ClassFile) -> Self { Self { methods, + fields, class_file, } } diff --git a/vm/src/method_area/method_area.rs b/vm/src/method_area/method_area.rs index ec86e123..60a904e6 100644 --- a/vm/src/method_area/method_area.rs +++ b/vm/src/method_area/method_area.rs @@ -19,6 +19,23 @@ impl MethodArea { } } + pub fn set_static_field_value( + &self, + class_name: &str, + fieldname: &str, + value: i32, + ) -> crate::error::Result<()> { + self.loaded_classes + .get(class_name) + .and_then(|java_class| java_class.fields.field_by_name.get(fieldname)) + .and_then(|field| { + field.borrow_mut().set_value(value); + + Some(()) + }) + .ok_or(Error::new_execution("Error modifying static field")) + } + pub(crate) fn get_method_by_name_signature( &self, class_name: &str, @@ -105,7 +122,7 @@ impl MethodArea { &self, java_class: &JavaClass, fieldref_cpool_index: u16, - ) -> crate::error::Result { + ) -> crate::error::Result<(String, String)> { let cpool = java_class.class_file.constant_pool(); // Retrieve Fieldref from the constant pool @@ -128,6 +145,9 @@ impl MethodArea { ) })?; + // Retrieve class name from the constant pool + let class_name = get_class_name_by_cpool_class_index(fieldref.0, &java_class.class_file); + // Retrieve field name from the constant pool let field_name = if let NameAndType { name_index, @@ -152,6 +172,6 @@ impl MethodArea { )); }; - Ok(field_name.unwrap()) + Ok((class_name.unwrap(), field_name.unwrap())) } } diff --git a/vm/src/method_area/mod.rs b/vm/src/method_area/mod.rs index c3d82f7f..e1e5c011 100644 --- a/vm/src/method_area/mod.rs +++ b/vm/src/method_area/mod.rs @@ -1,3 +1,4 @@ +pub(crate) mod field; pub(crate) mod java_class; pub(crate) mod java_method; pub(crate) mod method_area; diff --git a/vm/src/util.rs b/vm/src/util.rs index 63db4e72..fd72679b 100644 --- a/vm/src/util.rs +++ b/vm/src/util.rs @@ -1,7 +1,8 @@ use crate::error::Error; -use crate::heap::java_instance::Field; +use crate::method_area::field::Field; use jclass::class_file::ClassFile; use jclass::constant_pool::ConstantPool::{Class, Utf8}; +use jclass::fields::FieldFlags; use std::collections::HashMap; pub(crate) fn get_class_name_by_cpool_class_index( @@ -28,17 +29,25 @@ pub(crate) fn get_cpool_string(class_file: &ClassFile, index: usize) -> Option crate::error::Result> { - let fields = class_file.fields(); - let mut field_by_name: HashMap = HashMap::new(); + let result = class_file + .fields() + .iter() + .filter_map(|field| { + if field.access_flags().contains(FieldFlags::ACC_STATIC) { + None + } else { + let field_name = get_cpool_string(class_file, field.name_index() as usize) + .ok_or_else(|| Error::new_constant_pool("Error getting field name")) + .ok()?; + let _field_signature = + get_cpool_string(class_file, field.descriptor_index() as usize) + .ok_or_else(|| Error::new_constant_pool("Error getting field signature")) + .ok()?; - for field in fields.iter() { - let field_name = get_cpool_string(class_file, field.name_index() as usize) - .ok_or(Error::new_constant_pool("Error getting field name"))?; - let _field_signature = get_cpool_string(class_file, field.descriptor_index() as usize) - .ok_or(Error::new_constant_pool("Error getting field signature"))?; - - field_by_name.insert(field_name, Field::new()); - } + Some((field_name, Field::new())) + } + }) + .collect(); - Ok(field_by_name) + Ok(result) }