Skip to content

Commit

Permalink
Add basic support for instance fields
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Sep 2, 2024
1 parent 3666479 commit eb19e0a
Show file tree
Hide file tree
Showing 10 changed files with 245 additions and 31 deletions.
10 changes: 7 additions & 3 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ fn should_do_subtraction() {
}

#[test]
fn should_do_subtraction_with_instance() {
let vm = VM::new("tests/test_data/SubNew.class", "tests/test_data/std").unwrap();
fn should_write_read_instance_fields() {
let vm = VM::new(
"tests/test_data/InstanceFields.class",
"tests/test_data/std",
)
.unwrap();
let last_frame_value = vm.run().unwrap();
assert_eq!(-989, last_frame_value.unwrap())
assert_eq!(11022, last_frame_value.unwrap())
}

#[test]
Expand Down
Binary file added tests/test_data/InstanceFields.class
Binary file not shown.
43 changes: 43 additions & 0 deletions tests/test_data/InstanceFields.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@

public class InstanceFields {

private int resultSub;
private int resultAdd;
private int resultMul;
public InstanceFields() {
}

public static void main(String[] args) {
int first = 11;
int second = 1000;
InstanceFields instance = new InstanceFields();
instance.sub(first, second);
instance.add(first, second);
instance.mul(first, second);
int result = instance.resultSub + instance.resultAdd + instance.resultMul;
}

public void sub(int first, int second) {
resultSub = first - second;
}

public void add(int first, int second) {
resultAdd = first + second;
}

public void mul(int first, int second) {
resultMul = first * second;
}

public int getResultSub() {
return resultSub;
}

public int getResultAdd() {
return resultAdd;
}

public int getResultMul() {
return resultMul;
}
}
Binary file removed tests/test_data/SubNew.class
Binary file not shown.
18 changes: 0 additions & 18 deletions tests/test_data/SubNew.java

This file was deleted.

69 changes: 63 additions & 6 deletions vm/src/execution_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -205,12 +205,13 @@ impl<'a> Engine<'a> {
println!("DUP -> {val}");
}
IADD => {
println!("IADD");
let b = stack_frame.pop();
let a = stack_frame.pop();
stack_frame.push(a + b);
let result = a + b;
stack_frame.push(result);

stack_frame.incr_pc();
println!("IADD -> {a} + {b} = {result}");
}
ISUB => {
let b = stack_frame.pop();
Expand Down Expand Up @@ -381,6 +382,62 @@ impl<'a> Engine<'a> {
stack_frames.pop(); // Return from method, pop the current frame
// add more logic here
}
GETFIELD => {
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 objectref = stack_frame.pop();
let field_name = self.method_area.get_fieldname_by_fieldref_cpool_index(
java_class,
fieldref_constpool_index,
)?;

let value = self
.heap
.get_object_field_value(objectref, field_name.as_str())?;

stack_frame.push(value);

stack_frame.incr_pc();
println!("GETFIELD -> fieldref_constpool_index={fieldref_constpool_index}, objectref={objectref}, value={value}");
}
PUTFIELD => {
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 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}");
}
INVOKEVIRTUAL => {
println!(
"INVOKEVIRTUAL -> locals={:?}, operand_stack={:?}",
Expand Down Expand Up @@ -514,12 +571,12 @@ impl<'a> Engine<'a> {
.loaded_classes
.get(class_to_invoke_new_for.as_str())
.unwrap(),
);
let reference = self.heap.create_instance(default_field_values_instance);
)?;

stack_frame.push(reference);
let instanceref = self.heap.create_instance(default_field_values_instance);
stack_frame.push(instanceref);

println!("NEW -> class={class_constpool_index}, reference={reference}");
println!("NEW -> class={class_constpool_index}, reference={instanceref}");
stack_frame.incr_pc();
}
_ => unreachable!("{}", format! {"xxx = {}", stack_frame.get_bytecode_byte()}),
Expand Down
27 changes: 27 additions & 0 deletions vm/src/heap/heap.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use crate::error::Error;
use crate::heap::java_instance::JavaInstance;
use std::collections::HashMap;

Expand All @@ -22,4 +23,30 @@ impl<'a> Heap<'a> {

self.next_id
}

pub fn set_object_field_value(
&mut self,
objectref: i32,
fieldname: &str,
value: i32,
) -> crate::error::Result<()> {
if let Some(instance) = self.data.get_mut(&objectref) {
instance.set_field_value(fieldname, value)?;
Ok(())
} else {
Err(Error::new_execution("error setting field value"))
}
}

pub fn get_object_field_value(
&mut self,
objectref: i32,
fieldname: &str,
) -> crate::error::Result<i32> {
if let Some(java_instance) = self.data.get(&objectref) {
java_instance.get_field_value(fieldname)
} else {
Err(Error::new_execution("error getting field value"))
}
}
}
34 changes: 31 additions & 3 deletions vm/src/heap/java_instance.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,49 @@
use crate::error::Error;
use crate::method_area::java_class::JavaClass;
use crate::util::get_fields;
use std::collections::HashMap;

#[derive(Debug)]
pub(crate) struct JavaInstance<'a> {
#[allow(dead_code)]
class_ref: &'a JavaClass,
fields: HashMap<String, Field>,
}

impl<'a> JavaInstance<'a> {
pub fn new(class_ref: &'a JavaClass) -> Self {
let mut fields = HashMap::new();
pub fn new(class_ref: &'a JavaClass) -> crate::error::Result<Self> {
Ok(Self {
class_ref,
fields: get_fields(&class_ref.class_file)?,
})
}

pub fn set_field_value(&mut self, fieldname: &str, value: i32) -> crate::error::Result<()> {
self.fields
.get_mut(fieldname)
.and_then(|v| Some(v.set_value(value)))
.ok_or(Error::new_execution("error setting instance field value"))
}

Self { class_ref, fields }
pub fn get_field_value(&self, fieldname: &str) -> crate::error::Result<i32> {
self.fields
.get(fieldname)
.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;
}
}
56 changes: 55 additions & 1 deletion vm/src/method_area/method_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use crate::error::Error;
use crate::method_area::java_class::JavaClass;
use crate::method_area::java_method::JavaMethod;
use crate::util::{get_class_name_by_cpool_class_index, get_cpool_string};
use jclass::constant_pool::ConstantPool::{Methodref, NameAndType};
use jclass::constant_pool::ConstantPool::{Fieldref, Methodref, NameAndType};
use std::collections::HashMap;

#[derive(Debug)]
Expand Down Expand Up @@ -100,4 +100,58 @@ impl MethodArea {
let full_signature = format!("{}:{}", method_name.unwrap(), method_signature.unwrap());
self.get_method_by_name_signature(class_name.unwrap().as_str(), full_signature.as_str())
}

pub(crate) fn get_fieldname_by_fieldref_cpool_index(
&self,
java_class: &JavaClass,
fieldref_cpool_index: u16,
) -> crate::error::Result<String> {
let cpool = java_class.class_file.constant_pool();

// Retrieve Fieldref from the constant pool
let fieldref = cpool
.get(fieldref_cpool_index as usize)
.and_then(|entry| match entry {
Fieldref {
class_index,
name_and_type_index,
} => Some((*class_index as usize, *name_and_type_index as usize)),
_ => None,
})
.ok_or_else(|| {
Error::new_constant_pool(
format!(
"Invalid Fieldref at index {} in class {:?}",
fieldref_cpool_index, java_class
)
.as_str(),
)
})?;

// Retrieve field name from the constant pool
let field_name = if let NameAndType {
name_index,
descriptor_index: _,
} = cpool.get(fieldref.1).ok_or_else(|| {
Error::new_constant_pool(
format!(
"Invalid NameAndType reference at index {} in class {:?}",
fieldref_cpool_index, java_class
)
.as_str(),
)
})? {
get_cpool_string(&java_class.class_file, *name_index as usize)
} else {
return Err(Error::new_constant_pool(
format!(
"Expected NameAndType at index {} in class {:?}",
fieldref_cpool_index, java_class
)
.as_str(),
));
};

Ok(field_name.unwrap())
}
}
19 changes: 19 additions & 0 deletions vm/src/util.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::error::Error;
use crate::heap::java_instance::Field;
use jclass::class_file::ClassFile;
use jclass::constant_pool::ConstantPool::{Class, Utf8};
use std::collections::HashMap;

pub(crate) fn get_class_name_by_cpool_class_index(
class_index: usize,
Expand All @@ -23,3 +26,19 @@ pub(crate) fn get_cpool_string(class_file: &ClassFile, index: usize) -> Option<S
_ => None,
})
}

pub(crate) fn get_fields(class_file: &ClassFile) -> crate::error::Result<HashMap<String, Field>> {
let fields = class_file.fields();
let mut field_by_name: HashMap<String, Field> = HashMap::new();

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());
}

Ok(field_by_name)
}

0 comments on commit eb19e0a

Please sign in to comment.