From ff0e682a18306b0aea6655e32fb8cebc028e7fa7 Mon Sep 17 00:00:00 2001 From: Igor Rudenko Date: Wed, 9 Oct 2024 13:07:07 +0300 Subject: [PATCH] Add support of checkcast with classes --- tests/integration_test.rs | 13 ++++--- tests/test_data/TrivialCast.java | 13 +++++++ .../javacore/cast/trivial/TrivialCast.class | Bin 0 -> 381 bytes vm/src/execution_engine/engine.rs | 30 ++++++++++++++-- vm/src/method_area/instance_checker.rs | 34 ++++++++++++++++++ vm/src/method_area/mod.rs | 1 + vm/src/stack/stack_frame.rs | 2 +- 7 files changed, 85 insertions(+), 8 deletions(-) create mode 100644 tests/test_data/TrivialCast.java create mode 100644 tests/test_data/samples/javacore/cast/trivial/TrivialCast.class create mode 100644 vm/src/method_area/instance_checker.rs diff --git a/tests/integration_test.rs b/tests/integration_test.rs index d66ab101..ef00a23b 100644 --- a/tests/integration_test.rs +++ b/tests/integration_test.rs @@ -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>) -> i32 { *locals.unwrap().last().unwrap() } @@ -306,6 +311,6 @@ fn get_long(locals_opt: Option>) -> i64 { fn get_float(locals: Option>) -> f32 { let value = *locals.unwrap().last().unwrap(); - + f32::from_bits(value as u32) } diff --git a/tests/test_data/TrivialCast.java b/tests/test_data/TrivialCast.java new file mode 100644 index 00000000..e2b42221 --- /dev/null +++ b/tests/test_data/TrivialCast.java @@ -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(); + var abstractMap = (AbstractMap) o; + + int result = 1337; + } +} diff --git a/tests/test_data/samples/javacore/cast/trivial/TrivialCast.class b/tests/test_data/samples/javacore/cast/trivial/TrivialCast.class new file mode 100644 index 0000000000000000000000000000000000000000..4bb52119ae1e57191cd143f4d4792e8d493fc4de GIT binary patch literal 381 zcmYjMO;5r=6r2ZzmIB)1$IXK`(8Rrw7!8S%7!RNa2#N8uts7j@7Se9{U!F)bF`oTV z#Tg~?@Xc!g2R`wEOCWGqU+rZ$|JZ%QBDEownlO zU*?6C;}k7k*q#c=uFSZ{w-aV!uAOZ_Z(Q)(jCO3XfdC=bR@!H)!`%6R`?Nu0bp8c@ a9M&!eJ6~t3@ZhntjV60r?C!9uf`eaN@=m@0 literal 0 HcmV?d00001 diff --git a/vm/src/execution_engine/engine.rs b/vm/src/execution_engine/engine.rs index 8e79aa6c..66c4ce39 100644 --- a/vm/src/execution_engine/engine.rs +++ b/vm/src/execution_engine/engine.rs @@ -3,6 +3,7 @@ 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}; @@ -10,12 +11,14 @@ 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(), } } @@ -668,7 +671,7 @@ impl Engine { } I2F => { let value = stack_frame.pop() as f32; - + stack_frame.push_f32(value); stack_frame.incr_pc(); @@ -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 diff --git a/vm/src/method_area/instance_checker.rs b/vm/src/method_area/instance_checker.rs new file mode 100644 index 00000000..e20e4b74 --- /dev/null +++ b/vm/src/method_area/instance_checker.rs @@ -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 { + 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 { + 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) + } +} diff --git a/vm/src/method_area/mod.rs b/vm/src/method_area/mod.rs index 17e10e50..5cd73265 100644 --- a/vm/src/method_area/mod.rs +++ b/vm/src/method_area/mod.rs @@ -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; diff --git a/vm/src/stack/stack_frame.rs b/vm/src/stack/stack_frame.rs index 3f6c627d..fa53204a 100644 --- a/vm/src/stack/stack_frame.rs +++ b/vm/src/stack/stack_frame.rs @@ -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") }