Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add basic support for instance fields #5

Merged
merged 1 commit into from
Sep 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
}