Skip to content

Commit

Permalink
Add basic support for static fields
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Sep 3, 2024
1 parent eb19e0a commit 042e0c2
Show file tree
Hide file tree
Showing 13 changed files with 232 additions and 45 deletions.
7 changes: 7 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Binary file modified tests/test_data/InstanceFields.class
Binary file not shown.
2 changes: 0 additions & 2 deletions tests/test_data/InstanceFields.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Binary file added tests/test_data/StaticFields.class
Binary file not shown.
29 changes: 29 additions & 0 deletions tests/test_data/StaticFields.java
Original file line number Diff line number Diff line change
@@ -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;
}

}
38 changes: 36 additions & 2 deletions vm/src/class_loader.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
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;
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;
Expand Down Expand Up @@ -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<Methods> {
Expand Down Expand Up @@ -94,6 +101,33 @@ impl ClassLoader {
Ok(Methods::new(method_by_signature))
}

fn get_static_fields(class_file: &ClassFile) -> Result<Fields> {
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<Attribute>) -> Option<(u16, u16, Vec<u8>)> {
attributes.iter().find_map(|item| {
if let Code {
Expand Down
90 changes: 80 additions & 10 deletions vm/src/execution_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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();
Expand All @@ -425,18 +494,19 @@ 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();

self.heap
.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!(
Expand Down
18 changes: 2 additions & 16 deletions vm/src/heap/java_instance.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand Down Expand Up @@ -28,22 +29,7 @@ impl<'a> JavaInstance<'a> {
pub fn get_field_value(&self, fieldname: &str) -> crate::error::Result<i32> {
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;
}
}
18 changes: 18 additions & 0 deletions vm/src/method_area/field.rs
Original file line number Diff line number Diff line change
@@ -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
}
}
17 changes: 16 additions & 1 deletion vm/src/method_area/java_class.rs
Original file line number Diff line number Diff line change
@@ -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,
}

Expand All @@ -13,10 +16,22 @@ pub(crate) struct Methods {
pub(crate) method_by_signature: HashMap<String, JavaMethod>,
}

#[derive(Debug)]
pub(crate) struct Fields {
pub(crate) field_by_name: HashMap<String, RefCell<Field>>,
}

impl Fields {
pub fn new(field_by_name: HashMap<String, RefCell<Field>>) -> 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,
}
}
Expand Down
24 changes: 22 additions & 2 deletions vm/src/method_area/method_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -105,7 +122,7 @@ impl MethodArea {
&self,
java_class: &JavaClass,
fieldref_cpool_index: u16,
) -> crate::error::Result<String> {
) -> crate::error::Result<(String, String)> {
let cpool = java_class.class_file.constant_pool();

// Retrieve Fieldref from the constant pool
Expand All @@ -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,
Expand All @@ -152,6 +172,6 @@ impl MethodArea {
));
};

Ok(field_name.unwrap())
Ok((class_name.unwrap(), field_name.unwrap()))
}
}
1 change: 1 addition & 0 deletions vm/src/method_area/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
pub(crate) mod field;
pub(crate) mod java_class;
pub(crate) mod java_method;
pub(crate) mod method_area;
Expand Down
Loading

0 comments on commit 042e0c2

Please sign in to comment.