use crate::throw_and_return;
use crate::vm::commons::auto_dash_map::auto_dash_map::AutoDashMap;
use crate::vm::commons::auto_dash_map::auto_dash_map_i32::AutoDashMapI32;
use crate::vm::error::{Error, Result};
use crate::vm::exception::helpers::throw_null_pointer_exception_with_message;
use crate::vm::exception::throwing_result::ThrowingResult;
use crate::vm::heap::java_instance::HeapValue::{Arr, Object};
use crate::vm::heap::java_instance::{Array, HeapValue, JavaInstance};
use crate::vm::stack::stack_frame::StackFrames;
use dashmap::mapref::one::{MappedRef, MappedRefMut, Ref, RefMut};
use dashmap::DashMap;
use serde::Serialize;
use std::fs::File;
use std::sync::LazyLock;
use std::time::{SystemTime, UNIX_EPOCH};
static HEAP: LazyLock<Heap> = LazyLock::new(Heap::default);
pub(crate) fn with_heap_read_lock<F, R>(f: F) -> R
where
F: FnOnce(&Heap) -> R,
{
f(&HEAP)
}
pub(crate) fn with_heap_write_lock<F, R>(f: F) -> R
where
F: FnOnce(&Heap) -> R,
{
f(&HEAP)
}
#[derive(Debug, Serialize)]
pub(crate) struct Heap {
data: AutoDashMapI32<HeapValue>,
ref_by_stringvalue: DashMap<String, i32>,
}
impl Default for Heap {
fn default() -> Self {
Self {
data: AutoDashMapI32::new(1), ref_by_stringvalue: DashMap::new(),
}
}
}
impl Heap {
pub fn create_instance(&self, java_instance: JavaInstance) -> i32 {
self.data.insert_auto(Object(java_instance))
}
pub fn set_object_field_value(
&self,
objectref: i32,
class_name: &str,
field_name_type: &str,
value: Vec<i32>,
) -> Result<()> {
if objectref == 0 {
return Err(Error::new_execution(&format!(
"error setting field value: {class_name} to null-object"
))); }
self.data.get_mut(objectref)
.and_then(|mut entry| {
match entry.value_mut() {
Object(instance) => {
instance.set_field_value(class_name, field_name_type, value.clone()).ok()?;
Some(())
},
_ => None,
}
})
.ok_or_else(|| Error::new_execution(&format!("error setting field value: objectref={objectref} class_name={class_name}, field_name_type={field_name_type}, value={value:?}")))
}
pub fn get_object_field_value(
&self,
objectref: i32,
class_name: &str,
field_name_type: &str,
) -> Result<Vec<i32>> {
if objectref == 0 {
return Err(Error::new_execution(&format!(
"error getting field value: {class_name} from null-object"
))); }
self.data.get(objectref)
.and_then(|entry| {
match entry.value() {
Object(java_instance) => {
let ret = java_instance.get_field_value(class_name, field_name_type).ok()?;
Some(ret)
},
_ => None,
}
})
.ok_or_else(|| Error::new_execution(&format!("error getting field value: objectref={objectref} class_name={class_name}, field_name_type={field_name_type}")))
}
pub fn get_object_field_value_throwing(
&self,
objectref: i32,
class_name: &str,
field_name_type: &str,
stack_frames: &mut StackFrames,
) -> ThrowingResult<Vec<i32>> {
if objectref == 0 {
throw_and_return!(throw_null_pointer_exception_with_message(
&format!("Cannot read field \"{field_name_type}\" because \"<VAR_NAME>\" is null"),
stack_frames
))
}
let result = self.get_object_field_value(objectref, class_name, field_name_type);
ThrowingResult::Result(result)
}
pub fn get_instance_name(&self, objectref: i32) -> Result<String> {
self.data
.get(objectref)
.and_then(|entry| match entry.value() {
Object(java_instance) => {
let name = java_instance.instance_name().to_string();
Some(name)
}
Arr(array) => {
let name = array.type_name().to_string();
Some(name)
}
})
.ok_or_else(|| {
Error::new_execution(&format!(
"error getting object from heap by ref {objectref}"
))
})
}
pub(crate) fn create_array(&self, type_name: &str, len: i32) -> i32 {
self.data.insert_auto(Arr(Array::new(type_name, len)))
}
pub(crate) fn create_array_with_values(&self, type_name: &str, array: &[i32]) -> i32 {
self.data
.insert_auto(Arr(Array::new_with_values(type_name, array)))
}
pub(crate) fn get_entire_array(&self, array_ref: i32) -> Result<Array> {
if array_ref == 0 {
return Err(Error::new_execution(
"NullPointerException: null array reference",
)); }
self.data
.get(array_ref)
.and_then(|entry| match entry.value() {
Arr(array) => {
let cloned = array.clone();
Some(cloned)
}
_ => None,
})
.ok_or_else(|| Error::new_execution("error getting array"))
}
pub(crate) fn get_entire_raw_data(
&self,
array_ref: i32,
) -> Result<MappedRef<i32, HeapValue, Vec<u8>>> {
if array_ref == 0 {
return Err(Error::new_execution(
"NullPointerException: null array reference",
)); }
self.data
.get(array_ref)
.and_then(|entry| match entry.value() {
Arr(_) => Some(Ref::map(entry, |value| match value {
Arr(arr) => arr.raw_data(),
_ => unreachable!("already checked that this is an array"),
})),
Object(_) => None,
})
.ok_or_else(|| Error::new_execution(&format!("instance is not an array: {array_ref}")))
}
pub(crate) fn get_entire_raw_data_mut(
&self,
array_ref: i32,
) -> Result<MappedRefMut<i32, HeapValue, Vec<u8>>> {
if array_ref == 0 {
return Err(Error::new_execution(
"NullPointerException: null array reference",
)); }
self.data
.get_mut(array_ref)
.and_then(|entry| match entry.value() {
Arr(_) => Some(RefMut::map(entry, |value| match value {
Arr(arr) => arr.raw_data_mut(),
_ => unreachable!("already checked that this is an array"),
})),
Object(_) => None,
})
.ok_or_else(|| Error::new_execution(&format!("instance is not an array: {array_ref}")))
}
pub(crate) fn set_entire_array(&self, array_ref: i32, array_val: Array) -> Result<()> {
self.data
.get_mut(array_ref)
.and_then(|mut entry| match entry.value_mut() {
Arr(array) => {
array.set_entire_value(array_val).ok()?;
Some(())
}
_ => None,
})
.ok_or_else(|| Error::new_execution("error getting array"))
}
pub(crate) fn get_array_value(&self, arrayref: i32, index: i32) -> Result<Vec<i32>> {
self.data
.get(arrayref)
.and_then(|entry| match entry.value() {
Arr(arr) => {
let value = arr.get_value(index).ok()?;
Some(value)
}
_ => None,
})
.ok_or_else(|| Error::new_execution("error getting array value from heap"))
}
pub(crate) fn get_array_value_by_raw_offset(
&self,
arrayref: i32,
offset: usize,
len: usize,
) -> Result<Vec<i32>> {
self.data
.get(arrayref)
.and_then(|entry| match entry.value() {
Arr(arr) => {
let value = arr.get_value_by_raw_offset(offset, len).ok()?;
Some(value)
}
_ => None,
})
.ok_or_else(|| {
Error::new_execution("error getting array value from heap by raw offset")
})
}
pub(crate) fn set_array_value(
&self,
arrayref: i32,
index: i32,
value: Vec<i32>,
) -> Result<()> {
self.data
.get_mut(arrayref)
.and_then(|mut entry| match entry.value_mut() {
Arr(arr) => {
arr.set_value(index, value).ok()?;
Some(())
}
_ => None,
})
.ok_or_else(|| Error::new_execution("error setting array value"))
}
pub(crate) fn set_array_value_by_raw_offset(
&self,
arrayref: i32,
offset: usize,
value: Vec<i32>,
size: usize,
) -> Result<()> {
self.data
.get_mut(arrayref)
.and_then(|mut entry| match entry.value_mut() {
Arr(arr) => {
arr.set_array_value_by_raw_offset(offset, value, size)
.ok()?;
Some(())
}
_ => None,
})
.ok_or_else(|| Error::new_execution("error setting array value"))
}
pub(crate) fn get_array_len(&self, arrayref: i32) -> Result<i32> {
self.data
.get(arrayref)
.and_then(|entry| match entry.value() {
Arr(arr) => {
let len = arr.get_length();
Some(len)
}
_ => None,
})
.ok_or_else(|| {
Error::new_execution(&format!("error getting array length by ref={arrayref}"))
})
}
pub fn get_array_len_throwing(
&self,
arrayref: i32,
stack_frames: &mut StackFrames,
) -> ThrowingResult<i32> {
if arrayref == 0 {
throw_and_return!(throw_null_pointer_exception_with_message(
"Cannot read the array length because \"<VAR_NAME>\" is null",
stack_frames
))
}
let result = self.get_array_len(arrayref);
ThrowingResult::Result(result)
}
pub(crate) fn get_const_string_ref(&self, string: &str) -> Option<i32> {
self.ref_by_stringvalue.get(string).map(|v| *v)
}
pub(crate) fn put_const_string_ref(&self, string: &str, reference: i32) -> Option<i32> {
self.ref_by_stringvalue
.insert(string.to_string(), reference)
}
#[allow(dead_code)]
pub(crate) fn dump(&self) -> Result<()> {
let json_string = serde_json::to_string(self)?;
let ts = SystemTime::now().duration_since(UNIX_EPOCH)?.as_millis();
File::create_new(format!("heap_dump_{ts}.json")).and_then(|mut file| {
use std::io::Write;
file.write_all(json_string.as_bytes())
})?;
Ok(())
}
pub(crate) fn clone_instance(&self, objectref: i32) -> Result<i32> {
let cloned_heap_value = {
self.data
.get(objectref)
.and_then(|entry| Some(entry.value().clone()))
.ok_or_else(|| {
Error::new_execution(&format!(
"error getting object by ref {objectref} for cloning"
))
})
}?;
let cloned_ref = match cloned_heap_value {
Object(new_instance) => self.create_instance(new_instance),
Arr(new_array) => {
let new_array_ref =
self.create_array(new_array.type_name(), new_array.get_length());
self.set_entire_array(new_array_ref, new_array)?;
new_array_ref
}
};
Ok(cloned_ref)
}
}