pub mod descriptor;
pub mod traits;
mod utils;
mod builtins;
mod dispatch;
mod dispatch_table;
mod invocation;
pub(crate) use dispatch_table::DISPATCH_TABLE;
use crate::inline_cache::InlineCacheManager;
use crate::class_file::{AttributeInfo, ClassFile, MethodInfo};
use crate::class_loader::ClassLoader;
use crate::debug::{debug_config_from_env, JvmDebugger};
use crate::deterministic::DeterministicConfig;
use crate::error::{ClassLoadingError, JvmError, RuntimeError};
use crate::jit::{JitManager, TieredCompilationConfig};
use crate::memory::{Memory, StackFrame, Value};
use crate::native::{init_builtins, NativeRegistry};
use crate::profiler::Profiler;
use crate::reflection::ReflectionApi;
use crate::security::Sanitizer;
use crate::trace::TraceRecorder;
use std::collections::HashMap;
use std::path::{Path, PathBuf};
use std::sync::Arc;
pub type InterpreterResult = Result<(), JvmError>;
pub struct Interpreter {
pub(crate) class_loader: ClassLoader,
pub(crate) memory: Memory,
pub(crate) string_cache: HashMap<u32, String>,
pub(crate) exception_handlers: Vec<ExceptionHandler>,
pub(crate) current_exception: Option<RuntimeError>,
pub(crate) debugger: JvmDebugger,
pub(crate) current_thread_id: u32,
pub(crate) native_registry: NativeRegistry,
pub(crate) reflection_api: ReflectionApi,
pub(crate) jit_manager: Option<JitManager>,
pub(crate) jit_config: TieredCompilationConfig,
pub(crate) profiler: Option<Arc<Profiler>>,
pub(crate) trace_recorder: Option<TraceRecorder>,
pub(crate) sanitizer: Option<Arc<Sanitizer>>,
pub(crate) deterministic_config: Option<DeterministicConfig>,
pub(crate) inline_cache_manager: InlineCacheManager,
}
#[derive(Debug, Clone)]
pub(crate) struct ExceptionHandler {
pub start_pc: usize,
pub end_pc: usize,
pub handler_pc: usize,
pub catch_type: Option<String>,
}
impl Interpreter {
pub fn new() -> Self {
let debug_config = debug_config_from_env();
let debugger = JvmDebugger::new(debug_config);
let memory = Memory::with_debugger(debugger.clone());
let mut native_registry = NativeRegistry::new();
init_builtins(&mut native_registry);
let reflection_api = ReflectionApi::new();
let jit_manager = JitManager::new().ok();
let jit_config = TieredCompilationConfig::default();
Interpreter {
class_loader: ClassLoader::new_default(),
memory,
string_cache: HashMap::new(),
exception_handlers: Vec::new(),
current_exception: None,
debugger,
current_thread_id: 1,
native_registry,
reflection_api,
jit_manager,
jit_config,
profiler: None,
trace_recorder: None,
sanitizer: None,
deterministic_config: None,
inline_cache_manager: InlineCacheManager::new(true),
}
}
pub fn with_classpath(classpath: Vec<PathBuf>) -> Self {
let debug_config = debug_config_from_env();
let debugger = JvmDebugger::new(debug_config);
let memory = Memory::with_debugger(debugger.clone());
let mut native_registry = NativeRegistry::new();
init_builtins(&mut native_registry);
let reflection_api = ReflectionApi::new();
let jit_manager = JitManager::new().ok();
let jit_config = TieredCompilationConfig::default();
Interpreter {
class_loader: ClassLoader::new(classpath),
memory,
string_cache: HashMap::new(),
exception_handlers: Vec::new(),
current_exception: None,
debugger,
current_thread_id: 1,
native_registry,
reflection_api,
jit_manager,
jit_config,
profiler: None,
trace_recorder: None,
sanitizer: None,
deterministic_config: None,
inline_cache_manager: InlineCacheManager::new(true),
}
}
pub fn is_assignable_from(&mut self, obj_class: &str, target_class: &str) -> bool {
if obj_class == target_class {
return true;
}
if target_class == "java/lang/Object" {
return true;
}
if obj_class.starts_with('[') {
if target_class == "java/lang/Cloneable" || target_class == "java/io/Serializable" {
return true;
}
if target_class.starts_with('[') {
if obj_class.len() > 1 && target_class.len() > 1 {
let obj_comp = &obj_class[1..];
let target_comp = &target_class[1..];
if obj_comp.starts_with('L') && target_comp.starts_with('L') {
let obj_inner = &obj_comp[1..obj_comp.len() - 1]; let target_inner = &target_comp[1..target_comp.len() - 1];
return self.is_assignable_from(obj_inner, target_inner);
}
if obj_comp.starts_with('[') && target_comp.starts_with('[') {
return self.is_assignable_from(obj_comp, target_comp);
}
}
}
return false;
}
if obj_class.len() == 1 {
return false;
}
if !self.class_loader.is_class_loaded(target_class) {
let _ = self.class_loader.load_class(target_class);
}
self.check_hierarchy(obj_class, target_class, 0)
}
fn check_hierarchy(&self, current: &str, target: &str, depth: usize) -> bool {
if depth > 100 {
return false;
} if current == target {
return true;
}
let class = match self.class_loader.get_class(current) {
Some(c) => c,
None => return false,
};
if let Some(super_name) = class.get_super_class_name() {
if self.check_hierarchy(&super_name, target, depth + 1) {
return true;
}
}
for &idx in &class.interfaces {
if let Some(interface_name) = class.get_class_name_from_index(idx) {
if self.check_hierarchy(&interface_name, target, depth + 1) {
return true;
}
}
}
false
}
pub fn allocate_multi_array(
&mut self,
type_desc: &str,
counts: &[i32],
) -> Result<u32, RuntimeError> {
if counts.is_empty() {
return Err(RuntimeError::IllegalArgument("Counts empty".to_string()));
}
let count = counts[0];
if count < 0 {
return Err(RuntimeError::NegativeArraySizeException(count));
}
use crate::memory::HeapArray;
if counts.len() == 1 {
let component = &type_desc[1..];
if component.starts_with('L') || component.starts_with('[') {
let arr = HeapArray::ReferenceArray(type_desc.to_string(), vec![0; count as usize]); return Ok(self.memory.heap.allocate_array(arr));
}
let arr = match component.chars().next().unwrap() {
'Z' => HeapArray::BooleanArray(vec![false; count as usize]),
'C' => HeapArray::CharArray(vec![0u16; count as usize]),
'F' => HeapArray::FloatArray(vec![0.0; count as usize]),
'D' => HeapArray::DoubleArray(vec![0.0; count as usize]),
'B' => HeapArray::ByteArray(vec![0; count as usize]),
'S' => HeapArray::ShortArray(vec![0; count as usize]),
'I' => HeapArray::IntArray(vec![0; count as usize]),
'J' => HeapArray::LongArray(vec![0; count as usize]),
_ => {
return Err(RuntimeError::IllegalArgument(format!(
"Unknown array type: {}",
component
)))
}
};
return Ok(self.memory.heap.allocate_array(arr));
}
let arr_ref = self.memory.heap.allocate_array(HeapArray::ReferenceArray(
type_desc.to_string(),
vec![0; count as usize],
));
let component_type = &type_desc[1..];
for i in 0..count {
let val = self.allocate_multi_array(component_type, &counts[1..])?;
self.memory
.heap
.array_set(arr_ref, i as usize, Value::ArrayRef(val))
.map_err(RuntimeError::from)?;
}
Ok(arr_ref)
}
pub fn get_value_class(&self, val: &Value) -> Result<String, RuntimeError> {
match val {
Value::Null => Ok("null".to_string()),
Value::Reference(addr) => {
let obj = self
.memory
.heap
.get_object(*addr)
.ok_or(RuntimeError::InvalidReference(*addr))?;
Ok(obj.class_name.clone())
}
Value::ArrayRef(addr) => {
let arr = self
.memory
.heap
.get_array(*addr)
.ok_or(RuntimeError::InvalidReference(*addr))?;
Ok(self.get_array_class(arr))
}
_ => Err(RuntimeError::ClassCastException(
"Not an object".to_string(),
"Object".to_string(),
)),
}
}
fn get_array_class(&self, arr: &crate::memory::HeapArray) -> String {
use crate::memory::HeapArray;
match arr {
HeapArray::BooleanArray(_) => "[Z".to_string(),
HeapArray::CharArray(_) => "[C".to_string(),
HeapArray::FloatArray(_) => "[F".to_string(),
HeapArray::DoubleArray(_) => "[D".to_string(),
HeapArray::ByteArray(_) => "[B".to_string(),
HeapArray::ShortArray(_) => "[S".to_string(),
HeapArray::IntArray(_) => "[I".to_string(),
HeapArray::LongArray(_) => "[J".to_string(),
HeapArray::ReferenceArray(class_name, _) => class_name.clone(),
}
}
pub fn value_to_string(&self, val: &Value) -> String {
match val {
Value::Null => "null".to_string(),
Value::Boolean(b) => b.to_string(),
Value::Byte(b) => b.to_string(),
Value::Char(c) => char::from_u32(*c as u32)
.map(|c| c.to_string())
.unwrap_or_else(|| format!("\\u{:04x}", c)),
Value::Short(s) => s.to_string(),
Value::Int(i) => i.to_string(),
Value::Long(l) => l.to_string(),
Value::Float(f) => f.to_string(),
Value::Double(d) => d.to_string(),
Value::Reference(addr) => {
if let Some(obj) = self.memory.heap.get_object(*addr) {
if obj.class_name == "java/lang/String" {
if let Some(s) = &obj.string_data {
return s.clone();
}
if let Some(Value::Reference(s_ref)) = obj.fields.get("value") {
if let Some(arr) = self.memory.heap.get_array(*s_ref) {
if let crate::memory::HeapArray::ByteArray(bytes) = arr {
return String::from_utf8_lossy(bytes).to_string();
}
}
}
}
format!("<{}@{:x}>", obj.class_name, addr)
} else {
format!("<invalid reference @{}>", addr)
}
}
Value::ArrayRef(addr) => {
if let Some(arr) = self.memory.heap.get_array(*addr) {
match arr {
crate::memory::HeapArray::BooleanArray(data) => format!("{:?}", data),
crate::memory::HeapArray::CharArray(data) => {
let s: String = data.iter().map(|&c| (c as u8) as char).collect();
format!("{:?}", s)
}
crate::memory::HeapArray::FloatArray(data) => format!("{:?}", data),
crate::memory::HeapArray::DoubleArray(data) => format!("{:?}", data),
crate::memory::HeapArray::ByteArray(data) => format!("{:?}", data),
crate::memory::HeapArray::ShortArray(data) => format!("{:?}", data),
crate::memory::HeapArray::IntArray(data) => format!("{:?}", data),
crate::memory::HeapArray::LongArray(data) => format!("{:?}", data),
crate::memory::HeapArray::ReferenceArray(_, data) => {
format!("[... {} elements]", data.len())
}
}
} else {
format!("<invalid array ref @{}>", addr)
}
}
Value::ReturnAddress(addr) => format!("<return address @{}>", addr),
}
}
pub fn with_jit(jit_config: TieredCompilationConfig) -> Self {
let debug_config = debug_config_from_env();
let debugger = JvmDebugger::new(debug_config);
let memory = Memory::with_debugger(debugger.clone());
let mut native_registry = NativeRegistry::new();
init_builtins(&mut native_registry);
let reflection_api = ReflectionApi::new();
let jit_manager = JitManager::with_config(jit_config.clone()).ok();
Interpreter {
class_loader: ClassLoader::new_default(),
memory,
string_cache: HashMap::new(),
exception_handlers: Vec::new(),
current_exception: None,
debugger,
current_thread_id: 1,
native_registry,
reflection_api,
jit_manager,
jit_config,
profiler: None,
trace_recorder: None,
inline_cache_manager: InlineCacheManager::new(true),
sanitizer: None,
deterministic_config: None,
}
}
pub fn set_deterministic(&mut self, config: Option<DeterministicConfig>) {
self.deterministic_config = config;
}
pub fn set_profiler(&mut self, profiler: Option<Arc<Profiler>>) {
self.profiler = profiler;
}
pub fn profiler(&self) -> Option<&Arc<Profiler>> {
self.profiler.as_ref()
}
pub fn set_trace_recorder(&mut self, mut recorder: Option<TraceRecorder>) {
if let Some(ref mut r) = recorder {
r.set_enabled(true);
}
self.trace_recorder = recorder;
}
pub fn set_sanitizer(&mut self, sanitizer: Option<Arc<Sanitizer>>) {
self.memory.set_sanitizer(sanitizer.clone());
self.sanitizer = sanitizer;
}
pub fn set_class_cache_dir(&mut self, path: Option<PathBuf>) {
self.class_loader.set_cache_dir(path);
}
pub fn memory(&self) -> &Memory {
&self.memory
}
pub fn memory_mut(&mut self) -> &mut Memory {
&mut self.memory
}
pub fn new_instance(&mut self, class_name: &str, args: &[Value]) -> Result<Value, JvmError> {
self.class_loader.load_class(class_name)?;
let class = self
.class_loader
.get_class(class_name)
.ok_or_else(|| {
JvmError::ClassLoadingError(ClassLoadingError::ClassFileNotFound(
class_name.to_string(),
))
})?
.clone();
let internal_name = class
.get_class_name()
.unwrap_or_else(|| class_name.to_string());
let addr = self.memory.heap.allocate(internal_name);
if args.is_empty() {
if let Some(init) = class.find_method("<init>", "()V") {
let _ = self.invoke_constructor_for_reflection(&class, init, addr);
}
}
Ok(Value::Reference(addr))
}
pub fn get_field_value(&self, obj: &Value, field_name: &str) -> Result<Value, JvmError> {
let addr = obj.as_reference().ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(
"Not an object reference".to_string(),
))
})?;
self.memory.heap.get_field(addr, field_name).ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(format!(
"Field '{}' not found or not initialized",
field_name
)))
})
}
pub fn set_field_value(
&mut self,
obj: &Value,
field_name: &str,
value: Value,
) -> Result<(), JvmError> {
let addr = obj.as_reference().ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(
"Not an object reference".to_string(),
))
})?;
self.memory
.heap
.set_field(addr, field_name.to_string(), value)
.map_err(|e| JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string())))
}
pub fn get_object_class(&self, obj: &Value) -> Result<String, JvmError> {
let addr = obj.as_reference().ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(
"Not an object reference".to_string(),
))
})?;
self.memory
.heap
.get_object(addr)
.map(|o| o.class_name.clone())
.ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(format!(
"Invalid object reference {}",
addr
)))
})
}
pub fn invoke_method(
&mut self,
obj: &Value,
method_name: &str,
args: &[Value],
) -> Result<Value, JvmError> {
let addr = obj.as_reference().ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(
"Not an object reference".to_string(),
))
})?;
let class_name = self.get_object_class(obj)?;
let class = self
.class_loader
.get_class(&class_name)
.ok_or_else(|| {
JvmError::ClassLoadingError(ClassLoadingError::ClassFileNotFound(
class_name.clone(),
))
})?
.clone();
let descriptor = Self::build_descriptor_for_args(args);
let method = class
.find_method(method_name, &descriptor)
.or_else(|| class.find_method(method_name, "()I"))
.or_else(|| class.find_method(method_name, "()V"))
.ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::MethodNotFound(
class_name.clone(),
method_name.to_string(),
))
})?;
let desc = class
.get_string(method.descriptor_index)
.unwrap_or_default();
let mut caller_frame = StackFrame::new(0, 64, "reflection_caller".to_string());
for v in args.iter().rev() {
caller_frame.push(v.clone()).map_err(|e| {
JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string()))
})?;
}
caller_frame
.push(Value::Reference(addr))
.map_err(|e| JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string())))?;
self.execute_method(&class, method, &mut caller_frame)?;
if desc.ends_with(")V") {
Ok(Value::Int(0))
} else {
caller_frame
.pop()
.map_err(|e| JvmError::RuntimeError(RuntimeError::IllegalArgument(e.to_string())))
}
}
fn build_descriptor_for_args(args: &[Value]) -> String {
let mut s = String::from("(");
for v in args {
match v {
Value::Int(_) => s.push('I'),
Value::Long(_) => s.push('J'),
Value::Float(_) => s.push('F'),
Value::Double(_) => s.push('D'),
Value::Reference(_) | Value::ArrayRef(_) => s.push_str("Ljava/lang/Object;"),
_ => s.push('I'),
}
}
s.push(')');
s.push('I');
s
}
fn invoke_constructor_for_reflection(
&mut self,
class: &ClassFile,
method: &MethodInfo,
addr: u32,
) -> InterpreterResult {
let mut caller_frame = StackFrame::new(0, 64, "reflection_caller".to_string());
caller_frame.push(Value::Reference(addr))?;
self.execute_method(class, method, &mut caller_frame)
}
pub fn trace_recorder(&self) -> Option<&TraceRecorder> {
self.trace_recorder.as_ref()
}
pub fn get_loaded_class_count(&self) -> usize {
self.class_loader.get_loaded_classes().len()
}
pub fn get_memory_usage(&self) -> usize {
self.memory.heap.memory_used()
}
pub fn get_gc_count(&self) -> usize {
self.memory.heap.object_count()
}
pub fn is_jit_enabled(&self) -> bool {
self.jit_manager.is_some() && self.jit_config.enabled
}
pub fn set_jit_enabled(&mut self, enabled: bool) {
if enabled && self.jit_manager.is_none() {
self.jit_manager = JitManager::new().ok();
} else if !enabled {
self.jit_manager = None;
}
self.jit_config.enabled = enabled;
}
pub fn jit_manager(&mut self) -> Option<&mut JitManager> {
self.jit_manager.as_mut()
}
pub fn is_class_loaded(&self, name: &str) -> bool {
self.class_loader.is_class_loaded(name)
}
pub fn load_class<P: AsRef<Path>>(&mut self, path: P) -> Result<(), JvmError> {
let _ = ClassFile::from_file(path).map_err(|e| {
JvmError::ClassLoadingError(ClassLoadingError::ClassFileNotFound(format!(
"Failed to load class: {:?}",
e
)))
})?;
Ok(())
}
pub fn load_class_by_name(&mut self, class_name: &str) -> Result<(), JvmError> {
self.class_loader.load_class(class_name)?;
Ok(())
}
pub fn get_class(&self, name: &str) -> Option<&ClassFile> {
self.class_loader.get_class(name)
}
pub fn get_reflection_api(&self) -> &ReflectionApi {
&self.reflection_api
}
pub fn get_class_reflection(
&self,
class_name: &str,
) -> Option<crate::reflection::ClassReflection> {
let class = self.class_loader.get_class(class_name)?;
Some(crate::reflection::class_to_reflection(class))
}
pub fn run_main(&mut self, class_name: &str) -> Result<(), JvmError> {
self.load_class_by_name(class_name)?;
let class = self.class_loader.get_class(class_name).ok_or_else(|| {
JvmError::ClassLoadingError(ClassLoadingError::NoClassDefFound(class_name.to_string()))
})?;
let main_method = class
.find_method("main", "([Ljava/lang/String;)V")
.ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::MethodNotFound(
class_name.to_string(),
"main([Ljava/lang/String;)V".to_string(),
))
})?
.clone();
let code_attr = self
.find_code_attribute(class, &main_method)
.ok_or_else(|| {
JvmError::RuntimeError(RuntimeError::UnsupportedOperation(
"Code attribute not found".to_string(),
))
})?
.clone();
let max_stack = utils::read_u16(&code_attr.info, 0) as usize;
let max_locals = utils::read_u16(&code_attr.info, 2) as usize;
let code_length = utils::read_u32(&code_attr.info, 4) as usize;
let mut frame = StackFrame::new(max_locals, max_stack, "main".to_string());
let code = code_attr.info[8..8 + code_length].to_vec();
let class_clone = class.clone();
while frame.pc < code.len() {
let opcode = code[frame.pc];
frame.pc += 1;
self.debugger.log_instruction(&frame, &class_clone, opcode);
if !DISPATCH_TABLE[opcode as usize](self, &class_clone, &code, &mut frame, opcode)? {
break;
}
}
Ok(())
}
pub(crate) fn find_code_attribute<'a>(
&self,
class: &ClassFile,
method: &'a MethodInfo,
) -> Option<&'a AttributeInfo> {
method
.attributes
.iter()
.find(|attr| {
attr.info.len() >= 8
&& class.get_string(attr.attribute_name_index).as_deref() == Some("Code")
})
.or_else(|| method.attributes.iter().find(|attr| attr.info.len() >= 8))
}
pub fn array_set_value(&mut self, array_ref: u32, index: i32, value: Value) -> Result<(), RuntimeError> {
self.memory.heap.array_set(array_ref, index as usize, value)?;
Ok(())
}
pub fn array_set_int(&mut self, array_ref: u32, index: i32, value: i32) -> Result<(), RuntimeError> {
self.memory.heap.array_set(array_ref, index as usize, Value::Int(value))?;
Ok(())
}
pub fn array_get_value(&mut self, array_ref: u32, index: i32) -> Result<Value, RuntimeError> {
Ok(self.memory.heap.array_get(array_ref, index as usize)?)
}
pub fn array_get_int(&mut self, array_ref: u32, index: i32) -> Result<i32, RuntimeError> {
match self.memory.heap.array_get(array_ref, index as usize)? {
Value::Int(val) => Ok(val),
_ => Err(RuntimeError::InvalidTypeConversion("array element".to_string(), "int".to_string())),
}
}
pub fn array_get_length(&mut self, array_ref: u32) -> Result<i32, RuntimeError> {
Ok(self.memory.heap.array_length(array_ref)? as i32)
}
pub fn new_array_object(&mut self, component_type: &str, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::ReferenceArray(component_type.to_string(), vec![0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_bool(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::BooleanArray(vec![false; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_byte(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::ByteArray(vec![0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_char(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::CharArray(vec![0u16; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_short(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::ShortArray(vec![0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_long(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::LongArray(vec![0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_float(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::FloatArray(vec![0.0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_double(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::DoubleArray(vec![0.0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
pub fn new_array_int(&mut self, length: i32) -> Result<u32, RuntimeError> {
if length < 0 {
return Err(RuntimeError::NegativeArraySizeException(length));
}
let arr = crate::memory::HeapArray::IntArray(vec![0; length as usize]);
Ok(self.memory.heap.allocate_array(arr))
}
}
impl Default for Interpreter {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod test_invokedynamic;
#[cfg(test)]
mod tests;