rusty-jvm 0.5.0

An implementation of a Java Virtual Machine (JVM).
Documentation
use crate::vm::error::{Error, Result};
use crate::vm::method_area::field::FieldValue;
use derive_new::new;
use indexmap::IndexMap;
use serde::Serialize;

pub type ClassName = String;
pub type FieldNameType = String;

#[derive(Debug, Serialize, Clone, new)]
pub(crate) struct JavaInstance {
    instance_name: String,
    fields: IndexMap<ClassName, IndexMap<FieldNameType, FieldValue>>,
}

#[derive(Debug, Clone, Serialize)]
pub(crate) struct Array {
    type_name: String,
    data: Vec<u8>,
}

impl Array {
    pub fn new(type_name: &str, len: i32) -> Self {
        Self {
            type_name: type_name.to_string(),
            data: Self::allocate_data(type_name, len as usize),
        }
    }

    fn allocate_data(type_name: &str, len: usize) -> Vec<u8> {
        vec![0; len * Self::bytes_size(type_name)]
    }

    fn bytes_size(type_name: &str) -> usize {
        match type_name {
            "[B" => 1,
            "[C" => 2,
            "[D" => 8,
            "[F" => 4,
            "[I" => 4,
            "[J" => 8,
            "[S" => 2,
            "[Z" => 1,
            _ => 4,
        }
    }

    pub fn new_with_values(type_name: &str, array: &[i32]) -> Self {
        let size = Self::bytes_size(type_name);
        let converted_arr: Vec<u8> = array
            .iter()
            .flat_map(|&value| {
                let bytes = value.to_ne_bytes();
                if cfg!(target_endian = "big") {
                    bytes[4 - size..4].to_vec()
                } else {
                    bytes[0..size].to_vec()
                }
            })
            .collect();

        Self {
            type_name: type_name.to_string(),
            data: converted_arr,
        }
    }

    pub fn get_value(&self, index: i32) -> Result<Vec<i32>> {
        let size = Self::bytes_size(&self.type_name);
        let offset = index as usize * size;

        self.get_value_by_raw_offset(offset, size)
    }

    pub fn get_value_by_raw_offset(&self, offset: usize, size: usize) -> Result<Vec<i32>> {
        match size {
            1..=4 => {
                let mut buf = [0u8; 4];
                if cfg!(target_endian = "big") {
                    buf[4 - size..4].copy_from_slice(&self.data[offset..offset + size]);
                } else {
                    buf[0..size].copy_from_slice(&self.data[offset..offset + size]);
                }
                let value = i32::from_ne_bytes(buf);
                Ok(vec![value])
            }
            8 => {
                let mut buf = [0u8; 8];
                buf.copy_from_slice(&self.data[offset..offset + size]);
                let high = i32::from_ne_bytes(buf[0..4].try_into().unwrap());
                let low = i32::from_ne_bytes(buf[4..8].try_into().unwrap());
                if cfg!(target_endian = "big") {
                    Ok(vec![high, low])
                } else {
                    Ok(vec![low, high])
                }
            }
            _ => Err(Error::new_execution(&format!("Invalid size: {size}"))),
        }
    }

    pub fn set_value(&mut self, index: i32, value: Vec<i32>) -> Result<()> {
        let size = Self::bytes_size(&self.type_name);
        let offset = index as usize * size;

        self.set_array_value_by_raw_offset(offset, value, size)
    }

    pub fn set_array_value_by_raw_offset(
        &mut self,
        offset: usize,
        value: Vec<i32>,
        size: usize,
    ) -> Result<()> {
        match size {
            1..=4 => {
                let int_buf = value[0].to_ne_bytes();
                if cfg!(target_endian = "big") {
                    self.data[offset..offset + size].copy_from_slice(&int_buf[4 - size..4]);
                } else {
                    self.data[offset..offset + size].copy_from_slice(&int_buf[0..size]);
                }
            }
            8 => {
                let mut buf = [0u8; 8];
                if cfg!(target_endian = "big") {
                    buf[0..4].copy_from_slice(&value[0].to_ne_bytes());
                    buf[4..8].copy_from_slice(&value[1].to_ne_bytes());
                } else {
                    buf[0..4].copy_from_slice(&value[1].to_ne_bytes());
                    buf[4..8].copy_from_slice(&value[0].to_ne_bytes());
                }
                self.data[offset..offset + size].copy_from_slice(&buf);
            }
            _ => return Err(Error::new_execution(&format!("Invalid size: {size}"))),
        }

        Ok(())
    }

    pub fn set_entire_value(&mut self, value: Array) -> Result<()> {
        self.data = value.data;
        Ok(())
    }

    pub fn get_entire_value(&self) -> Vec<Vec<i32>> {
        let size = Self::bytes_size(&self.type_name);
        let mut result = Vec::new();
        for i in 0..self.data.len() / size {
            let value = self.get_value(i as i32).unwrap();
            result.push(value);
        }
        result
    }

    pub fn get_length(&self) -> i32 {
        let size = Self::bytes_size(&self.type_name);
        let length = self.data.len() / size;
        length as i32
    }

    pub fn type_name(&self) -> &str {
        &self.type_name
    }

    pub fn raw_data(&self) -> &Vec<u8> {
        &self.data
    }

    pub fn raw_data_mut(&mut self) -> &mut Vec<u8> {
        &mut self.data
    }
}

#[derive(Debug, Serialize, Clone)]
pub(crate) enum HeapValue {
    Object(JavaInstance),
    Arr(Array),
}

impl JavaInstance {
    pub fn set_field_value(
        &mut self,
        class_name: &str,
        field_name_type: &str,
        value: Vec<i32>,
    ) -> Result<()> {
        self.lookup_for_field_mut(class_name, field_name_type)
            .and_then(|v| Some(v.set_raw_value(value)))
            .ok_or_else(|| {
                Error::new_execution(&format!(
                    "error setting value for instance field {class_name}.{field_name_type}"
                ))
            })?
    }

    pub fn get_field_value(&self, class_name: &str, field_name_type: &str) -> Result<Vec<i32>> {
        self.lookup_for_field(class_name, field_name_type)
            .and_then(|v| Some(v.raw_value()))
            .ok_or(Error::new_execution(&format!(
                "error getting instance field value {class_name}.{field_name_type}"
            )))?
    }

    fn lookup_for_field(
        &self,
        starting_from_class_name: &str,
        field_name_type: &str,
    ) -> Option<&FieldValue> {
        match self.fields.get_index_of(starting_from_class_name) {
            Some(start_index) => self
                .fields
                .iter()
                .take(start_index + 1)
                .rev()
                .find_map(|(_, map)| map.get(field_name_type)),
            None => None,
        }
    }

    fn lookup_for_field_mut(
        &mut self,
        starting_from_class_name: &str,
        field_name_type: &str,
    ) -> Option<&mut FieldValue> {
        match self.fields.get_index_of(starting_from_class_name) {
            Some(start_index) => self
                .fields
                .iter_mut()
                .take(start_index + 1)
                .rev()
                .find_map(|(_, map)| map.get_mut(field_name_type)),
            None => None,
        }
    }

    pub fn instance_name(&self) -> &str {
        &self.instance_name
    }
}