/*! * VM Backend - Execute MIR instructions in a virtual machine * * Simple stack-based VM for executing MIR code */ use crate::mir::{MirModule, MirFunction, MirInstruction, ConstValue, BinaryOp, CompareOp, UnaryOp, ValueId, BasicBlockId}; use crate::box_trait::{NyashBox, StringBox, IntegerBox, BoolBox, VoidBox}; use std::collections::HashMap; use std::sync::Arc; use crate::runtime::NyashRuntime; use crate::scope_tracker::ScopeTracker; // MirModule is already imported via crate::mir at top use crate::instance_v2::InstanceBox; use super::vm_phi::LoopExecutor; use std::time::Instant; // Phase 9.78a: Import necessary components for unified Box handling // TODO: Re-enable when interpreter refactoring is complete // use crate::box_factory::UnifiedBoxRegistry; // use crate::instance_v2::InstanceBox; // use crate::interpreter::BoxDeclaration; // use crate::scope_tracker::ScopeTracker; // #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] // use crate::runtime::plugin_loader_v2::PluginLoaderV2; /// VM execution error #[derive(Debug)] pub enum VMError { InvalidValue(String), InvalidInstruction(String), InvalidBasicBlock(String), DivisionByZero, StackUnderflow, TypeError(String), } impl std::fmt::Display for VMError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { VMError::InvalidValue(msg) => write!(f, "Invalid value: {}", msg), VMError::InvalidInstruction(msg) => write!(f, "Invalid instruction: {}", msg), VMError::InvalidBasicBlock(msg) => write!(f, "Invalid basic block: {}", msg), VMError::DivisionByZero => write!(f, "Division by zero"), VMError::StackUnderflow => write!(f, "Stack underflow"), VMError::TypeError(msg) => write!(f, "Type error: {}", msg), } } } impl std::error::Error for VMError {} /// VM value representation #[derive(Debug, Clone)] pub enum VMValue { Integer(i64), Float(f64), Bool(bool), String(String), Future(crate::boxes::future::FutureBox), Void, // Phase 9.78a: Add BoxRef for complex Box types BoxRef(Arc), } // Manual PartialEq implementation to avoid requiring PartialEq on FutureBox impl PartialEq for VMValue { fn eq(&self, other: &Self) -> bool { match (self, other) { (VMValue::Integer(a), VMValue::Integer(b)) => a == b, (VMValue::Float(a), VMValue::Float(b)) => a == b, (VMValue::Bool(a), VMValue::Bool(b)) => a == b, (VMValue::String(a), VMValue::String(b)) => a == b, (VMValue::Void, VMValue::Void) => true, // Future equality semantics are not defined; treat distinct futures as not equal (VMValue::Future(_), VMValue::Future(_)) => false, // BoxRef equality by reference (VMValue::BoxRef(_), VMValue::BoxRef(_)) => false, _ => false, } } } impl VMValue { /// Convert to NyashBox for output pub fn to_nyash_box(&self) -> Box { match self { VMValue::Integer(i) => Box::new(IntegerBox::new(*i)), VMValue::Float(f) => Box::new(StringBox::new(&f.to_string())), // Simplified for now VMValue::Bool(b) => Box::new(BoolBox::new(*b)), VMValue::String(s) => Box::new(StringBox::new(s)), VMValue::Future(f) => Box::new(f.clone()), VMValue::Void => Box::new(VoidBox::new()), // BoxRef returns a shared handle (do NOT birth a new instance) VMValue::BoxRef(arc_box) => arc_box.share_box(), } } /// Get string representation for printing pub fn to_string(&self) -> String { match self { VMValue::Integer(i) => i.to_string(), VMValue::Float(f) => f.to_string(), VMValue::Bool(b) => b.to_string(), VMValue::String(s) => s.clone(), VMValue::Future(f) => f.to_string_box().value, VMValue::Void => "void".to_string(), VMValue::BoxRef(arc_box) => arc_box.to_string_box().value, } } /// Attempt to convert to integer pub fn as_integer(&self) -> Result { match self { VMValue::Integer(i) => Ok(*i), _ => Err(VMError::TypeError(format!("Expected integer, got {:?}", self))), } } /// Attempt to convert to bool pub fn as_bool(&self) -> Result { match self { VMValue::Bool(b) => Ok(*b), VMValue::Integer(i) => Ok(*i != 0), // Pragmatic coercions for dynamic boxes VMValue::BoxRef(b) => { // BoolBox → bool if let Some(bb) = b.as_any().downcast_ref::() { return Ok(bb.value); } // VoidBox → false (nullish false) if b.as_any().downcast_ref::().is_some() { return Ok(false); } Err(VMError::TypeError(format!("Expected bool, got BoxRef({})", b.type_name()))) } _ => Err(VMError::TypeError(format!("Expected bool, got {:?}", self))), } } /// Convert from NyashBox to VMValue pub fn from_nyash_box(nyash_box: Box) -> VMValue { // Try to downcast to known types for optimization if let Some(int_box) = nyash_box.as_any().downcast_ref::() { VMValue::Integer(int_box.value) } else if let Some(bool_box) = nyash_box.as_any().downcast_ref::() { VMValue::Bool(bool_box.value) } else if let Some(string_box) = nyash_box.as_any().downcast_ref::() { VMValue::String(string_box.value.clone()) } else if let Some(future_box) = nyash_box.as_any().downcast_ref::() { VMValue::Future(future_box.clone()) } else { // Phase 9.78a: For all other Box types (user-defined, plugin), store as BoxRef VMValue::BoxRef(Arc::from(nyash_box)) } } } impl From<&ConstValue> for VMValue { fn from(const_val: &ConstValue) -> Self { match const_val { ConstValue::Integer(i) => VMValue::Integer(*i), ConstValue::Float(f) => VMValue::Float(*f), ConstValue::Bool(b) => VMValue::Bool(*b), ConstValue::String(s) => VMValue::String(s.clone()), ConstValue::Null => VMValue::Void, // Simplified ConstValue::Void => VMValue::Void, } } } /// Virtual Machine state pub struct VM { /// Value storage (uses ValueId as direct index into Vec for O(1) access) values: Vec>, /// Current function being executed current_function: Option, /// Current basic block current_block: Option, /// Previous basic block (for phi node resolution) previous_block: Option, /// Program counter within current block pc: usize, /// Return value from last execution #[allow(dead_code)] last_result: Option, /// Simple field storage for objects (maps reference -> field -> value) object_fields: HashMap>, /// Class name mapping for objects (for visibility checks) object_class: HashMap, /// Marks ValueIds that represent internal (me/this) references within the current function object_internal: std::collections::HashSet, /// Loop executor for handling phi nodes and loop-specific logic loop_executor: LoopExecutor, /// Shared runtime for box creation and declarations runtime: NyashRuntime, /// Scope tracker for calling fini on scope exit scope_tracker: ScopeTracker, /// Active MIR module during execution (for function calls) module: Option, /// Instruction execution counters (by MIR opcode) instr_counter: std::collections::HashMap<&'static str, usize>, /// Execution start time for optional stats exec_start: Option, // Phase 9.78a: Add unified Box handling components // TODO: Re-enable when interpreter refactoring is complete // /// Box registry for creating all Box types // box_registry: Arc, // /// Plugin loader for external Box types // #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] // plugin_loader: Option>, // Scope tracker for lifecycle management // scope_tracker: ScopeTracker, // /// Box declarations from the AST // box_declarations: Arc>>, } impl VM { /// Create a new VM instance pub fn new() -> Self { Self { values: Vec::new(), current_function: None, current_block: None, previous_block: None, pc: 0, last_result: None, object_fields: HashMap::new(), object_class: HashMap::new(), object_internal: std::collections::HashSet::new(), loop_executor: LoopExecutor::new(), runtime: NyashRuntime::new(), scope_tracker: ScopeTracker::new(), module: None, instr_counter: std::collections::HashMap::new(), exec_start: None, // TODO: Re-enable when interpreter refactoring is complete // box_registry: Arc::new(UnifiedBoxRegistry::new()), // #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] // plugin_loader: None, // scope_tracker: ScopeTracker::new(), // box_declarations: Arc::new(RwLock::new(HashMap::new())), } } /// Create a VM with an external runtime (dependency injection) pub fn with_runtime(runtime: NyashRuntime) -> Self { Self { values: Vec::new(), current_function: None, current_block: None, previous_block: None, pc: 0, last_result: None, object_fields: HashMap::new(), object_class: HashMap::new(), object_internal: std::collections::HashSet::new(), loop_executor: LoopExecutor::new(), runtime, scope_tracker: ScopeTracker::new(), module: None, instr_counter: std::collections::HashMap::new(), exec_start: None, } } // TODO: Re-enable when interpreter refactoring is complete /* /// Create a new VM instance with Box registry and declarations pub fn new_with_registry( box_registry: Arc, box_declarations: Arc>> ) -> Self { // Implementation pending interpreter refactoring unimplemented!() } /// Phase 9.78a: Create VM with plugin support #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] pub fn new_with_plugins( box_registry: Arc, plugin_loader: Arc, box_declarations: Arc>>, ) -> Self { // Implementation pending interpreter refactoring unimplemented!() } */ /// Execute a MIR module pub fn execute_module(&mut self, module: &MirModule) -> Result, VMError> { // Store module for nested calls self.module = Some(module.clone()); // Reset stats self.instr_counter.clear(); self.exec_start = Some(Instant::now()); // Find main function let main_function = module.get_function("main") .ok_or_else(|| VMError::InvalidInstruction("No main function found".to_string()))?; // Execute main function let result = self.execute_function(main_function)?; // Optional: print VM stats self.maybe_print_stats(); // Convert result to NyashBox Ok(result.to_nyash_box()) } /// Call a MIR function by name with VMValue arguments fn call_function_by_name(&mut self, func_name: &str, args: Vec) -> Result { let module_ref = self.module.as_ref().ok_or_else(|| VMError::InvalidInstruction("No active module".to_string()))?; let function_ref = module_ref.get_function(func_name) .ok_or_else(|| VMError::InvalidInstruction(format!("Function '{}' not found", func_name)))?; // Clone function to avoid borrowing conflicts during execution let function = function_ref.clone(); // Save current frame let saved_values = std::mem::take(&mut self.values); let saved_current_function = self.current_function.clone(); let saved_current_block = self.current_block; let saved_previous_block = self.previous_block; let saved_pc = self.pc; let saved_last_result = self.last_result.clone(); // Bind parameters for (i, param_id) in function.params.iter().enumerate() { if let Some(arg) = args.get(i) { self.set_value(*param_id, arg.clone()); } } // Heuristic: map `me` (first param) to class name parsed from function name (e.g., User.method/N) if let Some(first) = function.params.get(0) { if let Some((class_part, _rest)) = func_name.split_once('.') { // Record class for internal field visibility checks self.object_class.insert(*first, class_part.to_string()); // Mark internal reference self.object_internal.insert(*first); } } // Execute the function let result = self.execute_function(&function); // Restore frame self.values = saved_values; self.current_function = saved_current_function; self.current_block = saved_current_block; self.previous_block = saved_previous_block; self.pc = saved_pc; self.last_result = saved_last_result; result } /// Execute a single function fn execute_function(&mut self, function: &MirFunction) -> Result { self.current_function = Some(function.signature.name.clone()); // Initialize loop executor for this function self.loop_executor.initialize(); // Enter a new scope for this function self.scope_tracker.push_scope(); // Start at entry block let mut current_block = function.entry_block; loop { let block = function.get_block(current_block) .ok_or_else(|| VMError::InvalidBasicBlock(format!("Block {} not found", current_block)))?; self.current_block = Some(current_block); self.pc = 0; let mut next_block = None; let mut should_return = None; // Execute instructions in this block (including terminator) let all_instructions: Vec<_> = block.all_instructions().collect(); for (index, instruction) in all_instructions.iter().enumerate() { self.pc = index; match self.execute_instruction(instruction)? { ControlFlow::Continue => continue, ControlFlow::Jump(target) => { next_block = Some(target); break; }, ControlFlow::Return(value) => { should_return = Some(value); break; }, } } // Handle control flow if let Some(return_value) = should_return { // Exit scope before returning self.scope_tracker.pop_scope(); return Ok(return_value); } else if let Some(target) = next_block { // Update previous block before jumping self.previous_block = Some(current_block); // Record the transition in loop executor self.loop_executor.record_transition(current_block, target); current_block = target; } else { // Block ended without terminator - this shouldn't happen in well-formed MIR // but let's handle it gracefully by returning void // Exit scope before returning self.scope_tracker.pop_scope(); return Ok(VMValue::Void); } } } /// Execute a single instruction fn execute_instruction(&mut self, instruction: &MirInstruction) -> Result { // Record instruction for stats self.record_instruction(instruction); match instruction { MirInstruction::Const { dst, value } => { let vm_value = VMValue::from(value); self.set_value(*dst, vm_value); Ok(ControlFlow::Continue) }, MirInstruction::BinOp { dst, op, lhs, rhs } => { let left = self.get_value(*lhs)?; let right = self.get_value(*rhs)?; let result = self.execute_binary_op(op, &left, &right)?; self.set_value(*dst, result); Ok(ControlFlow::Continue) }, MirInstruction::UnaryOp { dst, op, operand } => { let operand_val = self.get_value(*operand)?; let result = self.execute_unary_op(op, &operand_val)?; self.set_value(*dst, result); Ok(ControlFlow::Continue) }, MirInstruction::Compare { dst, op, lhs, rhs } => { let left = self.get_value(*lhs)?; let right = self.get_value(*rhs)?; let result = self.execute_compare_op(op, &left, &right)?; self.set_value(*dst, VMValue::Bool(result)); Ok(ControlFlow::Continue) }, MirInstruction::Print { value, .. } => { let val = self.get_value(*value)?; println!("{}", val.to_string()); Ok(ControlFlow::Continue) }, MirInstruction::TypeOp { dst, op, value, ty } => { match op { crate::mir::TypeOpKind::Check => { let v = self.get_value(*value)?; let ok = match ty { crate::mir::MirType::Integer => matches!(v, VMValue::Integer(_)), crate::mir::MirType::Float => matches!(v, VMValue::Float(_)), crate::mir::MirType::Bool => matches!(v, VMValue::Bool(_)), crate::mir::MirType::String => matches!(v, VMValue::String(_)), crate::mir::MirType::Void => matches!(v, VMValue::Void), crate::mir::MirType::Box(name) => match v { VMValue::BoxRef(ref arc) => arc.type_name() == name, _ => false, }, _ => true, }; self.set_value(*dst, VMValue::Bool(ok)); } crate::mir::TypeOpKind::Cast => { let v = self.get_value(*value)?; self.set_value(*dst, v); } } Ok(ControlFlow::Continue) }, MirInstruction::Return { value } => { let return_value = if let Some(val_id) = value { let val = self.get_value(*val_id)?; val } else { VMValue::Void }; Ok(ControlFlow::Return(return_value)) }, MirInstruction::Jump { target } => { Ok(ControlFlow::Jump(*target)) }, MirInstruction::Branch { condition, then_bb, else_bb } => { let cond_val = self.get_value(*condition)?; let cond_bool = cond_val.as_bool()?; if cond_bool { Ok(ControlFlow::Jump(*then_bb)) } else { Ok(ControlFlow::Jump(*else_bb)) } }, MirInstruction::Phi { dst, inputs } => { // Create a closure that captures self immutably let values = &self.values; let get_value_fn = |value_id: ValueId| -> Result { let index = value_id.to_usize(); if index < values.len() { if let Some(ref value) = values[index] { Ok(value.clone()) } else { Err(VMError::InvalidValue(format!("Value {} not set", value_id))) } } else { Err(VMError::InvalidValue(format!("Value {} out of bounds", value_id))) } }; // Delegate phi node execution to loop executor let selected_value = self.loop_executor.execute_phi( *dst, inputs, get_value_fn )?; self.set_value(*dst, selected_value); Ok(ControlFlow::Continue) }, // Missing instructions that need basic implementations MirInstruction::Load { dst, ptr } => { // For now, loading is the same as getting the value let value = self.get_value(*ptr)?; self.set_value(*dst, value); Ok(ControlFlow::Continue) }, MirInstruction::Store { value, ptr } => { // For now, storing just updates the ptr with the value let val = self.get_value(*value)?; self.set_value(*ptr, val); Ok(ControlFlow::Continue) }, MirInstruction::Call { dst, func, args, effects: _ } => { // Resolve function name from func value (expects Const String) let func_val = self.get_value(*func)?; let func_name = match func_val { VMValue::String(s) => s, _ => return Err(VMError::InvalidInstruction("Call expects func to be a String name".to_string())), }; // Gather argument VM values let mut vm_args = Vec::new(); for arg_id in args { vm_args.push(self.get_value(*arg_id)?); } let result = self.call_function_by_name(&func_name, vm_args)?; if let Some(dst_id) = dst { self.set_value(*dst_id, result); } Ok(ControlFlow::Continue) }, MirInstruction::BoxCall { dst, box_val, method, args, effects: _ } => { // Phase 9.78a: Unified method dispatch for all Box types // Get the box value let box_vm_value = self.get_value(*box_val)?; // Handle BoxRef for proper method dispatch let box_nyash = match &box_vm_value { // Use shared handle to avoid unintended constructor calls VMValue::BoxRef(arc_box) => arc_box.share_box(), _ => box_vm_value.to_nyash_box(), }; // Fast path: birth() for user-defined boxes is lowered to a MIR function if method == "birth" { if let Some(instance) = box_nyash.as_any().downcast_ref::() { let class_name = instance.class_name.clone(); let func_name = format!("{}.birth/{}", class_name, args.len()); // Prepare VMValue args: me + evaluated arguments let mut vm_args: Vec = Vec::new(); vm_args.push(VMValue::from_nyash_box(box_nyash.clone_or_share())); for arg_id in args { let arg_vm_value = self.get_value(*arg_id)?; vm_args.push(arg_vm_value); } // Call the lowered function (ignore return) let _ = self.call_function_by_name(&func_name, vm_args)?; // birth returns void; only set dst if specified (rare for birth) if let Some(dst_id) = dst { self.set_value(*dst_id, VMValue::Void); } return Ok(ControlFlow::Continue); } } // Evaluate arguments let mut arg_values: Vec> = Vec::new(); let mut arg_vm_values: Vec = Vec::new(); for arg_id in args { let arg_vm_value = self.get_value(*arg_id)?; arg_values.push(arg_vm_value.to_nyash_box()); arg_vm_values.push(arg_vm_value); } self.debug_log_boxcall(&box_vm_value, method, &arg_values, "enter", None); // PluginBoxV2 method dispatch via BID-FFI (zero-arg minimal) #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))] if let Some(plugin) = box_nyash.as_any().downcast_ref::() { let loader = crate::runtime::get_global_loader_v2(); let loader = loader.read().map_err(|_| VMError::InvalidInstruction("Plugin loader lock poisoned".into()))?; match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) { Ok(Some(result_box)) => { if let Some(dst_id) = dst { self.set_value(*dst_id, VMValue::from_nyash_box(result_box)); } } Ok(None) => { if let Some(dst_id) = dst { self.set_value(*dst_id, VMValue::Void); } } Err(_) => { return Err(VMError::InvalidInstruction(format!("Plugin method call failed: {}", method))); } } return Ok(ControlFlow::Continue); } // Fast-path for ArrayBox methods using original BoxRef (preserve state) if let VMValue::BoxRef(ref arc_any) = box_vm_value { if let Some(arr) = arc_any.as_any().downcast_ref::() { match method.as_str() { "get" => { if let Some(arg0) = arg_values.get(0) { let res = arr.get((*arg0).clone_or_share()); if let Some(dst_id) = dst { let v = VMValue::from_nyash_box(res); self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); } return Ok(ControlFlow::Continue); } } "set" => { if arg_values.len() >= 2 { let idx = (*arg_values.get(0).unwrap()).clone_or_share(); let val = (*arg_values.get(1).unwrap()).clone_or_share(); let _ = arr.set(idx, val); if let Some(dst_id) = dst { let v = VMValue::Void; self.debug_log_boxcall(&box_vm_value, method, &arg_values, "fastpath", Some(&v)); self.set_value(*dst_id, v); } return Ok(ControlFlow::Continue); } } _ => {} } } } // Call the method - unified dispatch for all Box types // If user-defined InstanceBox: dispatch to lowered MIR function `{Class}.{method}/{argc}` if let Some(instance) = box_nyash.as_any().downcast_ref::() { let class_name = instance.class_name.clone(); let func_name = format!("{}.{}{}", class_name, method, format!("/{}", args.len())); // Prepare VMValue args: me + evaluated arguments (use original VM args for value-level fidelity) let mut vm_args: Vec = Vec::new(); vm_args.push(VMValue::from_nyash_box(box_nyash.clone_or_share())); for arg_id in args { let arg_vm_value = self.get_value(*arg_id)?; vm_args.push(arg_vm_value); } let call_result = self.call_function_by_name(&func_name, vm_args)?; if let Some(dst_id) = dst { self.set_value(*dst_id, call_result); } return Ok(ControlFlow::Continue); } let result = self.call_unified_method(box_nyash, method, arg_values)?; // Store result if destination is specified if let Some(dst_id) = dst { let vm_result = VMValue::from_nyash_box(result); self.debug_log_boxcall(&box_vm_value, method, &arg_vm_values.iter().map(|v| v.to_nyash_box()).collect::>(), "unified", Some(&vm_result)); self.set_value(*dst_id, vm_result); } Ok(ControlFlow::Continue) }, MirInstruction::NewBox { dst, box_type, args } => { // Evaluate arguments into NyashBox for unified factory let mut nyash_args: Vec> = Vec::new(); for arg_id in args { let arg_value = self.get_value(*arg_id)?; nyash_args.push(arg_value.to_nyash_box()); } // Create via unified registry from runtime let registry = self.runtime.box_registry.clone(); let created = { let guard = registry.lock().map_err(|_| VMError::InvalidInstruction("Registry lock poisoned".into()))?; guard.create_box(box_type, &nyash_args) }; match created { Ok(b) => { // Register for scope-based finalization (share; keep same instance) let reg_arc = std::sync::Arc::from(b.share_box()); self.scope_tracker.register_box(reg_arc); // Record class name for visibility checks self.object_class.insert(*dst, box_type.clone()); // Store value in VM self.set_value(*dst, VMValue::from_nyash_box(b)); Ok(ControlFlow::Continue) } Err(e) => Err(VMError::InvalidInstruction(format!("NewBox failed for {}: {}", box_type, e))) } }, MirInstruction::TypeCheck { dst, value: _, expected_type: _ } => { // For now, type checks always return true // TODO: Implement proper type checking self.set_value(*dst, VMValue::Bool(true)); Ok(ControlFlow::Continue) }, MirInstruction::Cast { dst, value, target_type: _ } => { // For now, casting just copies the value // TODO: Implement proper type casting let val = self.get_value(*value)?; self.set_value(*dst, val); Ok(ControlFlow::Continue) }, MirInstruction::ArrayGet { dst, array, index } => { // Implement ArrayBox get(index) → value let arr_val = self.get_value(*array)?; let idx_val = self.get_value(*index)?; if let VMValue::BoxRef(arc) = arr_val { if let Some(arr) = arc.as_any().downcast_ref::() { let idx_box = idx_val.to_nyash_box(); let got = arr.get(idx_box); self.set_value(*dst, VMValue::from_nyash_box(got)); return Ok(ControlFlow::Continue); } } Err(VMError::TypeError("ArrayGet expects ArrayBox".to_string())) }, MirInstruction::ArraySet { array, index, value } => { // Implement ArrayBox set(index, value) let arr_val = self.get_value(*array)?; let idx_val = self.get_value(*index)?; let val_val = self.get_value(*value)?; if let VMValue::BoxRef(arc) = arr_val { if let Some(arr) = arc.as_any().downcast_ref::() { let idx_box = idx_val.to_nyash_box(); let val_box = val_val.to_nyash_box(); let _ = arr.set(idx_box, val_box); return Ok(ControlFlow::Continue); } } Err(VMError::TypeError("ArraySet expects ArrayBox".to_string())) }, MirInstruction::Copy { dst, src } => { // Copy instruction - duplicate the source value let val = self.get_value(*src)?; self.set_value(*dst, val); // Propagate class mapping for references (helps track `me` copies) if let Some(class_name) = self.object_class.get(src).cloned() { self.object_class.insert(*dst, class_name); } // Propagate internal marker (me/this lineage) if self.object_internal.contains(src) { self.object_internal.insert(*dst); } Ok(ControlFlow::Continue) }, MirInstruction::Debug { value: _, message: _ } => { // Debug instruction - skip debug output for performance Ok(ControlFlow::Continue) }, MirInstruction::Nop => { // No-op instruction Ok(ControlFlow::Continue) }, // Phase 5: Control flow & exception handling MirInstruction::Throw { exception, effects: _ } => { let exception_val = self.get_value(*exception)?; // For now, convert throw to error return (simplified exception handling) // In a full implementation, this would unwind the stack looking for catch handlers println!("Exception thrown: {}", exception_val.to_string()); Err(VMError::InvalidInstruction(format!("Unhandled exception: {}", exception_val.to_string()))) }, MirInstruction::Catch { exception_type: _, exception_value, handler_bb: _ } => { // For now, catch is a no-op since we don't have full exception handling // In a real implementation, this would set up exception handling metadata self.set_value(*exception_value, VMValue::Void); Ok(ControlFlow::Continue) }, MirInstruction::Safepoint => { // Safepoint is a no-op for now // In a real implementation, this could trigger GC, debugging, etc. Ok(ControlFlow::Continue) }, // Phase 6: Box reference operations MirInstruction::RefNew { dst, box_val } => { // For now, a reference is just the same as the box value // In a real implementation, this would create a proper reference let box_value = self.get_value(*box_val)?; self.set_value(*dst, box_value); Ok(ControlFlow::Continue) }, MirInstruction::RefGet { dst, reference, field } => { // Visibility check (if class known and visibility declared). Skip for internal refs. let is_internal = self.object_internal.contains(reference); if !is_internal { if let Some(class_name) = self.object_class.get(reference) { if let Ok(decls) = self.runtime.box_declarations.read() { if let Some(decl) = decls.get(class_name) { let has_vis = !decl.public_fields.is_empty() || !decl.private_fields.is_empty(); if has_vis && !decl.public_fields.contains(field) { return Err(VMError::TypeError(format!("Field '{}' is private in {}", field, class_name))); } } } } } // Get field value from object let field_value = if let Some(fields) = self.object_fields.get(reference) { if let Some(value) = fields.get(field) { value.clone() } else { // Field not set yet, return default VMValue::Integer(0) } } else { // Object has no fields yet, return default VMValue::Integer(0) }; self.set_value(*dst, field_value); Ok(ControlFlow::Continue) }, MirInstruction::RefSet { reference, field, value } => { // Get the value to set let new_value = self.get_value(*value)?; // Visibility check (Skip for internal refs; otherwise enforce public) let is_internal = self.object_internal.contains(reference); if !is_internal { if let Some(class_name) = self.object_class.get(reference) { if let Ok(decls) = self.runtime.box_declarations.read() { if let Some(decl) = decls.get(class_name) { let has_vis = !decl.public_fields.is_empty() || !decl.private_fields.is_empty(); if has_vis && !decl.public_fields.contains(field) { return Err(VMError::TypeError(format!("Field '{}' is private in {}", field, class_name))); } } } } } // Ensure object has field storage if !self.object_fields.contains_key(reference) { self.object_fields.insert(*reference, HashMap::new()); } // Set the field if let Some(fields) = self.object_fields.get_mut(reference) { fields.insert(field.clone(), new_value); } Ok(ControlFlow::Continue) }, MirInstruction::WeakNew { dst, box_val } => { // For now, a weak reference is just a copy of the value // In a real implementation, this would create a proper weak reference let box_value = self.get_value(*box_val)?; self.set_value(*dst, box_value); Ok(ControlFlow::Continue) }, MirInstruction::WeakLoad { dst, weak_ref } => { // For now, loading from weak ref is the same as getting the value // In a real implementation, this would check if the weak ref is still valid let weak_value = self.get_value(*weak_ref)?; self.set_value(*dst, weak_value); Ok(ControlFlow::Continue) }, // Unified PoC ops mapped to legacy behavior MirInstruction::WeakRef { dst, op, value } => { match op { crate::mir::WeakRefOp::New => { let v = self.get_value(*value)?; self.set_value(*dst, v); } crate::mir::WeakRefOp::Load => { let v = self.get_value(*value)?; self.set_value(*dst, v); } } Ok(ControlFlow::Continue) }, MirInstruction::Barrier { .. } => { // No-op Ok(ControlFlow::Continue) }, MirInstruction::BarrierRead { ptr: _ } => { // Memory barrier read is a no-op for now // In a real implementation, this would ensure memory ordering Ok(ControlFlow::Continue) }, MirInstruction::BarrierWrite { ptr: _ } => { // Memory barrier write is a no-op for now // In a real implementation, this would ensure memory ordering Ok(ControlFlow::Continue) }, // Phase 7: Async/Future Operations MirInstruction::FutureNew { dst, value } => { let initial_value = self.get_value(*value)?; let future = crate::boxes::future::FutureBox::new(); // Convert VMValue to NyashBox and set it in the future let nyash_box = initial_value.to_nyash_box(); future.set_result(nyash_box); self.set_value(*dst, VMValue::Future(future)); Ok(ControlFlow::Continue) }, MirInstruction::FutureSet { future, value } => { let future_val = self.get_value(*future)?; let new_value = self.get_value(*value)?; if let VMValue::Future(ref future_box) = future_val { future_box.set_result(new_value.to_nyash_box()); Ok(ControlFlow::Continue) } else { Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) } }, MirInstruction::Await { dst, future } => { let future_val = self.get_value(*future)?; if let VMValue::Future(ref future_box) = future_val { // This blocks until the future is ready let result = future_box.get(); // Convert NyashBox back to VMValue let vm_value = VMValue::from_nyash_box(result); self.set_value(*dst, vm_value); Ok(ControlFlow::Continue) } else { Err(VMError::TypeError(format!("Expected Future, got {:?}", future_val))) } }, // Phase 9.7: External Function Calls MirInstruction::ExternCall { dst, iface_name, method_name, args, effects: _ } => { // Evaluate arguments as NyashBox for loader let mut nyash_args: Vec> = Vec::new(); for arg_id in args { let arg_value = self.get_value(*arg_id)?; nyash_args.push(arg_value.to_nyash_box()); } // Route through plugin loader v2 (also handles env.* stubs) let loader = crate::runtime::get_global_loader_v2(); let loader = loader.read().map_err(|_| VMError::InvalidInstruction("Plugin loader lock poisoned".into()))?; match loader.extern_call(iface_name, method_name, &nyash_args) { Ok(Some(result_box)) => { if let Some(dst_id) = dst { self.set_value(*dst_id, VMValue::from_nyash_box(result_box)); } } Ok(None) => { if let Some(dst_id) = dst { self.set_value(*dst_id, VMValue::Void); } } Err(_) => { return Err(VMError::InvalidInstruction(format!("ExternCall failed: {}.{}", iface_name, method_name))); } } Ok(ControlFlow::Continue) }, } } /// Get a value from storage fn get_value(&self, value_id: ValueId) -> Result { let index = value_id.to_usize(); if index < self.values.len() { if let Some(ref value) = self.values[index] { Ok(value.clone()) } else { Err(VMError::InvalidValue(format!("Value {} not set", value_id))) } } else { Err(VMError::InvalidValue(format!("Value {} out of bounds", value_id))) } } /// Set a value in the VM storage fn set_value(&mut self, value_id: ValueId, value: VMValue) { let index = value_id.to_usize(); // Resize Vec if necessary if index >= self.values.len() { self.values.resize(index + 1, None); } self.values[index] = Some(value); } /// Execute binary operation fn execute_binary_op(&self, op: &BinaryOp, left: &VMValue, right: &VMValue) -> Result { match (left, right) { (VMValue::Integer(l), VMValue::Integer(r)) => { let result = match op { BinaryOp::Add => *l + *r, BinaryOp::Sub => *l - *r, BinaryOp::Mul => *l * *r, BinaryOp::Div => { if *r == 0 { return Err(VMError::DivisionByZero); } *l / *r }, _ => return Err(VMError::InvalidInstruction(format!("Unsupported integer operation: {:?}", op))), }; Ok(VMValue::Integer(result)) }, (VMValue::String(l), VMValue::Integer(r)) => { // String + Integer concatenation match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), _ => Err(VMError::TypeError("String-integer operations only support addition".to_string())), } }, (VMValue::String(l), VMValue::Bool(r)) => { // String + Bool concatenation match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), _ => Err(VMError::TypeError("String-bool operations only support addition".to_string())), } }, (VMValue::String(l), VMValue::String(r)) => { // String concatenation match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r))), _ => Err(VMError::TypeError("String operations only support addition".to_string())), } }, // String + BoxRef concatenation (VMValue::String(l), VMValue::BoxRef(r)) => { match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, r.to_string_box().value))), _ => Err(VMError::TypeError("String-BoxRef operations only support addition".to_string())), } }, // BoxRef + String concatenation (VMValue::BoxRef(l), VMValue::String(r)) => { match op { BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l.to_string_box().value, r))), _ => Err(VMError::TypeError("BoxRef-String operations only support addition".to_string())), } }, _ => Err(VMError::TypeError(format!("Unsupported binary operation: {:?} on {:?} and {:?}", op, left, right))), } } /// Execute unary operation fn execute_unary_op(&self, op: &UnaryOp, operand: &VMValue) -> Result { match (op, operand) { (UnaryOp::Neg, VMValue::Integer(i)) => Ok(VMValue::Integer(-i)), (UnaryOp::Not, VMValue::Bool(b)) => Ok(VMValue::Bool(!b)), _ => Err(VMError::TypeError(format!("Unsupported unary operation: {:?} on {:?}", op, operand))), } } /// Execute comparison operation fn execute_compare_op(&self, op: &CompareOp, left: &VMValue, right: &VMValue) -> Result { match (left, right) { // Numeric mixed comparisons (Integer/Float) (VMValue::Integer(l), VMValue::Float(r)) => { let l = *l as f64; let r = *r; let result = match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r, }; Ok(result) }, (VMValue::Float(l), VMValue::Integer(r)) => { let l = *l; let r = *r as f64; let result = match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r, }; Ok(result) }, // Bool comparisons: support Eq/Ne only for now (VMValue::Bool(l), VMValue::Bool(r)) => { let result = match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, _ => return Err(VMError::TypeError(format!("Unsupported boolean comparison: {:?}", op))), }; Ok(result) }, // Void comparisons: only Eq/Ne are defined (VMValue::Void, VMValue::Void) => { let result = match op { CompareOp::Eq => true, CompareOp::Ne => false, _ => return Err(VMError::TypeError("Cannot order Void".to_string())), }; Ok(result) }, (VMValue::Void, _) | (_, VMValue::Void) => { let result = match op { CompareOp::Eq => false, // void == X (X != void) is false CompareOp::Ne => true, // void != X is true _ => return Err(VMError::TypeError("Cannot order Void".to_string())), }; Ok(result) }, (VMValue::Integer(l), VMValue::Integer(r)) => { let result = match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r, }; Ok(result) }, (VMValue::Float(l), VMValue::Float(r)) => { let result = match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r, }; Ok(result) }, (VMValue::String(l), VMValue::String(r)) => { let result = match op { CompareOp::Eq => l == r, CompareOp::Ne => l != r, CompareOp::Lt => l < r, CompareOp::Le => l <= r, CompareOp::Gt => l > r, CompareOp::Ge => l >= r, }; Ok(result) }, _ => Err(VMError::TypeError(format!("Unsupported comparison: {:?} on {:?} and {:?}", op, left, right))), } } /// Record an instruction execution for statistics fn record_instruction(&mut self, instruction: &MirInstruction) { let key: &'static str = match instruction { MirInstruction::Const { .. } => "Const", MirInstruction::BinOp { .. } => "BinOp", MirInstruction::UnaryOp { .. } => "UnaryOp", MirInstruction::Compare { .. } => "Compare", MirInstruction::Load { .. } => "Load", MirInstruction::Store { .. } => "Store", MirInstruction::Call { .. } => "Call", MirInstruction::BoxCall { .. } => "BoxCall", MirInstruction::Branch { .. } => "Branch", MirInstruction::Jump { .. } => "Jump", MirInstruction::Return { .. } => "Return", MirInstruction::Phi { .. } => "Phi", MirInstruction::NewBox { .. } => "NewBox", MirInstruction::TypeCheck { .. } => "TypeCheck", MirInstruction::Cast { .. } => "Cast", MirInstruction::TypeOp { .. } => "TypeOp", MirInstruction::ArrayGet { .. } => "ArrayGet", MirInstruction::ArraySet { .. } => "ArraySet", MirInstruction::Copy { .. } => "Copy", MirInstruction::Debug { .. } => "Debug", MirInstruction::Print { .. } => "Print", MirInstruction::Nop => "Nop", MirInstruction::Throw { .. } => "Throw", MirInstruction::Catch { .. } => "Catch", MirInstruction::Safepoint => "Safepoint", MirInstruction::RefNew { .. } => "RefNew", MirInstruction::RefGet { .. } => "RefGet", MirInstruction::RefSet { .. } => "RefSet", MirInstruction::WeakNew { .. } => "WeakNew", MirInstruction::WeakLoad { .. } => "WeakLoad", MirInstruction::BarrierRead { .. } => "BarrierRead", MirInstruction::BarrierWrite { .. } => "BarrierWrite", MirInstruction::WeakRef { .. } => "WeakRef", MirInstruction::Barrier { .. } => "Barrier", MirInstruction::FutureNew { .. } => "FutureNew", MirInstruction::FutureSet { .. } => "FutureSet", MirInstruction::Await { .. } => "Await", MirInstruction::ExternCall { .. } => "ExternCall", }; *self.instr_counter.entry(key).or_insert(0) += 1; } fn debug_log_boxcall(&self, recv: &VMValue, method: &str, args: &[Box], stage: &str, result: Option<&VMValue>) { if std::env::var("NYASH_VM_DEBUG_BOXCALL").ok().as_deref() == Some("1") { let recv_ty = match recv { VMValue::BoxRef(arc) => arc.type_name().to_string(), VMValue::Integer(_) => "Integer".to_string(), VMValue::Float(_) => "Float".to_string(), VMValue::Bool(_) => "Bool".to_string(), VMValue::String(_) => "String".to_string(), VMValue::Future(_) => "Future".to_string(), VMValue::Void => "Void".to_string(), }; let args_desc: Vec = args.iter().map(|a| a.type_name().to_string()).collect(); if let Some(res) = result { let res_ty = match res { VMValue::BoxRef(arc) => format!("BoxRef({})", arc.type_name()), VMValue::Integer(_) => "Integer".to_string(), VMValue::Float(_) => "Float".to_string(), VMValue::Bool(_) => "Bool".to_string(), VMValue::String(_) => "String".to_string(), VMValue::Future(_) => "Future".to_string(), VMValue::Void => "Void".to_string(), }; eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?} => result_ty={}", stage, recv_ty, method, args.len(), args_desc, res_ty); } else { eprintln!("[VM-BOXCALL][{}] recv_ty={} method={} argc={} args={:?}", stage, recv_ty, method, args.len(), args_desc); } } } /// Print simple VM execution statistics when enabled via env var fn maybe_print_stats(&mut self) { let enabled = std::env::var("NYASH_VM_STATS").ok().map(|v| v != "0").unwrap_or(false); if !enabled { return; } let elapsed_ms = self.exec_start.map(|t| t.elapsed().as_secs_f64() * 1000.0).unwrap_or(0.0); let mut items: Vec<(&str, usize)> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); items.sort_by(|a,b| b.1.cmp(&a.1).then_with(|| a.0.cmp(&b.0))); let total: usize = items.iter().map(|(_,v)| *v).sum(); let json_enabled = std::env::var("NYASH_VM_STATS_JSON").ok().map(|v| v != "0").unwrap_or(false) || std::env::var("NYASH_VM_STATS_FORMAT").map(|v| v == "json").unwrap_or(false); if json_enabled { // Build JSON structure: { total, elapsed_ms, counts: {op: n, ...}, top20: [{op, count}], timestamp_ms } let counts_obj: std::collections::BTreeMap<&str, usize> = self.instr_counter.iter().map(|(k,v)| (*k, *v)).collect(); let top20: Vec<_> = items.iter().take(20).map(|(op,cnt)| { serde_json::json!({ "op": op, "count": cnt }) }).collect(); let now_ms = { use std::time::{SystemTime, UNIX_EPOCH}; SystemTime::now().duration_since(UNIX_EPOCH).map(|d| d.as_millis() as u64).unwrap_or(0) }; let payload = serde_json::json!({ "total": total, "elapsed_ms": elapsed_ms, "counts": counts_obj, "top20": top20, "timestamp_ms": now_ms }); match serde_json::to_string_pretty(&payload) { Ok(s) => println!("{}", s), Err(_) => println!("{{\"total\":{},\"elapsed_ms\":{:.3}}}", total, elapsed_ms), } } else { println!("\n🧮 VM Stats: {} instructions in {:.3} ms", total, elapsed_ms); for (k, v) in items.into_iter().take(20) { println!(" {:>10}: {:>8}", k, v); } } } /// Phase 9.78a: Unified method dispatch for all Box types fn call_unified_method(&self, box_value: Box, method: &str, args: Vec>) -> Result, VMError> { // For now, we use the simplified method dispatch // In a full implementation, this would check for InstanceBox and dispatch appropriately self.call_box_method(box_value, method, args) } /// Call a method on a Box - simplified version of interpreter method dispatch fn call_box_method(&self, box_value: Box, method: &str, _args: Vec>) -> Result, VMError> { // For now, implement basic methods for common box types // This is a simplified version - real implementation would need full method dispatch // ResultBox (NyashResultBox - new) if let Some(result_box) = box_value.as_any().downcast_ref::() { match method { // Rust側の公開APIメソッド名に合わせたバリアントを許容 "is_ok" | "isOk" => { return Ok(result_box.is_ok()); } "get_value" | "getValue" => { return Ok(result_box.get_value()); } "get_error" | "getError" => { return Ok(result_box.get_error()); } _ => return Ok(Box::new(VoidBox::new())), } } // ResultBox (box_trait::ResultBox - legacy) if let Some(result_box_legacy) = box_value.as_any().downcast_ref::() { match method { "is_ok" | "isOk" => { return Ok(result_box_legacy.is_ok()); } "get_value" | "getValue" => { return Ok(result_box_legacy.get_value()); } "get_error" | "getError" => { return Ok(result_box_legacy.get_error()); } _ => return Ok(Box::new(VoidBox::new())), } } // Generic fallback: toString for any Box type if method == "toString" { return Ok(Box::new(StringBox::new(box_value.to_string_box().value))); } // StringBox methods if let Some(string_box) = box_value.as_any().downcast_ref::() { match method { "length" | "len" => { return Ok(Box::new(IntegerBox::new(string_box.value.len() as i64))); }, "toString" => { return Ok(Box::new(StringBox::new(string_box.value.clone()))); }, "substring" => { // substring(start, end) - simplified implementation if _args.len() >= 2 { if let (Some(start_box), Some(end_box)) = (_args.get(0), _args.get(1)) { if let (Some(start_int), Some(end_int)) = ( start_box.as_any().downcast_ref::(), end_box.as_any().downcast_ref::() ) { let start = start_int.value.max(0) as usize; let end = end_int.value.max(0) as usize; let len = string_box.value.len(); if start <= len { let end_idx = end.min(len); if start <= end_idx { let substr = &string_box.value[start..end_idx]; return Ok(Box::new(StringBox::new(substr))); } } } } } return Ok(Box::new(StringBox::new(""))); // Return empty string on error }, "concat" => { // concat(other) - concatenate with another string if let Some(other_box) = _args.get(0) { let other_str = other_box.to_string_box().value; let result = string_box.value.clone() + &other_str; return Ok(Box::new(StringBox::new(result))); } return Ok(Box::new(StringBox::new(string_box.value.clone()))); }, _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } } // ArrayBox methods (minimal set) if let Some(array_box) = box_value.as_any().downcast_ref::() { match method { "push" => { if let Some(v) = _args.get(0) { return Ok(array_box.push(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: push(value) requires 1 arg"))); }, "pop" => { return Ok(array_box.pop()); }, "length" | "len" => { return Ok(array_box.length()); }, "get" => { if let Some(i) = _args.get(0) { return Ok(array_box.get(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(index) requires 1 arg"))); }, "set" => { if _args.len() >= 2 { return Ok(array_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(index, value) requires 2 args"))); }, "remove" => { if let Some(i) = _args.get(0) { return Ok(array_box.remove(i.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: remove(index) requires 1 arg"))); }, "contains" => { if let Some(v) = _args.get(0) { return Ok(array_box.contains(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: contains(value) requires 1 arg"))); }, "indexOf" => { if let Some(v) = _args.get(0) { return Ok(array_box.indexOf(v.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: indexOf(value) requires 1 arg"))); }, "clear" => { return Ok(array_box.clear()); }, "join" => { if let Some(sep) = _args.get(0) { return Ok(array_box.join(sep.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: join(sep) requires 1 arg"))); }, "sort" => { return Ok(array_box.sort()); }, "reverse" => { return Ok(array_box.reverse()); }, "slice" => { if _args.len() >= 2 { return Ok(array_box.slice(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: slice(start, end) requires 2 args"))); }, _ => return Ok(Box::new(VoidBox::new())), } } // MapBox methods (minimal set) if let Some(map_box) = box_value.as_any().downcast_ref::() { match method { "set" => { if _args.len() >= 2 { return Ok(map_box.set(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: set(key, value) requires 2 args"))); }, "get" => { if let Some(k) = _args.get(0) { return Ok(map_box.get(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: get(key) requires 1 arg"))); }, "has" => { if let Some(k) = _args.get(0) { return Ok(map_box.has(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: has(key) requires 1 arg"))); }, "delete" | "remove" => { if let Some(k) = _args.get(0) { return Ok(map_box.delete(k.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: delete(key) requires 1 arg"))); }, "keys" => { return Ok(map_box.keys()); }, "values" => { return Ok(map_box.values()); }, "size" => { return Ok(map_box.size()); }, "clear" => { return Ok(map_box.clear()); }, _ => return Ok(Box::new(VoidBox::new())), } } // SocketBox methods (minimal set + timeout variants) if let Some(sock) = box_value.as_any().downcast_ref::() { match method { "bind" => { if _args.len() >= 2 { return Ok(sock.bind(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: bind(address, port) requires 2 args"))); }, "listen" => { if let Some(b) = _args.get(0) { return Ok(sock.listen(b.clone_or_share())); } return Ok(Box::new(StringBox::new("Error: listen(backlog) requires 1 arg"))); }, "accept" => { return Ok(sock.accept()); }, "acceptTimeout" | "accept_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.accept_timeout(ms.clone_or_share())); } return Ok(Box::new(crate::box_trait::VoidBox::new())); }, "connect" => { if _args.len() >= 2 { return Ok(sock.connect(_args[0].clone_or_share(), _args[1].clone_or_share())); } return Ok(Box::new(StringBox::new("Error: connect(address, port) requires 2 args"))); }, "read" => { return Ok(sock.read()); }, "recvTimeout" | "recv_timeout" => { if let Some(ms) = _args.get(0) { return Ok(sock.recv_timeout(ms.clone_or_share())); } return Ok(Box::new(StringBox::new(""))); }, "write" => { if let Some(d) = _args.get(0) { return Ok(sock.write(d.clone_or_share())); } return Ok(Box::new(crate::box_trait::BoolBox::new(false))); }, "close" => { return Ok(sock.close()); }, "isServer" | "is_server" => { return Ok(sock.is_server()); }, "isConnected" | "is_connected" => { return Ok(sock.is_connected()); }, _ => return Ok(Box::new(VoidBox::new())), } } // IntegerBox methods if let Some(integer_box) = box_value.as_any().downcast_ref::() { match method { "toString" => { return Ok(Box::new(StringBox::new(integer_box.value.to_string()))); }, "abs" => { return Ok(Box::new(IntegerBox::new(integer_box.value.abs()))); }, _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } } // BoolBox methods if let Some(bool_box) = box_value.as_any().downcast_ref::() { match method { "toString" => { return Ok(Box::new(StringBox::new(bool_box.value.to_string()))); }, _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } } // ArrayBox methods - needed for kilo editor if let Some(array_box) = box_value.as_any().downcast_ref::() { match method { "length" | "len" => { let items = array_box.items.read().unwrap(); return Ok(Box::new(IntegerBox::new(items.len() as i64))); }, "get" => { // get(index) - get element at index if let Some(index_box) = _args.get(0) { if let Some(index_int) = index_box.as_any().downcast_ref::() { let items = array_box.items.read().unwrap(); let index = index_int.value as usize; if index < items.len() { return Ok(items[index].clone_box()); } } } return Ok(Box::new(VoidBox::new())); // Return void for out of bounds }, "set" => { // set(index, value) - simplified implementation // Note: This is a read-only operation in the VM for now // In a real implementation, we'd need mutable access return Ok(Box::new(VoidBox::new())); }, "push" => { // push(value) - simplified implementation // Note: This is a read-only operation in the VM for now return Ok(Box::new(VoidBox::new())); }, "insert" => { // insert(index, value) - simplified implementation // Note: This is a read-only operation in the VM for now return Ok(Box::new(VoidBox::new())); }, _ => return Ok(Box::new(VoidBox::new())), // Unsupported method } } // PluginBoxV2 support if let Some(plugin_box) = box_value.as_any().downcast_ref::() { // For toString on plugins, return a descriptive string if method == "toString" { return Ok(Box::new(StringBox::new(format!("{}(id={})", plugin_box.box_type, plugin_box.inner.instance_id)))); } // Other plugin methods should be called via BoxCall instruction // This path shouldn't normally be reached for plugin methods eprintln!("Warning: Plugin method '{}' called via call_box_method - should use BoxCall", method); return Ok(Box::new(VoidBox::new())); } // Default: return void for any unrecognized box type or method Ok(Box::new(VoidBox::new())) } } /// Control flow result from instruction execution enum ControlFlow { Continue, Jump(BasicBlockId), Return(VMValue), } impl Default for VM { fn default() -> Self { Self::new() } } #[cfg(test)] mod tests { use super::*; use crate::mir::{MirModule, MirFunction, FunctionSignature, MirType, EffectMask, BasicBlock}; use crate::parser::NyashParser; use crate::runtime::NyashRuntime; use crate::core::model::BoxDeclaration as CoreBoxDecl; use crate::interpreter::SharedState; use crate::box_factory::user_defined::UserDefinedBoxFactory; use std::sync::Arc; use std::collections::HashMap; #[test] fn test_basic_vm_execution() { let mut vm = VM::new(); // Test constant loading let const_instr = MirInstruction::Const { dst: ValueId(1), value: ConstValue::Integer(42), }; let result = vm.execute_instruction(&const_instr); assert!(result.is_ok()); let value = vm.get_value(ValueId(1)).unwrap(); assert_eq!(value.as_integer().unwrap(), 42); } #[test] fn test_binary_operations() { let mut vm = VM::new(); // Load constants vm.set_value(ValueId(1), VMValue::Integer(10)); vm.set_value(ValueId(2), VMValue::Integer(32)); // Test addition let add_instr = MirInstruction::BinOp { dst: ValueId(3), op: BinaryOp::Add, lhs: ValueId(1), rhs: ValueId(2), }; let result = vm.execute_instruction(&add_instr); assert!(result.is_ok()); let value = vm.get_value(ValueId(3)).unwrap(); assert_eq!(value.as_integer().unwrap(), 42); } fn collect_box_declarations(ast: &crate::ast::ASTNode, runtime: &NyashRuntime) { fn walk(node: &crate::ast::ASTNode, runtime: &NyashRuntime) { match node { crate::ast::ASTNode::Program { statements, .. } => { for st in statements { walk(st, runtime); } } crate::ast::ASTNode::BoxDeclaration { name, fields, public_fields, private_fields, methods, constructors, init_fields, weak_fields, is_interface, extends, implements, type_parameters, .. } => { let decl = CoreBoxDecl { name: name.clone(), fields: fields.clone(), public_fields: public_fields.clone(), private_fields: private_fields.clone(), methods: methods.clone(), constructors: constructors.clone(), init_fields: init_fields.clone(), weak_fields: weak_fields.clone(), is_interface: *is_interface, extends: extends.clone(), implements: implements.clone(), type_parameters: type_parameters.clone(), }; if let Ok(mut map) = runtime.box_declarations.write() { map.insert(name.clone(), decl); } } _ => {} } } walk(ast, runtime); } #[test] fn test_vm_user_box_birth_and_method() { let code = r#" box Person { init { name } birth(n) { me.name = n } greet() { return "Hello, " + me.name } } return new Person("Alice").greet() "#; // Parse to AST let ast = NyashParser::parse_from_string(code).expect("parse failed"); // Prepare runtime with user-defined declarations and factory let runtime = { let rt = NyashRuntime::new(); collect_box_declarations(&ast, &rt); let mut shared = SharedState::new(); shared.box_declarations = rt.box_declarations.clone(); let udf = Arc::new(UserDefinedBoxFactory::new(shared)); if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); } rt }; // Compile to MIR let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); // Debug: Print MIR println!("=== MIR Output ==="); let mut printer = crate::mir::MirPrinter::verbose(); println!("{}", printer.print_module(&compile_result.module)); println!("=================="); // Execute with VM let mut vm = VM::with_runtime(runtime); let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); assert_eq!(result.to_string_box().value, "Hello, Alice"); } #[test] fn test_vm_user_box_var_then_method() { let code = r#" box Counter { init { x } birth(n) { me.x = n } inc() { me.x = me.x + 1 } get() { return me.x } } local c c = new Counter(10) c.inc() c.get() "#; let ast = NyashParser::parse_from_string(code).expect("parse failed"); let runtime = { let rt = NyashRuntime::new(); collect_box_declarations(&ast, &rt); let mut shared = SharedState::new(); shared.box_declarations = rt.box_declarations.clone(); let udf = Arc::new(UserDefinedBoxFactory::new(shared)); if let Ok(mut reg) = rt.box_registry.lock() { reg.register(udf); } rt }; let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); let mut vm = VM::with_runtime(runtime); let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); assert_eq!(result.to_string_box().value, "11"); } #[test] fn test_vm_extern_console_log() { let code = r#" console.log("ok") "#; let ast = NyashParser::parse_from_string(code).expect("parse failed"); let runtime = NyashRuntime::new(); let mut compiler = crate::mir::MirCompiler::new(); let compile_result = compiler.compile(ast).expect("mir compile failed"); let mut vm = VM::with_runtime(runtime); let result = vm.execute_module(&compile_result.module).expect("vm exec failed"); assert_eq!(result.to_string_box().value, "void"); } }