Skip to content

Commit

Permalink
Add support for trivial interfaces
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Sep 30, 2024
1 parent 0189692 commit d65cbff
Show file tree
Hide file tree
Showing 11 changed files with 171 additions and 1 deletion.
9 changes: 9 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ fn should_do_inherited_static_fields() {
assert_eq!(128, get_int(last_frame_value))
}

#[test]
fn should_do_trivial_interfaces() {
let mut vm = VM::new("std");
let last_frame_value = vm
.run("samples.inheritance.interfaces.trivial.TrivialInterface")
.unwrap();
assert_eq!(-200, get_int(last_frame_value))
}

fn get_int(locals: Option<Vec<i32>>) -> i32 {
*locals.unwrap().last().unwrap()
}
Expand Down
22 changes: 22 additions & 0 deletions tests/test_data/TrivialInterface.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package samples.inheritance.interfaces.trivial;

public class TrivialInterface {
public static void main(String[] args) {
Subtraction subtraction = new SubtractionImpl();

int one = 100;
int two = 300;
int result = subtraction.sub(one, two);
}
}

interface Subtraction {
int sub(int first, int second);
}

class SubtractionImpl implements Subtraction {
@Override
public int sub(int first, int second) {
return first - second;
}
}
Binary file not shown.
Binary file not shown.
Binary file not shown.
49 changes: 49 additions & 0 deletions vm/src/execution_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -923,6 +923,55 @@ impl Engine {
stack_frames.push(next_frame);
println!("INVOKESTATIC -> {class_name}.{method_name}(...)");
}
INVOKEINTERFACE => {
let interfacemethodref_constpool_index = Self::extract_two_bytes(stack_frame);
stack_frame.incr_pc();

let arg_count = stack_frame.get_bytecode_byte();
stack_frame.incr_pc();

let mut args = vec![0; arg_count as usize - 1];
for i in (0..(arg_count - 1)).rev() {
let val = stack_frame.pop();
args[i as usize] = val;
}
let reference = stack_frame.pop();

let zero = stack_frame.get_bytecode_byte();
stack_frame.incr_pc();
if zero != 0 {
return Err(Error::new_execution(&format!("Error calling interface method by index {interfacemethodref_constpool_index}")));
}

let method_area = self.method_area.borrow();
let rc = method_area.get(current_class_name.as_str())?;
let cpool_helper = rc.cpool_helper();

let (interface_class_name, method_name, method_descriptor) = cpool_helper
.get_full_interfacemethodref_info(interfacemethodref_constpool_index)
.ok_or_else(|| Error::new_constant_pool(&format!("Error getting full interfacemethodref info by index {interfacemethodref_constpool_index}")))?;

let heap = self.heap.borrow();
let instance_name = heap.get_instance_name(reference)?;

let full_method_signature = format!("{method_name}:{method_descriptor}");
let interface_implementation_method = method_area.lookup_for_implementation(instance_name, &full_method_signature)
.ok_or_else(|| Error::new_constant_pool(&format!("Error getting implementaion of {interface_class_name}.{method_name}{method_descriptor} in {instance_name}")))?;

let mut next_frame = interface_implementation_method.new_stack_frame();
let arg_num = interface_implementation_method
.get_signature()
.arguments_length();

next_frame.set_local(0, reference);
for i in (0..arg_num).rev() {
next_frame.set_local(i + 1, args[i]);
}

stack_frames.push(next_frame);

println!("INVOKEINTERFACE -> {interface_class_name}.{method_name}{method_descriptor}({reference}, ...) for {instance_name}");
}
NEW => {
let class_constpool_index = Self::extract_two_bytes(stack_frame);

Expand Down
10 changes: 10 additions & 0 deletions vm/src/heap/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,16 @@ impl Heap {
}
}

pub fn get_instance_name(&self, objectref: i32) -> crate::error::Result<&str> {
if let Some(Object(java_instance)) = self.data.get(&objectref) {
Ok(java_instance.instance_name())
} else {
Err(Error::new_execution(&format!(
"error getting object from heap by ref {objectref}"
)))
}
}

pub(crate) fn create_array(&mut self, len: i32) -> i32 {
self.next_id = self.next_id + 1; //todo: make me atomic

Expand Down
6 changes: 6 additions & 0 deletions vm/src/heap/java_instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::rc::Rc;

#[derive(Debug)]
pub(crate) struct JavaInstance {
instance_name: String,
_class_ref: Rc<JavaClass>, // todo use me or delete
fields: HashMap<String, Field>,
}
Expand Down Expand Up @@ -53,6 +54,7 @@ pub(crate) enum HeapValue {
impl<'a> JavaInstance {
pub fn new(class_ref: Rc<JavaClass>) -> crate::error::Result<Self> {
Ok(Self {
instance_name: class_ref.this_class_name().to_owned(),
_class_ref: Rc::clone(&class_ref),
fields: class_ref //todo: refactor me: remove static fields, put this code in right place
.field_descriptors
Expand Down Expand Up @@ -84,4 +86,8 @@ impl<'a> JavaInstance {
.and_then(|v| Some(v.raw_value()))
.ok_or(Error::new_execution("error getting instance field value"))
}

pub fn instance_name(&self) -> &str {
&self.instance_name
}
}
56 changes: 55 additions & 1 deletion vm/src/method_area/cpool_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,25 @@ impl CPoolHelper {
Some((class_name, method_name, method_descriptor))
}

pub fn get_full_interfacemethodref_info(
&self,
index: u16,
) -> Option<(String, String, String)> {
let (class_index, name_and_type_index) =
match self.get(CPoolType::InterfaceMethodref, index)? {
ConstantPool::InterfaceMethodref {
class_index,
name_and_type_index,
} => Some((class_index, name_and_type_index)),
_ => None,
}?;

let class_name = self.get_class_name(*class_index)?;
let (method_name, method_descriptor) = self.get_name_and_type(*name_and_type_index)?;

Some((class_name, method_name, method_descriptor))
}

pub fn get_name_and_type(&self, index: u16) -> Option<(String, String)> {
let (name_index, descriptor_index) = match self.get(CPoolType::NameAndType, index)? {
ConstantPool::NameAndType {
Expand All @@ -150,7 +169,7 @@ impl CPoolHelper {
mod tests {
use super::*;
use jclass::constant_pool::ConstantPool::{
Class, Empty, Fieldref, Integer, Long, Methodref, NameAndType, Utf8,
Class, Empty, Fieldref, Integer, InterfaceMethodref, Long, Methodref, NameAndType, Utf8,
};

#[test]
Expand Down Expand Up @@ -423,6 +442,41 @@ mod tests {
);
}

#[test]
fn should_return_full_interfacemethod_info() {
let resolver = CPoolHelper::new(&vec![
Empty,
InterfaceMethodref {
class_index: 2,
name_and_type_index: 3,
},
Class { name_index: 4 },
NameAndType {
name_index: 5,
descriptor_index: 6,
},
Utf8 {
value: "Interface".to_string(),
},
Utf8 {
value: "sub".to_string(),
},
Utf8 {
value: "(II)I".to_string(),
},
]);

let actual = resolver.get_full_interfacemethodref_info(1);
assert_eq!(
Some((
"Interface".to_string(),
"sub".to_string(),
"(II)I".to_string()
)),
actual
);
}

#[test]
fn should_return_name_and_type() {
let resolver = CPoolHelper::new(&vec![
Expand Down
4 changes: 4 additions & 0 deletions vm/src/method_area/java_class.rs
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,10 @@ impl JavaClass {

Ok(())
}

pub fn this_class_name(&self) -> &str {
&self.this_class_name
}
}

impl Methods {
Expand Down
16 changes: 16 additions & 0 deletions vm/src/method_area/method_area.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,20 @@ impl MethodArea {
self.lookup_for_static_field(&parent_class_name, field_name)
}
}

pub fn lookup_for_implementation(
&self,
class_name: &str,
full_method_signature: &str,
) -> Option<Rc<JavaMethod>> {
let rc = self.get(class_name).ok()?;

if let Some(java_method) = rc.methods.method_by_signature.get(full_method_signature) {
Some(Rc::clone(&java_method))
} else {
let parent_class_name = rc.parent().clone()?;

self.lookup_for_implementation(&parent_class_name, full_method_signature)
}
}
}

0 comments on commit d65cbff

Please sign in to comment.