diff --git a/tests/should_do_trivial_switch.rs b/tests/should_do_trivial_switch.rs new file mode 100644 index 00000000..559a37ae --- /dev/null +++ b/tests/should_do_trivial_switch.rs @@ -0,0 +1,12 @@ +mod utils; +use utils::get_int; +use utils::setup; + +#[test] +fn should_do_trivial_switch() { + let mut vm = setup(); + let last_frame_value = vm + .run("samples.javacore.switches.trivial.SwitchExample") + .unwrap(); + assert_eq!(300, get_int(last_frame_value)) +} diff --git a/tests/test_data/SwitchExample.java b/tests/test_data/SwitchExample.java new file mode 100644 index 00000000..5ec479f4 --- /dev/null +++ b/tests/test_data/SwitchExample.java @@ -0,0 +1,30 @@ +package samples.javacore.switches.trivial; + +public class SwitchExample { + + public static void main(String[] args) { + int value = 30; + int result = switched(value); + } + + private static int switched(int value) { + int result; + switch (value) { + case 10: + result = 100; + break; + case 20: + result = 200; + break; + case 30: + result = 300; + break; + default: + result = -1; + break; + } + + return result; + } + +} diff --git a/tests/test_data/samples/javacore/switches/trivial/SwitchExample.class b/tests/test_data/samples/javacore/switches/trivial/SwitchExample.class new file mode 100644 index 00000000..36465d1e Binary files /dev/null and b/tests/test_data/samples/javacore/switches/trivial/SwitchExample.class differ diff --git a/vm/src/execution_engine/engine.rs b/vm/src/execution_engine/engine.rs index 3e7dbfcf..57b77f69 100644 --- a/vm/src/execution_engine/engine.rs +++ b/vm/src/execution_engine/engine.rs @@ -1013,6 +1013,35 @@ impl Engine { stack_frame.advance_pc(offset); println!("GOTO -> offset={offset}"); } + LOOKUPSWITCH => { + let key = stack_frame.pop(); + let instruction_pc = stack_frame.pc() as i16; + stack_frame.adjust_pc_to_4(); + + stack_frame.advance_pc(-1); + let default_offset = Self::extract_four_bytes(stack_frame) as i16; + let npairs = Self::extract_four_bytes(stack_frame); + + let mut match_found = false; + for _ in 0..npairs { + let case_key = Self::extract_four_bytes(stack_frame); + let offset = Self::extract_four_bytes(stack_frame) as i16; + + if key == case_key { + let current_pc = stack_frame.pc() as i16; + stack_frame.advance_pc(offset + instruction_pc - current_pc); + match_found = true; + break; + } + } + + if !match_found { + let current_pc = stack_frame.pc() as i16; + stack_frame.advance_pc(default_offset + instruction_pc - current_pc); + } + + println!("LOOKUPSWITCH -> default_offset={default_offset}, npairs={npairs}"); + } IRETURN => { let ret = stack_frame.pop(); stack_frames.pop(); @@ -1564,6 +1593,19 @@ impl Engine { (high << 8) | (low) } + fn extract_four_bytes(stack_frame: &mut StackFrame) -> i32 { + stack_frame.incr_pc(); + let byte1 = stack_frame.get_bytecode_byte() as u32; + stack_frame.incr_pc(); + let byte2 = stack_frame.get_bytecode_byte() as u32; + stack_frame.incr_pc(); + let byte3 = stack_frame.get_bytecode_byte() as u32; + stack_frame.incr_pc(); + let byte4 = stack_frame.get_bytecode_byte() as u32; + + ((byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4) as i32 + } + fn branch(op: impl Fn(T, T) -> bool, stack_frame: &mut StackFrame, op_code: &str) where T: PartialOrd + TryFrom + Copy + std::fmt::Display, diff --git a/vm/src/stack/stack_frame.rs b/vm/src/stack/stack_frame.rs index a76441c0..8d39f1f5 100644 --- a/vm/src/stack/stack_frame.rs +++ b/vm/src/stack/stack_frame.rs @@ -29,6 +29,17 @@ impl<'a> StackFrame { self.advance_pc(1) } + pub fn adjust_pc_to_4(&mut self) { + while self.pc % 4 != 0 { + self.pc += 1; + } + } + + pub fn pc(&self) -> usize { + println!("bytecode_ref: {:?}", self.bytecode_ref); + self.pc + } + pub(crate) fn advance_pc(&mut self, offset: i16) { if offset >= 0 { self.pc += offset as usize;