Skip to content

Commit

Permalink
Add support of checkcast with classes
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Oct 9, 2024
1 parent c84bd29 commit ff0e682
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 8 deletions.
13 changes: 9 additions & 4 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -284,12 +284,17 @@ fn should_do_trivial_reflection() {
#[test]
fn should_do_subtraction_with_floats() {
let mut vm = VM::new("std");
let last_frame_value = vm
.run("samples.arithmetics.sub.floats.SubFloats")
.unwrap();
let last_frame_value = vm.run("samples.arithmetics.sub.floats.SubFloats").unwrap();
assert_eq!(-998.9, get_float(last_frame_value))
}

#[test]
fn should_do_trivial_cast() {
let mut vm = VM::new("std");
let last_frame_value = vm.run("samples.javacore.cast.trivial.TrivialCast").unwrap();
assert_eq!(1337, get_int(last_frame_value))
}

fn get_int(locals: Option<Vec<i32>>) -> i32 {
*locals.unwrap().last().unwrap()
}
Expand All @@ -306,6 +311,6 @@ fn get_long(locals_opt: Option<Vec<i32>>) -> i64 {

fn get_float(locals: Option<Vec<i32>>) -> f32 {
let value = *locals.unwrap().last().unwrap();

f32::from_bits(value as u32)
}
13 changes: 13 additions & 0 deletions tests/test_data/TrivialCast.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package samples.javacore.cast.trivial;

import java.util.AbstractMap;
import java.util.HashMap;

public class TrivialCast {
public static void main(String[] args) {
Object o = new HashMap<Integer, Integer>();
var abstractMap = (AbstractMap<Integer, Integer>) o;

int result = 1337;
}
}
Binary file not shown.
30 changes: 27 additions & 3 deletions vm/src/execution_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,22 @@ use crate::execution_engine::ldc_resolution_manager::LdcResolutionManager;
use crate::execution_engine::opcode::*;
use crate::execution_engine::system_native_table::invoke_native_method;
use crate::heap::heap::{with_heap_read_lock, with_heap_write_lock};
use crate::method_area::instance_checker::InstanceChecker;
use crate::method_area::java_method::JavaMethod;
use crate::method_area::method_area::with_method_area;
use crate::stack::stack_frame::{i32toi64, StackFrame};
use jdescriptor::get_length;

pub(crate) struct Engine {
ldc_resolution_manager: LdcResolutionManager,
instance_checker: InstanceChecker,
}

impl Engine {
pub fn new() -> Self {
Self {
ldc_resolution_manager: LdcResolutionManager::new(),
instance_checker: InstanceChecker::new(),
}
}

Expand Down Expand Up @@ -668,7 +671,7 @@ impl Engine {
}
I2F => {
let value = stack_frame.pop() as f32;

stack_frame.push_f32(value);

stack_frame.incr_pc();
Expand Down Expand Up @@ -1203,13 +1206,34 @@ impl Engine {
CHECKCAST => {
let class_constpool_index = Self::extract_two_bytes(stack_frame);
stack_frame.incr_pc();
let rc = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
})?;
let cpool_helper = rc.cpool_helper();
let class_name = cpool_helper
.get_class_name(class_constpool_index)
.ok_or_else(|| {
Error::new_constant_pool(&format!(
"Error getting class name by index {class_constpool_index}"
))
})?;

let objectref = stack_frame.pop();
// todo: implementation here
let instance_class_name =
with_heap_read_lock(|heap| heap.get_instance_name(objectref))?;

let possible_cast = self
.instance_checker
.checkcast(&instance_class_name, &class_name)?;
if !possible_cast {
return Err(Error::new_execution(&format!(
"Error casting {instance_class_name} to {class_name}"
))); //todo: throw ClassCastException here
}

stack_frame.push(objectref);

println!("CHECKCAST -> class_constpool_index={class_constpool_index}, objectref={objectref}");
todo!("add implementation");
}
IFNULL => {
//todo: this one is opposite to IFNE ops code
Expand Down
34 changes: 34 additions & 0 deletions vm/src/method_area/instance_checker.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
use crate::method_area::method_area::with_method_area;

pub(crate) struct InstanceChecker {}

impl InstanceChecker {
pub(crate) fn new() -> Self {
Self {}
}
}

impl InstanceChecker {
pub fn checkcast(
&self,
class_cast_from: &str,
class_cast_to: &str,
) -> crate::error::Result<bool> {
if let Some(base_of) = Self::is_base_of(class_cast_to, class_cast_from) {
return Ok(base_of);
}

Ok(false)
}

fn is_base_of(base: &str, child: &str) -> Option<bool> {
if base == child {
return Some(true);
}

let class = with_method_area(|method_area| method_area.get(child)).ok()?;
let class_name = class.parent().clone()?;

Self::is_base_of(base, &class_name)
}
}
1 change: 1 addition & 0 deletions vm/src/method_area/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod attributes_helper;
mod cpool_helper;
pub(crate) mod field;
pub(crate) mod instance_checker;
pub(crate) mod java_class;
pub(crate) mod java_method;
pub(crate) mod method_area;
2 changes: 1 addition & 1 deletion vm/src/stack/stack_frame.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ impl<'a> StackFrame {

f32::from_bits(high as u32)
}

pub fn pop(&mut self) -> i32 {
self.operand_stack.pop().expect("Empty stack")
}
Expand Down

0 comments on commit ff0e682

Please sign in to comment.