Skip to content

Commit

Permalink
Trivial operations with HashMap (including new ops and bugfixes)
Browse files Browse the repository at this point in the history
  • Loading branch information
hextriclosan committed Oct 9, 2024
1 parent ff0e682 commit 1fee57c
Show file tree
Hide file tree
Showing 5 changed files with 121 additions and 40 deletions.
9 changes: 9 additions & 0 deletions tests/integration_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,15 @@ fn should_do_trivial_cast() {
assert_eq!(1337, get_int(last_frame_value))
}

#[test]
fn should_do_trivial_hashmaps() {
let mut vm = VM::new("std");
let last_frame_value = vm
.run("samples.javabase.util.hashmap.trivial.TrivialHashMap")
.unwrap();
assert_eq!(-999, get_int(last_frame_value))
}

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

import java.util.Map;
import java.util.HashMap;

public class TrivialHashMap {

public static void main(String[] args) {
Map<Integer, Integer> map = new HashMap<>();
map.put(13, -999);

int result = map.get(13);
}
}
Binary file not shown.
134 changes: 95 additions & 39 deletions vm/src/execution_engine/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ impl Engine {
LDC_W => {
//todo: merge me with LDC
stack_frame.incr_pc();
let cpoolindex = Self::extract_two_bytes(stack_frame);
let cpoolindex = Self::extract_two_bytes(stack_frame) as u16;

let java_class = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand All @@ -132,7 +132,7 @@ impl Engine {
println!("LDC -> cpoolindex={cpoolindex}, value={value}");
}
LDC2_W => {
let cpoolindex = Self::extract_two_bytes(stack_frame);
let cpoolindex = Self::extract_two_bytes(stack_frame) as u16;

let java_class = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand Down Expand Up @@ -646,6 +646,16 @@ impl Engine {
stack_frame.incr_pc();
println!("IAND -> {a} & {b} = {result}");
}
IXOR => {
let b = stack_frame.pop();
let a = stack_frame.pop();

let result = a ^ b;
stack_frame.push(result);

stack_frame.incr_pc();
println!("IXOR -> {a} & {b} = {result}");
}
IINC => {
stack_frame.incr_pc();
let index = stack_frame.get_bytecode_byte() as usize;
Expand Down Expand Up @@ -749,6 +759,9 @@ impl Engine {
IF_ACMPEQ => {
Self::branch(|a: i32, b| a == b, stack_frame, "IF_ACMPEQ");
}
IF_ACMPNE => {
Self::branch(|a: i32, b| a != b, stack_frame, "IF_ACMPEQ");
}
GOTO => {
let offset = Self::get_two_bytes_ahead(stack_frame);
stack_frame.advance_pc(offset);
Expand Down Expand Up @@ -800,7 +813,7 @@ impl Engine {
stack_frames.pop();
}
GETSTATIC => {
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame);
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;

let java_class = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand Down Expand Up @@ -829,7 +842,7 @@ impl Engine {
stack_frame.incr_pc();
}
PUTSTATIC => {
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame);
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;

let rc = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand Down Expand Up @@ -861,7 +874,7 @@ impl Engine {
stack_frame.incr_pc();
}
GETFIELD => {
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame);
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;

let rc = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand All @@ -888,7 +901,7 @@ impl Engine {
println!("GETFIELD -> objectref={objectref}, class_name={class_name}, field_name_type={field_name_type}, value={value:?}");
}
PUTFIELD => {
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame);
let fieldref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;

let rc = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand Down Expand Up @@ -931,7 +944,7 @@ impl Engine {
stack_frame.incr_pc();
}
INVOKEVIRTUAL => {
let methodref_constpool_index = Self::extract_two_bytes(stack_frame);
let methodref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;
stack_frame.incr_pc();

let rc = with_method_area(|method_area| {
Expand Down Expand Up @@ -989,7 +1002,7 @@ impl Engine {
);
}
INVOKESPECIAL => {
let methodref_constpool_index = Self::extract_two_bytes(stack_frame);
let methodref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;
stack_frame.incr_pc();

let rc = with_method_area(|method_area| {
Expand Down Expand Up @@ -1030,7 +1043,7 @@ impl Engine {
println!("INVOKESPECIAL -> {class_name}.{method_name}({method_args:?})");
}
INVOKESTATIC => {
let methodref_constpool_index = Self::extract_two_bytes(stack_frame);
let methodref_constpool_index = Self::extract_two_bytes(stack_frame) as u16;
stack_frame.incr_pc();

let rc = with_method_area(|method_area| {
Expand Down Expand Up @@ -1083,7 +1096,8 @@ impl Engine {
println!("INVOKESTATIC -> {class_name}.{method_name}({method_args:?})");
}
INVOKEINTERFACE => {
let interfacemethodref_constpool_index = Self::extract_two_bytes(stack_frame);
let interfacemethodref_constpool_index =
Self::extract_two_bytes(stack_frame) as u16;
stack_frame.incr_pc();

let arg_count = stack_frame.get_bytecode_byte() as usize;
Expand Down Expand Up @@ -1134,7 +1148,7 @@ impl Engine {
println!("INVOKEINTERFACE -> {interface_class_name}.{method_name}{method_descriptor}({method_args:?}) on instance {instance_name}");
}
NEW => {
let class_constpool_index = Self::extract_two_bytes(stack_frame);
let class_constpool_index = Self::extract_two_bytes(stack_frame) as u16;

let rc = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
Expand Down Expand Up @@ -1175,7 +1189,7 @@ impl Engine {
ANEWARRAY => {
let length = stack_frame.pop();

let class_constpool_index = Self::extract_two_bytes(stack_frame);
let class_constpool_index = Self::extract_two_bytes(stack_frame) as u16;
let rc = with_method_area(|method_area| {
method_area.get(current_class_name.as_str())
})?;
Expand Down Expand Up @@ -1204,44 +1218,86 @@ impl Engine {
println!("ARRAYLENGTH -> arrayref={arrayref}, len={len}");
}
CHECKCAST => {
let class_constpool_index = Self::extract_two_bytes(stack_frame);
let class_constpool_index = Self::extract_two_bytes(stack_frame) as u16;
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();
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

if objectref != 0 {
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 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}");
}
INSTANCEOF => {
// todo: merge me with CHECKCAST
let class_constpool_index = Self::extract_two_bytes(stack_frame) as u16;
stack_frame.incr_pc();
let mut objectref = stack_frame.pop();

if objectref != 0 {
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 instance_class_name =
with_heap_read_lock(|heap| heap.get_instance_name(objectref))?;

let instanse_of = self
.instance_checker
.checkcast(&instance_class_name, &class_name)?;
objectref = if instanse_of { 1 } else { 0 };
}

stack_frame.push(objectref);

println!("INSTANCEOF -> class_constpool_index={class_constpool_index}, objectref={objectref}");
}
IFNULL => {
//todo: this one is opposite to IFNE ops code
let value = stack_frame.pop();
let offset = Self::get_two_bytes_ahead(stack_frame);
stack_frame.advance_pc(if value == 0 { offset } else { 3 });
println!("IFNULL -> value={value}, offset={offset}");
}
IFNONNULL => {
//todo: this one is opposite to IFNULL ops code
let value = stack_frame.pop();
let offset = Self::get_two_bytes_ahead(stack_frame);
stack_frame.advance_pc(if value != 0 { offset } else { 3 });
println!("IFNONNULL -> value={value}, offset={offset}");
}
_ => unreachable!("{}", format! {"xxx = {}", stack_frame.get_bytecode_byte()}),
}
}
Expand All @@ -1256,13 +1312,13 @@ impl Engine {
((branchbyte1 << 8) | branchbyte2) as i16
}

fn extract_two_bytes(stack_frame: &mut StackFrame) -> u16 {
fn extract_two_bytes(stack_frame: &mut StackFrame) -> i16 {
stack_frame.incr_pc();
let high = stack_frame.get_bytecode_byte() as u16;
let high = stack_frame.get_bytecode_byte() as i16;
stack_frame.incr_pc();
let low = stack_frame.get_bytecode_byte() as u16;
let low = stack_frame.get_bytecode_byte() as i16;

(high << 8) | low
(high << 8) | (low)
}

fn branch<T>(op: impl Fn(T, T) -> bool, stack_frame: &mut StackFrame, op_code: &str)
Expand Down
4 changes: 3 additions & 1 deletion vm/src/heap/heap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ impl Heap {
if let Some(Object(java_instance)) = self.data.get(&objectref) {
java_instance.get_field_value(class_name, field_name_type)
} else {
Err(Error::new_execution("error getting field value"))
Err(Error::new_execution(&format!(
"error getting field value {class_name}.{field_name_type}"
)))
}
}

Expand Down

0 comments on commit 1fee57c

Please sign in to comment.