rusty-jvm 0.5.0

An implementation of a Java Virtual Machine (JVM).
Documentation
use crate::vm::error::{Error, Result};
use crate::vm::execution_engine::common::last_frame_mut;
use crate::vm::execution_engine::opcode::*;
use crate::vm::execution_engine::ops_comparison_processor::branch1arg;
use crate::vm::execution_engine::ops_load_processor::handle_load;
use crate::vm::execution_engine::ops_math_processor::increment;
use crate::vm::execution_engine::ops_store_processor::handle_store;
use crate::vm::heap::heap::with_heap_write_lock;
use crate::vm::method_area::cpool_helper::CPoolHelperTrait;
use crate::vm::method_area::method_area::with_method_area;
use crate::vm::stack::stack_frame::{StackFrame, StackFrames};
use crate::vm::stack::stack_value::StackValue;
use std::fmt::Display;
use tracing::trace;

pub(crate) fn process(
    code: u8,
    current_class_name: &str,
    stack_frames: &mut StackFrames,
) -> Result<()> {
    let stack_frame = last_frame_mut(stack_frames)?;
    match code {
        WIDE => {
            let opcode = stack_frame.extract_one_byte();
            match opcode {
                ILOAD => handle_pos_and_load::<i32>(stack_frame, "WIDE ILOAD ")?,
                LLOAD => handle_pos_and_load::<i64>(stack_frame, "WIDE LLOAD ")?,
                FLOAD => handle_pos_and_load::<f32>(stack_frame, "WIDE FLOAD ")?,
                DLOAD => handle_pos_and_load::<f64>(stack_frame, "WIDE DLOAD ")?,
                ALOAD => handle_pos_and_load::<i32>(stack_frame, "WIDE ALOAD ")?,
                ISTORE => handle_pos_and_store::<i32>(stack_frame, "WIDE ISTORE "),
                LSTORE => handle_pos_and_store::<i64>(stack_frame, "WIDE LSTORE "),
                FSTORE => handle_pos_and_store::<f32>(stack_frame, "WIDE FSTORE "),
                DSTORE => handle_pos_and_store::<f64>(stack_frame, "WIDE DSTORE "),
                ASTORE => handle_pos_and_store::<i32>(stack_frame, "WIDE ASTORE "),
                IINC => increment(
                    stack_frame,
                    |sf| sf.extract_two_bytes() as usize,
                    |sf| sf.extract_two_bytes(),
                    "WIDE IINC",
                ),
                _ => {
                    return Err(Error::new_execution(&format!(
                        "Error executing WIDE opcode {opcode}"
                    )))
                }
            }
        }
        MULTIANEWARRAY => {
            let class_index = stack_frame.extract_two_bytes();
            let dimension_number = stack_frame.extract_one_byte();

            let rc = with_method_area(|method_area| method_area.get(current_class_name))?;
            let cpool_helper = rc.cpool_helper();
            let class_name = cpool_helper
                .get_class_name(class_index as u16)
                .ok_or_else(|| {
                    Error::new_execution(&format!(
                        "Error getting class name by index {class_index}"
                    ))
                })?;

            let dimentions = (0..dimension_number)
                .map(|_| stack_frame.pop())
                .collect::<Vec<_>>()
                .into_iter()
                .rev()
                .collect::<Vec<_>>();

            let root_array_ref = create_n_array(&dimentions, &class_name, 0)?;
            stack_frame.push(root_array_ref)?;

            stack_frame.incr_pc();
            trace!("MULTIANEWARRAY -> type={class_name}, dimension_number={dimension_number}, root_array_ref={root_array_ref}");
        }
        IFNULL => branch1arg(|a| a == 0, stack_frame, "IFNULL"),
        IFNONNULL => branch1arg(|a| a != 0, stack_frame, "IFNONNULL"),
        GOTO_W => {
            let stack_frame = last_frame_mut(stack_frames)?;
            let offset = stack_frame.get_four_bytes_ahead();
            stack_frame.advance_pc_wide(offset);
            trace!("GOTO_W -> offset={offset}");
        }
        _ => {
            return Err(Error::new_execution(&format!(
                "Unknown extended opcode: {}",
                code
            )));
        }
    }

    Ok(())
}

fn handle_pos_and_load<T: StackValue + Display + Copy>(
    stack_frame: &mut StackFrame,
    name_starts: &str,
) -> Result<()> {
    let pos = stack_frame.extract_two_bytes();
    handle_load::<T, _>(stack_frame, pos as usize, name_starts)
}

fn handle_pos_and_store<T: StackValue + Display + Copy>(
    stack_frame: &mut StackFrame,
    name_starts: &str,
) {
    let pos = stack_frame.extract_two_bytes();
    handle_store::<T, _>(stack_frame, pos as usize, name_starts);
}

fn create_n_array(dimensions: &[i32], signature: &str, current_level: usize) -> Result<i32> {
    let current_length = dimensions[current_level];
    let current_signature = &signature[current_level..];
    let arrayref =
        with_heap_write_lock(|heap| heap.create_array(current_signature, current_length));

    if current_level < dimensions.len() - 1 {
        for i in 0..current_length {
            let next_ref = create_n_array(dimensions, signature, current_level + 1)?;
            with_heap_write_lock(|heap| heap.set_array_value(arrayref, i, vec![next_ref]))?;
        }
    }

    Ok(arrayref)
}