stage3: unify to cleanup; MIR return-defer; docs+smokes updated; LLVM(harness): finalize_phis ownership, ret.py simplified, uses-predeclare; cleanup return override green; method-postfix cleanup return WIP (PHI head)

This commit is contained in:
Selfhosting Dev
2025-09-19 02:07:38 +09:00
parent 951a050592
commit 5e818eeb7e
205 changed files with 9671 additions and 1849 deletions

View File

@ -1,32 +0,0 @@
/*!
* VM Control-Flow helpers (scaffolding)
*
* Purpose: Encapsulate block transitions, branching decisions, and phi entry bookkeeping.
* Status: Initial skeleton for future extraction from vm.rs
*/
use super::vm::VMError;
use crate::mir::BasicBlockId;
/// Result of a block step when evaluating a terminator
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Step {
/// Continue in the same block (advance pc)
Continue,
/// Jump to target block
Jump(BasicBlockId),
/// Function returned (handled by VM)
Return,
}
/// Record a block transition inside VM bookkeeping
pub fn record_transition(
previous_block: &mut Option<BasicBlockId>,
loop_recorder: &mut crate::backend::vm_phi::LoopExecutor,
from: BasicBlockId,
to: BasicBlockId,
) -> Result<(), VMError> {
*previous_block = Some(from);
loop_recorder.record_transition(from, to);
Ok(())
}

View File

@ -1,298 +0,0 @@
/*!
* VM Dispatch table (scaffolding)
*
* Purpose: Centralize mapping from MIR instruction kinds to handler fns.
* Status: Initial skeleton; currently unused. Future: build static table for hot-path dispatch.
*/
use super::vm::ControlFlow;
use super::vm::VMValue;
use super::vm::{VMError, VM};
use crate::mir::CompareOp;
use crate::mir::MirInstruction;
/// Minimal dispatcher that routes a single instruction to the appropriate handler.
/// Keeps behavior identical to the big match in vm.rs but centralized here.
pub(super) fn execute_instruction(
vm: &mut VM,
instruction: &MirInstruction,
debug_global: bool,
) -> Result<ControlFlow, VMError> {
match instruction {
// Basic operations
MirInstruction::Const { dst, value } => vm.execute_const(*dst, value),
MirInstruction::BinOp { dst, op, lhs, rhs } => {
if debug_global || std::env::var("NYASH_VM_DEBUG_ANDOR").ok().as_deref() == Some("1") {
eprintln!("[VM] execute_instruction -> BinOp({:?})", op);
}
vm.execute_binop(*dst, op, *lhs, *rhs)
}
MirInstruction::UnaryOp { dst, op, operand } => vm.execute_unaryop(*dst, op, *operand),
MirInstruction::Compare { dst, op, lhs, rhs } => {
let debug_cmp =
debug_global || std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] dispatch Compare op={:?} lhs={:?} rhs={:?}",
op, lhs, rhs
);
}
if let (Ok(lv), Ok(rv)) = (vm.get_value(*lhs), vm.get_value(*rhs)) {
if debug_cmp {
eprintln!("[VM] values before fastpath: left={:?} right={:?}", lv, rv);
}
if let (VMValue::BoxRef(lb), VMValue::BoxRef(rb)) = (&lv, &rv) {
if debug_cmp {
eprintln!(
"[VM] BoxRef types: lty={} rty={} lstr={} rstr={}",
lb.type_name(),
rb.type_name(),
lb.to_string_box().value,
rb.to_string_box().value
);
}
let li = lb
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| lb.to_string_box().value.trim().parse::<i64>().ok());
let ri = rb
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| rb.to_string_box().value.trim().parse::<i64>().ok());
if let (Some(li), Some(ri)) = (li, ri) {
let out = match op {
CompareOp::Eq => li == ri,
CompareOp::Ne => li != ri,
CompareOp::Lt => li < ri,
CompareOp::Le => li <= ri,
CompareOp::Gt => li > ri,
CompareOp::Ge => li >= ri,
};
vm.set_value(*dst, VMValue::Bool(out));
return Ok(ControlFlow::Continue);
}
}
}
vm.execute_compare(*dst, op, *lhs, *rhs)
}
// I/O operations
MirInstruction::Print { value, .. } => vm.execute_print(*value),
// Type operations
MirInstruction::TypeOp { dst, op, value, ty } => vm.execute_typeop(*dst, op, *value, ty),
// Control flow
MirInstruction::Return { value } => {
if crate::config::env::vm_vt_trace() {
if let Some(v) = value {
eprintln!("[VT] Dispatch Return val_id={}", v.to_usize());
} else {
eprintln!("[VT] Dispatch Return void");
}
}
vm.execute_return(*value)
}
MirInstruction::Jump { target } => vm.execute_jump(*target),
MirInstruction::Branch {
condition,
then_bb,
else_bb,
} => vm.execute_branch(*condition, *then_bb, *else_bb),
MirInstruction::Phi { dst, inputs } => vm.execute_phi(*dst, inputs),
// Memory operations
MirInstruction::Load { dst, ptr } => vm.execute_load(*dst, *ptr),
MirInstruction::Store { value, ptr } => vm.execute_store(*value, *ptr),
MirInstruction::Copy { dst, src } => vm.execute_copy(*dst, *src),
// Complex operations
MirInstruction::Call {
dst,
func,
args,
effects: _,
} => vm.execute_call(*dst, *func, args),
MirInstruction::FunctionNew {
dst,
params,
body,
captures,
me,
} => vm.execute_function_new(*dst, params, body, captures, me),
MirInstruction::BoxCall {
dst,
box_val,
method,
method_id,
args,
effects: _,
..
} => vm.execute_boxcall(*dst, *box_val, method, *method_id, args),
MirInstruction::PluginInvoke {
dst,
box_val,
method,
args,
effects: _,
} => vm.execute_plugin_invoke(*dst, *box_val, method, args),
MirInstruction::NewBox {
dst,
box_type,
args,
} => vm.execute_newbox(*dst, box_type, args),
// Array operations
MirInstruction::ArrayGet { dst, array, index } => {
vm.execute_array_get(*dst, *array, *index)
}
MirInstruction::ArraySet {
array,
index,
value,
} => vm.execute_array_set(*array, *index, *value),
// Reference operations
MirInstruction::RefNew { dst, box_val } => vm.execute_ref_new(*dst, *box_val),
MirInstruction::RefGet {
dst,
reference,
field,
} => vm.execute_ref_get(*dst, *reference, field),
MirInstruction::RefSet {
reference,
field,
value,
} => vm.execute_ref_set(*reference, field, *value),
// Weak references
MirInstruction::WeakNew { dst, box_val } => vm.execute_weak_new(*dst, *box_val),
MirInstruction::WeakLoad { dst, weak_ref } => vm.execute_weak_load(*dst, *weak_ref),
MirInstruction::WeakRef { dst, op, value } => match op {
crate::mir::WeakRefOp::New => vm.execute_weak_new(*dst, *value),
crate::mir::WeakRefOp::Load => vm.execute_weak_load(*dst, *value),
},
// Barriers
MirInstruction::BarrierRead { .. } => {
if crate::config::env::gc_trace() {
let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: Read @{} bb={} pc={}", func, bb, pc);
}
vm.runtime.gc.barrier(crate::runtime::gc::BarrierKind::Read);
Ok(ControlFlow::Continue)
}
MirInstruction::BarrierWrite { .. } => {
if crate::config::env::gc_trace() {
let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: Write @{} bb={} pc={}", func, bb, pc);
}
vm.runtime
.gc
.barrier(crate::runtime::gc::BarrierKind::Write);
Ok(ControlFlow::Continue)
}
MirInstruction::Barrier { op, .. } => {
let k = match op {
crate::mir::BarrierOp::Read => crate::runtime::gc::BarrierKind::Read,
crate::mir::BarrierOp::Write => crate::runtime::gc::BarrierKind::Write,
};
if crate::config::env::gc_trace() {
let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] barrier: {:?} @{} bb={} pc={}", k, func, bb, pc);
}
vm.runtime.gc.barrier(k);
Ok(ControlFlow::Continue)
}
// Exceptions
MirInstruction::Throw { exception, .. } => vm.execute_throw(*exception),
MirInstruction::Catch {
exception_value, ..
} => vm.execute_catch(*exception_value),
// Futures
MirInstruction::FutureNew { dst, value } => {
let initial_value = vm.get_value(*value)?;
let future = crate::boxes::future::FutureBox::new();
let nyash_box = initial_value.to_nyash_box();
future.set_result(nyash_box);
vm.set_value(*dst, VMValue::Future(future));
Ok(ControlFlow::Continue)
}
MirInstruction::FutureSet { future, value } => {
let future_val = vm.get_value(*future)?;
let new_value = vm.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
)))
}
}
// Special
MirInstruction::Await { dst, future } => vm.execute_await(*dst, *future),
MirInstruction::ExternCall {
dst,
iface_name,
method_name,
args,
..
} => vm.execute_extern_call(*dst, iface_name, method_name, args),
MirInstruction::TypeCheck { dst, .. } => {
vm.set_value(*dst, VMValue::Bool(true));
Ok(ControlFlow::Continue)
}
MirInstruction::Cast { dst, value, .. } => {
let val = vm.get_value(*value)?;
vm.set_value(*dst, val);
Ok(ControlFlow::Continue)
}
MirInstruction::Debug { .. } => Ok(ControlFlow::Continue),
MirInstruction::Nop => Ok(ControlFlow::Continue),
MirInstruction::Safepoint => {
if crate::config::env::gc_trace() {
let (func, bb, pc) = vm.gc_site_info();
eprintln!("[GC] safepoint @{} bb={} pc={}", func, bb, pc);
}
vm.runtime.gc.safepoint();
// Cooperative scheduling: poll single-thread scheduler
if let Some(s) = &vm.runtime.scheduler {
s.poll();
}
Ok(ControlFlow::Continue)
}
}
}
/// Placeholder for an instruction dispatch entry
pub struct DispatchEntry;
/// Placeholder dispatch table
pub struct DispatchTable;
impl DispatchTable {
pub fn new() -> Self {
Self
}
/// Example API for future use: resolve a handler for an instruction
pub fn resolve(&self, _instr: &MirInstruction) -> Option<DispatchEntry> {
None
}
}
/// Example execution of a dispatch entry
pub fn execute_entry(_entry: &DispatchEntry) -> Result<(), VMError> {
Ok(())
}
#![cfg(feature = "vm-legacy")]

View File

@ -1,30 +0,0 @@
/*!
* VM Frame management (scaffolding)
*
* Purpose: Provide a dedicated struct for per-function execution state (pc, block, locals).
* Status: Initial skeleton; VM still stores fields directly.
*/
use crate::mir::{BasicBlockId, ValueId};
#[derive(Debug, Default, Clone)]
pub struct ExecutionFrame {
pub current_block: Option<BasicBlockId>,
pub pc: usize,
pub last_result: Option<ValueId>,
}
impl ExecutionFrame {
pub fn new() -> Self {
Self {
current_block: None,
pc: 0,
last_result: None,
}
}
pub fn reset(&mut self) {
self.current_block = None;
self.pc = 0;
self.last_result = None;
}
}

View File

@ -5,18 +5,24 @@
// VM core types are always available
pub mod vm_types;
// Legacy VM execution pipeline (feature-gated)
// Legacy VM execution pipeline (feature-gated) — loaded from archive path
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm.rs"]
pub mod vm;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_boxcall.rs"]
pub mod vm_boxcall;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_instructions/mod.rs"]
pub mod vm_instructions;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_phi.rs"]
pub mod vm_phi;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_stats.rs"]
pub mod vm_stats;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_values.rs"]
pub mod vm_values;
// When vm-legacy is disabled, provide a compatibility shim module so
@ -28,22 +34,30 @@ pub mod vm {
// Phase 9.78h: VM split scaffolding (control_flow/dispatch/frame)
pub mod abi_util; // Shared ABI/utility helpers
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/control_flow.rs"]
pub mod control_flow;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/dispatch.rs"]
pub mod dispatch;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/frame.rs"]
pub mod frame;
pub mod gc_helpers;
pub mod mir_interpreter;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_control_flow.rs"]
pub mod vm_control_flow;
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_exec.rs"]
mod vm_exec; // A3: execution loop extracted
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_gc.rs"]
mod vm_gc; // A3: GC roots & diagnostics extracted
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_methods.rs"]
mod vm_methods; // A3-S1: method dispatch wrappers extracted
#[cfg(feature = "vm-legacy")]
#[path = "../archive/vm_legacy/vm_state.rs"]
mod vm_state; // A3: state & basic helpers extracted // Lightweight MIR interpreter
#[cfg(feature = "wasm-backend")]

View File

@ -1,869 +0,0 @@
/*!
* VM Backend - Execute MIR instructions in a virtual machine
*
* Purpose: Core VM (execute loop, storage, control-flow, integration glue)
* Responsibilities: fetch/dispatch instructions, manage values/blocks, stats hooks
* Key APIs: VM::execute_module, execute_instruction, get_value/set_value
* Typical Callers: runner (VM backend), instruction handlers (vm_instructions)
*/
use crate::mir::{BasicBlockId, MirModule, ValueId};
use crate::runtime::NyashRuntime;
use crate::scope_tracker::ScopeTracker;
use std::collections::HashMap;
// MirModule is already imported via crate::mir at top
use super::frame::ExecutionFrame;
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;
// Thinned: core types moved to vm_types.rs
pub use super::vm_types::{VMError, VMValue};
/// Virtual Machine state
pub struct VM {
/// Value storage (uses ValueId as direct index into Vec for O(1) access)
pub(super) values: Vec<Option<VMValue>>,
/// Current function being executed
pub(super) current_function: Option<String>,
/// Frame state (current block, pc, last result)
pub(super) frame: ExecutionFrame,
/// Previous basic block (for phi node resolution)
pub(super) previous_block: Option<BasicBlockId>,
/// Simple field storage for objects (maps reference -> field -> value)
pub(super) object_fields: HashMap<ValueId, HashMap<String, VMValue>>,
/// Class name mapping for objects (for visibility checks)
pub(super) object_class: HashMap<ValueId, String>,
/// Marks ValueIds that represent internal (me/this) references within the current function
pub(super) object_internal: std::collections::HashSet<ValueId>,
/// Loop executor for handling phi nodes and loop-specific logic
pub(super) loop_executor: LoopExecutor,
/// Shared runtime for box creation and declarations
pub(super) runtime: NyashRuntime,
/// Scope tracker for calling fini on scope exit
pub(super) scope_tracker: ScopeTracker,
/// Active MIR module during execution (for function calls)
pub(super) module: Option<MirModule>,
/// Instruction execution counters (by MIR opcode)
pub(super) instr_counter: std::collections::HashMap<&'static str, usize>,
/// Execution start time for optional stats
pub(super) exec_start: Option<Instant>,
/// Stats: number of BoxCall hits via VTable path
pub(super) boxcall_hits_vtable: u64,
/// Stats: number of BoxCall hits via Poly-PIC path
pub(super) boxcall_hits_poly_pic: u64,
/// Stats: number of BoxCall hits via Mono-PIC path
pub(super) boxcall_hits_mono_pic: u64,
/// Stats: number of BoxCall hits via generic fallback path
pub(super) boxcall_hits_generic: u64,
/// Mono-PIC skeleton: global hit counters keyed by (recv_type, method_id/name)
pub(super) boxcall_pic_hits: std::collections::HashMap<String, u32>,
/// Mono-PIC: cached direct targets (currently InstanceBox function name)
pub(super) boxcall_pic_funcname: std::collections::HashMap<String, String>,
/// Poly-PIC: per call-site up to 4 entries of (label, version, func_name)
pub(super) boxcall_poly_pic: std::collections::HashMap<String, Vec<(String, u32, String)>>,
/// VTable-like cache: (type, method_id, arity) → direct target (InstanceBox method)
pub(super) boxcall_vtable_funcname: std::collections::HashMap<String, String>,
/// Version map for cache invalidation: label -> version
pub(super) type_versions: std::collections::HashMap<String, u32>,
/// Optional JIT manager (Phase 10_a skeleton)
pub(super) jit_manager: Option<crate::jit::manager::JitManager>,
// 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<UnifiedBoxRegistry>,
// /// Plugin loader for external Box types
// #[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
// plugin_loader: Option<Arc<PluginLoaderV2>>,
// Scope tracker for lifecycle management
// scope_tracker: ScopeTracker,
// /// Box declarations from the AST
// box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>,
}
impl VM {
pub fn runtime_ref(&self) -> &NyashRuntime {
&self.runtime
}
// 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<UnifiedBoxRegistry>,
box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>
) -> 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<UnifiedBoxRegistry>,
plugin_loader: Arc<PluginLoaderV2>,
box_declarations: Arc<RwLock<HashMap<String, BoxDeclaration>>>,
) -> Self {
// Implementation pending interpreter refactoring
unimplemented!()
}
*/
// Call a method on a Box - moved to vm_methods.rs (wrapper now in vm_methods)
// removed: old inline implementation
/*
// ResultBox (NyashResultBox - new)
if let Some(result_box) = box_value.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
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())),
}
}
// Legacy box_trait::ResultBox is no longer handled here (migration complete)
// InstanceBox field access unification: getField/setField
if let Some(instance) = box_value.as_any().downcast_ref::<InstanceBox>() {
match method {
"getField" => {
if _args.len() != 1 { return Ok(Box::new(StringBox::new("getField(name) requires 1 arg"))); }
let name = _args[0].to_string_box().value;
if let Some(shared) = instance.get_field(&name) {
return Ok(shared.clone_box());
}
return Ok(Box::new(VoidBox::new()));
}
"setField" => {
if _args.len() != 2 { return Ok(Box::new(StringBox::new("setField(name, value) requires 2 args"))); }
let name = _args[0].to_string_box().value;
let val_arc: crate::box_trait::SharedNyashBox = std::sync::Arc::from(_args[1].clone_or_share());
let _ = instance.set_field(&name, val_arc);
return Ok(Box::new(VoidBox::new()));
}
_ => {}
}
}
// JitStatsBox methods (process-local JIT counters)
if let Some(_jsb) = box_value.as_any().downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>() {
match method {
"toJson" | "toJSON" => {
return Ok(crate::boxes::jit_stats_box::JitStatsBox::new().to_json());
}
// Return detailed per-function stats as JSON array
// Each item: { name, phi_total, phi_b1, ret_bool_hint, hits, compiled, handle }
"perFunction" | "per_function" => {
if let Some(jm) = &self.jit_manager {
let v = jm.per_function_stats();
let arr: Vec<serde_json::Value> = v.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"phi_total": phi_t,
"phi_b1": phi_b1,
"ret_bool_hint": rb,
"hits": hits,
"compiled": compiled,
"handle": handle
})
}).collect();
let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(crate::box_trait::StringBox::new(s)));
}
return Ok(Box::new(crate::box_trait::StringBox::new("[]")));
}
"top5" => {
if let Some(jm) = &self.jit_manager {
let v = jm.top_hits(5);
let arr: Vec<serde_json::Value> = v.into_iter().map(|(name, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"hits": hits,
"compiled": compiled,
"handle": handle
})
}).collect();
let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(crate::box_trait::StringBox::new(s)));
}
return Ok(Box::new(crate::box_trait::StringBox::new("[]")));
}
"summary" => {
let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" };
let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1 = crate::jit::rt::ret_bool_hint_get();
let mut payload = serde_json::json!({
"abi_mode": abi_mode,
"abi_b1_enabled": cfg.native_bool_abi,
"abi_b1_supported": caps.supports_b1_sig,
"b1_norm_count": b1_norm,
"ret_bool_hint_count": ret_b1,
"top5": []
});
if let Some(jm) = &self.jit_manager {
let v = jm.top_hits(5);
let top5: Vec<serde_json::Value> = v.into_iter().map(|(name, hits, compiled, handle)| serde_json::json!({
"name": name, "hits": hits, "compiled": compiled, "handle": handle
})).collect();
if let Some(obj) = payload.as_object_mut() { obj.insert("top5".to_string(), serde_json::Value::Array(top5)); }
}
let s = serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string());
return Ok(Box::new(crate::box_trait::StringBox::new(s)));
}
_ => return Ok(Box::new(crate::box_trait::VoidBox::new())),
}
}
// StringBox methods
if let Some(string_box) = box_value.as_any().downcast_ref::<StringBox>() {
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::<IntegerBox>(),
end_box.as_any().downcast_ref::<IntegerBox>()
) {
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::<crate::boxes::array::ArrayBox>() {
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::<crate::boxes::map_box::MapBox>() {
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())),
}
}
// MathBox methods (minimal set)
if let Some(math_box) = box_value.as_any().downcast_ref::<crate::boxes::math_box::MathBox>() {
// Coerce numeric-like StringBox to FloatBox for function-style lowering path
let mut coerce_num = |b: &Box<dyn NyashBox>| -> Box<dyn NyashBox> {
if let Some(sb) = b.as_any().downcast_ref::<StringBox>() {
let s = sb.value.trim();
if let Ok(f) = s.parse::<f64>() { return Box::new(crate::boxes::FloatBox::new(f)); }
if let Ok(i) = s.parse::<i64>() { return Box::new(IntegerBox::new(i)); }
}
b.clone_or_share()
};
match method {
"min" => {
if _args.len() >= 2 {
let a = coerce_num(&_args[0]);
let b = coerce_num(&_args[1]);
return Ok(math_box.min(a, b));
}
return Ok(Box::new(StringBox::new("Error: min(a,b) requires 2 args")));
}
"max" => {
if _args.len() >= 2 {
let a = coerce_num(&_args[0]);
let b = coerce_num(&_args[1]);
return Ok(math_box.max(a, b));
}
return Ok(Box::new(StringBox::new("Error: max(a,b) requires 2 args")));
}
"abs" => {
if let Some(v) = _args.get(0) { return Ok(math_box.abs(coerce_num(v))); }
return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg")));
}
"sin" => {
if let Some(v) = _args.get(0) { return Ok(math_box.sin(coerce_num(v))); }
return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg")));
}
"cos" => {
if let Some(v) = _args.get(0) { return Ok(math_box.cos(coerce_num(v))); }
return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg")));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// SocketBox methods (minimal set + timeout variants)
if let Some(sock) = box_value.as_any().downcast_ref::<crate::boxes::socket_box::SocketBox>() {
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::<IntegerBox>() {
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::<BoolBox>() {
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::<crate::boxes::array::ArrayBox>() {
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::<IntegerBox>() {
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::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
// 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))));
}
// Name-based fallback: delegate to unified PluginHost to invoke by (box_type, method)
// This preserves existing semantics but actually performs the plugin call instead of no-op.
let host = crate::runtime::get_global_plugin_host();
if let Ok(h) = host.read() {
// Prepare args as NyashBox list; they are already Box<dyn NyashBox>
let nyash_args: Vec<Box<dyn NyashBox>> = _args.into_iter().map(|b| b).collect();
match h.invoke_instance_method(&plugin_box.box_type, method, plugin_box.inner.instance_id, &nyash_args) {
Ok(Some(ret)) => return Ok(ret),
Ok(None) => return Ok(Box::new(VoidBox::new())),
Err(e) => {
eprintln!("[VM] Plugin invoke error: {}.{} -> {}", plugin_box.box_type, method, e.message());
return Ok(Box::new(VoidBox::new()));
}
}
}
return Ok(Box::new(VoidBox::new()));
}
*/
}
/// RAII guard for GC root regions
// Root region guard removed in favor of explicit enter/leave to avoid borrow conflicts
pub(super) use crate::backend::vm_control_flow::ControlFlow;
impl Default for VM {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::box_factory::user_defined::UserDefinedBoxFactory;
use crate::core::model::BoxDeclaration as CoreBoxDecl;
use crate::interpreter::SharedState;
use crate::mir::{
BasicBlock, BinaryOp, EffectMask, FunctionSignature, MirFunction, MirModule, MirType,
};
use crate::parser::NyashParser;
use crate::runtime::NyashRuntime;
use std::collections::HashMap;
use std::sync::Arc;
#[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]
#[ignore = "MIR13 migration: user box + string add semantics pending"]
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]
#[ignore = "MIR13 migration: local var then method semantics pending"]
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]
#[ignore = "MIR13 migration: console.log lowering/VM return value pending"]
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");
}
#[test]
#[ignore = "MIR13 migration: vtable cache expectations pending"]
fn test_vm_user_box_vtable_caching_two_calls() {
// Defines a simple user box and calls the same method twice; ensures correctness.
// Builder reserves slots for user methods (from 4), so method_id should be present,
// enabling vtable cache population on first call and (conceptually) direct call on second.
let code = r#"
box Greeter {
init { name }
birth(n) { me.name = n }
greet() { return "Hi, " + me.name }
}
local g
g = new Greeter("Bob")
g.greet()
g.greet()
"#;
// Parse to AST
let ast = crate::parser::NyashParser::parse_from_string(code).expect("parse failed");
// Prepare runtime with user-defined declarations and factory
let runtime = {
let rt = crate::runtime::NyashRuntime::new();
// Collect box declarations into runtime so that user-defined factory works
fn collect_box_decls(
ast: &crate::ast::ASTNode,
runtime: &crate::runtime::NyashRuntime,
) {
use crate::core::model::BoxDeclaration as CoreBoxDecl;
fn walk(node: &crate::ast::ASTNode, runtime: &crate::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);
}
collect_box_decls(&ast, &rt);
// Register user-defined factory
let mut shared = crate::interpreter::SharedState::new();
shared.box_declarations = rt.box_declarations.clone();
let udf = std::sync::Arc::new(
crate::box_factory::user_defined::UserDefinedBoxFactory::new(shared),
);
if let Ok(mut reg) = rt.box_registry.lock() {
reg.register(udf);
}
rt
};
// Compile and execute
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, "Hi, Bob");
}
#[test]
#[ignore = "MIR13 migration: type()/equals() fast-path alignment pending"]
fn test_vm_fastpath_universal_type_and_equals() {
// toString/type/equals/cloneのうち、typeとequalsのfast-pathを検証
// 1) type: (new ArrayBox()).type() == "ArrayBox"
let code_type = r#"
return (new ArrayBox()).type()
"#;
let ast = NyashParser::parse_from_string(code_type).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, "ArrayBox");
// 2) equals: (new IntegerBox(5)).equals(5) == true
let code_eq = r#"
return (new IntegerBox(5)).equals(5)
"#;
let ast = NyashParser::parse_from_string(code_eq).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, "true");
}
}

View File

@ -1,767 +0,0 @@
/*!
* VM BoxCall Dispatch
*
* Purpose: Method calls on Box values (dispatch by concrete box type)
* Responsibilities: call_box_method_impl, BoxCall debug logging
* Key APIs: call_box_method_impl, debug_log_boxcall
* Typical Callers: vm_instructions::execute_boxcall / VM::call_unified_method
*/
use super::vm::{VMError, VMValue, VM};
use crate::box_trait::{BoolBox, IntegerBox, NyashBox, StringBox, VoidBox};
impl VM {
/// Call a method on a Box - simplified version of interpreter method dispatch
pub(super) fn call_box_method_impl(
&self,
box_value: Box<dyn NyashBox>,
method: &str,
_args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, VMError> {
// PluginBoxV2: delegate to unified plugin host (BID-FFI v1)
if let Some(pbox) = box_value
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!(
"[VM][BoxCall→PluginInvoke] {}.{} inst_id={}",
pbox.box_type, method, pbox.inner.instance_id
);
}
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
match host.invoke_instance_method(
&pbox.box_type,
method,
pbox.inner.instance_id,
&_args,
) {
Ok(Some(val)) => {
return Ok(val);
}
Ok(None) => {
return Ok(Box::new(crate::box_trait::VoidBox::new()));
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"PluginInvoke failed via BoxCall: {}.{} ({})",
pbox.box_type,
method,
e.message()
)));
}
}
}
// MathBox methods (minimal set used in 10.9)
if let Some(math) = box_value
.as_any()
.downcast_ref::<crate::boxes::math_box::MathBox>()
{
match method {
"min" => {
if _args.len() >= 2 {
return Ok(math.min(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: min(a, b) requires 2 args")));
}
"max" => {
if _args.len() >= 2 {
return Ok(math.max(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: max(a, b) requires 2 args")));
}
"abs" => {
if let Some(v) = _args.get(0) {
return Ok(math.abs(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: abs(x) requires 1 arg")));
}
"sin" => {
if let Some(v) = _args.get(0) {
return Ok(math.sin(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: sin(x) requires 1 arg")));
}
"cos" => {
if let Some(v) = _args.get(0) {
return Ok(math.cos(v.clone_or_share()));
}
return Ok(Box::new(StringBox::new("Error: cos(x) requires 1 arg")));
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// ResultBox (NyashResultBox - new)
if let Some(result_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
match method {
"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())),
}
}
// Legacy box_trait::ResultBox path removed (migration complete)
// Generic fallback: toString for any Box type
if method == "toString" {
return Ok(Box::new(StringBox::new(box_value.to_string_box().value)));
}
// JitConfigBox methods
if let Some(jcb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_config_box::JitConfigBox>()
{
match method {
"get" => {
if let Some(k) = _args.get(0) {
return Ok(jcb
.get_flag(&k.to_string_box().value)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("get(name) requires 1 arg")));
}
"set" => {
if _args.len() >= 2 {
let k = _args[0].to_string_box().value;
let v = _args[1].to_string_box().value;
let on = matches!(v.as_str(), "1" | "true" | "True" | "on" | "ON");
return Ok(jcb
.set_flag(&k, on)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args")));
}
"getThreshold" => {
return Ok(jcb.get_threshold());
}
"setThreshold" => {
if let Some(v) = _args.get(0) {
let iv = v.to_string_box().value.parse::<i64>().unwrap_or(0);
return Ok(jcb
.set_threshold(iv)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("setThreshold(n) requires 1 arg")));
}
"apply" => {
return Ok(jcb.apply());
}
"toJson" => {
return Ok(jcb.to_json());
}
"fromJson" => {
if let Some(s) = _args.get(0) {
return Ok(jcb
.from_json(&s.to_string_box().value)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("fromJson(json) requires 1 arg")));
}
"enable" => {
if let Some(k) = _args.get(0) {
return Ok(jcb
.set_flag(&k.to_string_box().value, true)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("enable(name) requires 1 arg")));
}
"disable" => {
if let Some(k) = _args.get(0) {
return Ok(jcb
.set_flag(&k.to_string_box().value, false)
.unwrap_or_else(|e| Box::new(StringBox::new(e.to_string()))));
}
return Ok(Box::new(StringBox::new("disable(name) requires 1 arg")));
}
"summary" => {
return Ok(jcb.summary());
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// JitPolicyBox methods
if let Some(jpb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_policy_box::JitPolicyBox>()
{
match method {
"get" => {
if let Some(k) = _args.get(0) {
return Ok(jpb.get_flag(&k.to_string_box().value));
}
return Ok(Box::new(StringBox::new("get(name) requires 1 arg")));
}
"set" => {
if _args.len() >= 2 {
let k = _args[0].to_string_box().value;
let v = _args[1].to_string_box().value;
let on = matches!(v.as_str(), "1" | "true" | "True" | "on" | "ON");
return Ok(jpb.set_flag(&k, on));
}
return Ok(Box::new(StringBox::new("set(name, bool) requires 2 args")));
}
"setWhitelistCsv" | "set_whitelist_csv" => {
if let Some(s) = _args.get(0) {
return Ok(jpb.set_whitelist_csv(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"setWhitelistCsv(csv) requires 1 arg",
)));
}
"addWhitelist" | "add_whitelist" => {
if let Some(s) = _args.get(0) {
return Ok(jpb.add_whitelist(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"addWhitelist(name) requires 1 arg",
)));
}
"clearWhitelist" | "clear_whitelist" => {
return Ok(jpb.clear_whitelist());
}
"enablePreset" | "enable_preset" => {
if let Some(s) = _args.get(0) {
return Ok(jpb.enable_preset(&s.to_string_box().value));
}
return Ok(Box::new(StringBox::new(
"enablePreset(name) requires 1 arg",
)));
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// JitStatsBox methods
if let Some(jsb) = box_value
.as_any()
.downcast_ref::<crate::boxes::jit_stats_box::JitStatsBox>()
{
match method {
"toJson" => {
return Ok(jsb.to_json());
}
"top5" => {
if let Some(jm) = &self.jit_manager {
let v = jm.top_hits(5);
let arr: Vec<serde_json::Value> = v
.into_iter()
.map(|(name, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"hits": hits,
"compiled": compiled,
"handle": handle
})
})
.collect();
let s = serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(StringBox::new(s)));
}
return Ok(Box::new(StringBox::new("[]")));
}
"summary" => {
let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1 = crate::jit::rt::ret_bool_hint_get();
let mut payload = serde_json::json!({
"abi_mode": abi_mode,
"abi_b1_enabled": cfg.native_bool_abi,
"abi_b1_supported": caps.supports_b1_sig,
"b1_norm_count": b1_norm,
"ret_bool_hint_count": ret_b1,
"top5": [],
"perFunction": []
});
if let Some(jm) = &self.jit_manager {
let v = jm.top_hits(5);
let top5: Vec<serde_json::Value> = v.into_iter().map(|(name, hits, compiled, handle)| serde_json::json!({
"name": name, "hits": hits, "compiled": compiled, "handle": handle
})).collect();
let perf = jm.per_function_stats();
let per_arr: Vec<serde_json::Value> = perf.into_iter().map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| serde_json::json!({
"name": name, "phi_total": phi_t, "phi_b1": phi_b1, "ret_bool_hint": rb, "hits": hits, "compiled": compiled, "handle": handle
})).collect();
if let Some(obj) = payload.as_object_mut() {
obj.insert("top5".to_string(), serde_json::Value::Array(top5));
obj.insert(
"perFunction".to_string(),
serde_json::Value::Array(per_arr),
);
}
}
let s =
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| "{}".to_string());
return Ok(Box::new(StringBox::new(s)));
}
"perFunction" | "per_function" => {
if let Some(jm) = &self.jit_manager {
let v = jm.per_function_stats();
let arr: Vec<serde_json::Value> = v
.into_iter()
.map(|(name, phi_t, phi_b1, rb, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"phi_total": phi_t,
"phi_b1": phi_b1,
"ret_bool_hint": rb,
"hits": hits,
"compiled": compiled,
"handle": handle,
})
})
.collect();
let s =
serde_json::to_string_pretty(&arr).unwrap_or_else(|_| "[]".to_string());
return Ok(Box::new(StringBox::new(s)));
}
return Ok(Box::new(StringBox::new("[]")));
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
// StringBox methods
if let Some(string_box) = box_value.as_any().downcast_ref::<StringBox>() {
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" => {
if _args.len() >= 2 {
let s = match _args[0].to_string_box().value.parse::<i64>() {
Ok(v) => v.max(0) as usize,
Err(_) => 0,
};
let e = match _args[1].to_string_box().value.parse::<i64>() {
Ok(v) => v.max(0) as usize,
Err(_) => string_box.value.chars().count(),
};
return Ok(string_box.substring(s, e));
}
return Ok(Box::new(VoidBox::new()));
}
"concat" => {
if let Some(arg0) = _args.get(0) {
let out = format!("{}{}", string_box.value, arg0.to_string_box().value);
return Ok(Box::new(StringBox::new(out)));
}
return Ok(Box::new(VoidBox::new()));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// ArrayBox methods (minimal set)
if let Some(array_box) = box_value
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
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::<crate::boxes::map_box::MapBox>()
{
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())),
}
}
// TaskGroupBox methods (scaffold → instance内の所有Futureに対して実行)
if box_value
.as_any()
.downcast_ref::<crate::boxes::task_group_box::TaskGroupBox>()
.is_some()
{
let mut owned = box_value;
if let Some(tg) = (&mut *owned)
.as_any_mut()
.downcast_mut::<crate::boxes::task_group_box::TaskGroupBox>()
{
match method {
"cancelAll" | "cancel_all" => {
return Ok(tg.cancelAll());
}
"joinAll" | "join_all" => {
let ms = _args
.get(0)
.map(|a| a.to_string_box().value.parse::<i64>().unwrap_or(2000));
return Ok(tg.joinAll(ms));
}
_ => {
return Ok(Box::new(VoidBox::new()));
}
}
}
return Ok(Box::new(VoidBox::new()));
}
// P2PBox methods (minimal)
if let Some(p2p) = box_value
.as_any()
.downcast_ref::<crate::boxes::p2p_box::P2PBox>()
{
match method {
"send" => {
if _args.len() >= 2 {
return Ok(p2p.send(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: send(to, intent) requires 2 args",
)));
}
"on" => {
if _args.len() >= 2 {
return Ok(p2p.on(_args[0].clone_or_share(), _args[1].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: on(intent, handler) requires 2 args",
)));
}
"onOnce" | "on_once" => {
if _args.len() >= 2 {
return Ok(
p2p.on_once(_args[0].clone_or_share(), _args[1].clone_or_share())
);
}
return Ok(Box::new(StringBox::new(
"Error: onOnce(intent, handler) requires 2 args",
)));
}
"off" => {
if _args.len() >= 1 {
return Ok(p2p.off(_args[0].clone_or_share()));
}
return Ok(Box::new(StringBox::new(
"Error: off(intent) requires 1 arg",
)));
}
"getLastFrom" => {
return Ok(p2p.get_last_from());
}
"getLastIntentName" => {
return Ok(p2p.get_last_intent_name());
}
"debug_nodes" | "debugNodes" => {
return Ok(p2p.debug_nodes());
}
"getNodeId" | "getId" => {
return Ok(p2p.get_node_id());
}
"getTransportType" | "transport" => {
return Ok(p2p.get_transport_type());
}
"isReachable" => {
if let Some(n) = _args.get(0) {
return Ok(p2p.is_reachable(n.clone_or_share()));
}
return Ok(Box::new(BoolBox::new(false)));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// SocketBox methods (minimal + timeouts)
if let Some(sock) = box_value
.as_any()
.downcast_ref::<crate::boxes::socket_box::SocketBox>()
{
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::<IntegerBox>() {
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())),
}
}
// BoolBox methods
if let Some(bool_box) = box_value.as_any().downcast_ref::<BoolBox>() {
match method {
"toString" => {
return Ok(Box::new(StringBox::new(bool_box.value.to_string())));
}
_ => return Ok(Box::new(VoidBox::new())),
}
}
// Default: return void for any unrecognized box type or method
Ok(Box::new(VoidBox::new()))
}
/// Debug helper for BoxCall tracing (enabled via NYASH_VM_DEBUG_BOXCALL=1)
pub(super) fn debug_log_boxcall(
&self,
recv: &VMValue,
method: &str,
args: &[Box<dyn NyashBox>],
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<String> = 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
);
}
}
}
}

View File

@ -1,9 +0,0 @@
use crate::backend::vm::VMValue;
use crate::mir::BasicBlockId;
/// Control flow result from instruction execution
pub(crate) enum ControlFlow {
Continue,
Jump(BasicBlockId),
Return(VMValue),
}

View File

@ -1,356 +0,0 @@
/*!
* VM Execution Loop (extracted from vm.rs)
*
* Contains the high-level execution entrypoints and per-instruction dispatch glue:
* - VM::execute_module
* - VM::execute_function
* - VM::call_function_by_name
* - VM::execute_instruction (delegates to backend::dispatch)
* - VM::print_cache_stats_summary (stats helper)
*
* Behavior and public APIs are preserved. This is a pure move/refactor.
*/
use super::{vm::VMError, vm::VMValue, vm::VM};
use crate::backend::vm_control_flow::ControlFlow;
use crate::box_trait::NyashBox;
use crate::mir::{BasicBlockId, MirFunction, MirInstruction, MirModule};
impl VM {
/// Execute a MIR module
pub fn execute_module(&mut self, module: &MirModule) -> Result<Box<dyn NyashBox>, VMError> {
self.module = Some(module.clone());
if std::env::var("NYASH_REG_DUMP").ok().as_deref() == Some("1") {
crate::runtime::type_meta::dump_registry();
}
self.instr_counter.clear();
self.exec_start = Some(std::time::Instant::now());
let main_function = module
.get_function("main")
.ok_or_else(|| VMError::InvalidInstruction("No main function found".to_string()))?;
let result = self.execute_function(main_function)?;
self.maybe_print_stats();
self.maybe_print_jit_unified_stats();
if crate::config::env::vm_pic_stats() {
self.print_cache_stats_summary();
}
if let Some(jm) = &self.jit_manager {
jm.print_summary();
}
{
let lvl = crate::config::env::gc_trace_level();
if lvl > 0 {
if let Some((sp, rd, wr)) = self.runtime.gc.snapshot_counters() {
eprintln!(
"[GC] counters: safepoints={} read_barriers={} write_barriers={}",
sp, rd, wr
);
}
let roots_total = self.scope_tracker.root_count_total();
let root_regions = self.scope_tracker.root_regions();
let field_slots: usize = self.object_fields.values().map(|m| m.len()).sum();
eprintln!(
"[GC] mock_mark: roots_total={} regions={} object_field_slots={}",
roots_total, root_regions, field_slots
);
if lvl >= 2 {
self.gc_print_roots_breakdown();
}
if lvl >= 3 {
self.gc_print_reachability_depth2();
}
}
}
Ok(result.to_nyash_box())
}
pub(super) fn print_cache_stats_summary(&self) {
let sites_poly = self.boxcall_poly_pic.len();
let entries_poly: usize = self.boxcall_poly_pic.values().map(|v| v.len()).sum();
let avg_entries = if sites_poly > 0 {
(entries_poly as f64) / (sites_poly as f64)
} else {
0.0
};
let sites_mono = self.boxcall_pic_funcname.len();
let hits_total: u64 = self.boxcall_pic_hits.values().map(|v| *v as u64).sum();
let vt_entries = self.boxcall_vtable_funcname.len();
eprintln!(
"[VM] PIC/VT summary: poly_sites={} avg_entries={:.2} mono_sites={} hits_total={} vt_entries={} | hits: vt={} poly={} mono={} generic={}",
sites_poly,
avg_entries,
sites_mono,
hits_total,
vt_entries,
self.boxcall_hits_vtable,
self.boxcall_hits_poly_pic,
self.boxcall_hits_mono_pic,
self.boxcall_hits_generic
);
let mut hits: Vec<(&String, &u32)> = self.boxcall_pic_hits.iter().collect();
hits.sort_by(|a, b| b.1.cmp(a.1));
for (i, (k, v)) in hits.into_iter().take(5).enumerate() {
eprintln!(" #{} {} hits={}", i + 1, k, v);
}
}
/// Call a MIR function by name with VMValue arguments
pub(super) fn call_function_by_name(
&mut self,
func_name: &str,
args: Vec<VMValue>,
) -> Result<VMValue, VMError> {
self.enter_root_region();
self.pin_roots(args.iter());
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))
})?;
let function = function_ref.clone();
let saved_values = std::mem::take(&mut self.values);
let saved_current_function = self.current_function.clone();
let saved_current_block = self.frame.current_block;
let saved_previous_block = self.previous_block;
let saved_pc = self.frame.pc;
let saved_last_result = self.frame.last_result;
for (i, param_id) in function.params.iter().enumerate() {
if let Some(arg) = args.get(i) {
self.set_value(*param_id, arg.clone());
}
}
if let Some(first) = function.params.get(0) {
if let Some((class_part, _rest)) = func_name.split_once('.') {
self.object_class.insert(*first, class_part.to_string());
self.object_internal.insert(*first);
}
}
let result = self.execute_function(&function);
self.values = saved_values;
self.current_function = saved_current_function;
self.frame.current_block = saved_current_block;
self.previous_block = saved_previous_block;
self.frame.pc = saved_pc;
self.frame.last_result = saved_last_result;
self.scope_tracker.leave_root_region();
result
}
/// Execute a single function
pub(super) fn execute_function(&mut self, function: &MirFunction) -> Result<VMValue, VMError> {
// unused: local downcasts not required here
use crate::runtime::global_hooks;
// use crate::instance_v2::InstanceBox; // not used in this path
use super::control_flow;
self.current_function = Some(function.signature.name.clone());
if let Some(jm) = &mut self.jit_manager {
if let Ok(s) = std::env::var("NYASH_JIT_THRESHOLD") {
if let Ok(t) = s.parse::<u32>() {
if t > 0 {
jm.set_threshold(t);
}
}
}
jm.record_entry(&function.signature.name);
let _ = jm.maybe_compile(&function.signature.name, function);
if jm.is_compiled(&function.signature.name)
&& std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1")
{
if let Some(h) = jm.handle_of(&function.signature.name) {
eprintln!(
"[JIT] dispatch would go to handle={} for {} (stub)",
h, function.signature.name
);
}
}
}
self.loop_executor.initialize();
self.scope_tracker.push_scope();
global_hooks::push_task_scope();
let args_vec: Vec<VMValue> = function
.params
.iter()
.filter_map(|pid| self.get_value(*pid).ok())
.collect();
if std::env::var("NYASH_JIT_EXEC").ok().as_deref() == Some("1") {
let jit_only = std::env::var("NYASH_JIT_ONLY").ok().as_deref() == Some("1");
self.enter_root_region();
self.pin_roots(args_vec.iter());
if let Some(compiled) = self
.jit_manager
.as_ref()
.map(|jm| jm.is_compiled(&function.signature.name))
{
if compiled {
crate::runtime::host_api::set_current_vm(self as *mut _);
let jit_val = if let Some(jm_mut) = self.jit_manager.as_mut() {
jm_mut.execute_compiled(
&function.signature.name,
&function.signature.return_type,
&args_vec,
)
} else {
None
};
crate::runtime::host_api::clear_current_vm();
if let Some(val) = jit_val {
self.leave_root_region();
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Ok(val);
} else if std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1")
|| std::env::var("NYASH_JIT_TRAP_LOG").ok().as_deref() == Some("1")
{
eprintln!(
"[JIT] fallback: VM path taken for {}",
function.signature.name
);
if jit_only {
self.leave_root_region();
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Err(VMError::InvalidInstruction(format!(
"JIT-only enabled and JIT trap occurred for {}",
function.signature.name
)));
}
}
} else if jit_only {
if let Some(jm_mut) = self.jit_manager.as_mut() {
let _ = jm_mut.maybe_compile(&function.signature.name, function);
}
if self
.jit_manager
.as_ref()
.map(|jm| jm.is_compiled(&function.signature.name))
.unwrap_or(false)
{
crate::runtime::host_api::set_current_vm(self as *mut _);
let jit_val = if let Some(jm_mut) = self.jit_manager.as_mut() {
jm_mut.execute_compiled(
&function.signature.name,
&function.signature.return_type,
&args_vec,
)
} else {
None
};
crate::runtime::host_api::clear_current_vm();
if let Some(val) = jit_val {
self.leave_root_region();
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Ok(val);
} else {
self.leave_root_region();
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Err(VMError::InvalidInstruction(format!(
"JIT-only mode: compiled execution failed for {}",
function.signature.name
)));
}
} else {
self.leave_root_region();
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Err(VMError::InvalidInstruction(format!(
"JIT-only mode: function {} not compiled",
function.signature.name
)));
}
}
}
}
let mut current_block = function.entry_block;
self.frame.current_block = Some(current_block);
self.frame.pc = 0;
let mut should_return: Option<VMValue> = None;
let mut next_block: Option<BasicBlockId> = None;
loop {
// Reset per-block control-flow decisions to avoid carrying over stale state
// from a previous block (which could cause infinite loops on if/return).
should_return = None;
next_block = None;
if let Some(block) = function.blocks.get(&current_block) {
for instruction in &block.instructions {
match self.execute_instruction(instruction)? {
ControlFlow::Continue => continue,
ControlFlow::Jump(target) => {
next_block = Some(target);
break;
}
ControlFlow::Return(value) => {
should_return = Some(value);
break;
}
}
}
// Execute terminator if present and we haven't decided control flow yet
if should_return.is_none() && next_block.is_none() {
if let Some(term) = &block.terminator {
match self.execute_instruction(term)? {
ControlFlow::Continue => {}
ControlFlow::Jump(target) => {
next_block = Some(target);
}
ControlFlow::Return(value) => {
should_return = Some(value);
}
}
}
}
} else {
return Err(VMError::InvalidBasicBlock(format!(
"Basic block {:?} not found",
current_block
)));
}
if let Some(return_value) = should_return {
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Ok(return_value);
} else if let Some(target) = next_block {
control_flow::record_transition(
&mut self.previous_block,
&mut self.loop_executor,
current_block,
target,
)
.ok();
current_block = target;
} else {
self.scope_tracker.pop_scope();
global_hooks::pop_task_scope();
return Ok(VMValue::Void);
}
}
}
/// Execute a single instruction
pub(super) fn execute_instruction(
&mut self,
instruction: &MirInstruction,
) -> Result<ControlFlow, VMError> {
let debug_global = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1");
let debug_exec =
debug_global || std::env::var("NYASH_VM_DEBUG_EXEC").ok().as_deref() == Some("1");
if debug_exec {
eprintln!("[VM] execute_instruction: {:?}", instruction);
}
self.record_instruction(instruction);
super::dispatch::execute_instruction(self, instruction, debug_global)
}
}

View File

@ -1,107 +0,0 @@
/*!
* VM GC Roots & Diagnostics (extracted from vm.rs)
*
* - Root region helpers: enter_root_region / pin_roots / leave_root_region
* - Site info for GC logs
* - Debug prints for roots snapshot and shallow reachability
*/
use super::vm::{VMValue, VM};
impl VM {
/// Enter a GC root region and return a guard that leaves on drop
pub(super) fn enter_root_region(&mut self) {
self.scope_tracker.enter_root_region();
}
/// Pin a slice of VMValue as roots in the current region
pub(super) fn pin_roots<'a>(&mut self, values: impl IntoIterator<Item = &'a VMValue>) {
for v in values {
self.scope_tracker.pin_root(v);
}
}
/// Leave current GC root region
pub(super) fn leave_root_region(&mut self) {
self.scope_tracker.leave_root_region();
}
/// Site info for GC logs: (func, bb, pc)
pub(super) fn gc_site_info(&self) -> (String, i64, i64) {
let func = self
.current_function
.as_deref()
.unwrap_or("<none>")
.to_string();
let bb = self.frame.current_block.map(|b| b.0 as i64).unwrap_or(-1);
let pc = self.frame.pc as i64;
(func, bb, pc)
}
/// Print a simple breakdown of root VMValue kinds and top BoxRef types
pub(super) fn gc_print_roots_breakdown(&self) {
use std::collections::HashMap;
let roots = self.scope_tracker.roots_snapshot();
let mut kinds: HashMap<&'static str, u64> = HashMap::new();
let mut box_types: HashMap<String, u64> = HashMap::new();
for v in &roots {
match v {
VMValue::Integer(_) => *kinds.entry("Integer").or_insert(0) += 1,
VMValue::Float(_) => *kinds.entry("Float").or_insert(0) += 1,
VMValue::Bool(_) => *kinds.entry("Bool").or_insert(0) += 1,
VMValue::String(_) => *kinds.entry("String").or_insert(0) += 1,
VMValue::Future(_) => *kinds.entry("Future").or_insert(0) += 1,
VMValue::Void => *kinds.entry("Void").or_insert(0) += 1,
VMValue::BoxRef(b) => {
let tn = b.type_name().to_string();
*box_types.entry(tn).or_insert(0) += 1;
}
}
}
eprintln!("[GC] roots_breakdown: kinds={:?}", kinds);
let mut top: Vec<(String, u64)> = box_types.into_iter().collect();
top.sort_by(|a, b| b.1.cmp(&a.1));
top.truncate(5);
eprintln!("[GC] roots_boxref_top5: {:?}", top);
}
/// Print shallow reachability from current roots into ArrayBox/MapBox values
pub(super) fn gc_print_reachability_depth2(&self) {
use std::collections::HashMap;
let roots = self.scope_tracker.roots_snapshot();
let mut child_types: HashMap<String, u64> = HashMap::new();
let mut child_count = 0u64;
for v in &roots {
if let VMValue::BoxRef(b) = v {
if let Some(arr) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
if let Ok(items) = arr.items.read() {
for item in items.iter() {
let tn = item.type_name().to_string();
*child_types.entry(tn).or_insert(0) += 1;
child_count += 1;
}
}
}
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
let vals = map.values();
if let Some(arr2) = vals
.as_any()
.downcast_ref::<crate::boxes::array::ArrayBox>()
{
if let Ok(items) = arr2.items.read() {
for item in items.iter() {
let tn = item.type_name().to_string();
*child_types.entry(tn).or_insert(0) += 1;
child_count += 1;
}
}
}
}
}
}
let mut top: Vec<(String, u64)> = child_types.into_iter().collect();
top.sort_by(|a, b| b.1.cmp(&a.1));
top.truncate(5);
eprintln!("[GC] depth2_children: total={} top5={:?}", child_count, top);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -1,66 +0,0 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
impl VM {
/// Execute Call instruction (supports function name or FunctionBox value)
pub(crate) fn execute_call(
&mut self,
dst: Option<ValueId>,
func: ValueId,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Evaluate function value
let fval = self.get_value(func)?;
match fval {
VMValue::String(func_name) => {
// Legacy: call function by name
let arg_values: Vec<VMValue> = args
.iter()
.map(|arg| self.get_value(*arg))
.collect::<Result<Vec<_>, _>>()?;
let result = self.call_function_by_name(&func_name, arg_values)?;
if let Some(dst_id) = dst {
self.set_value(dst_id, result);
}
Ok(ControlFlow::Continue)
}
VMValue::BoxRef(arc_box) => {
// FunctionBox call path
if let Some(fun) = arc_box
.as_any()
.downcast_ref::<crate::boxes::function_box::FunctionBox>()
{
// Convert args to NyashBox for interpreter helper
let nyash_args: Vec<Box<dyn NyashBox>> = args
.iter()
.map(|a| self.get_value(*a).map(|v| v.to_nyash_box()))
.collect::<Result<Vec<_>, VMError>>()?;
// Execute via interpreter helper
match crate::interpreter::run_function_box(fun, nyash_args) {
Ok(out) => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::from_nyash_box(out));
}
Ok(ControlFlow::Continue)
}
Err(e) => Err(VMError::InvalidInstruction(format!(
"FunctionBox call failed: {:?}",
e
))),
}
} else {
Err(VMError::TypeError(format!(
"Call target not callable: {}",
arc_box.type_name()
)))
}
}
other => Err(VMError::TypeError(format!(
"Call target must be function name or FunctionBox, got {:?}",
other
))),
}
}
}

View File

@ -1,652 +0,0 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::{BoolBox, VoidBox};
use crate::boxes::ArrayBox;
use crate::mir::{
BasicBlockId, BinaryOp, CompareOp, ConstValue, MirType, TypeOpKind, UnaryOp, ValueId,
};
use std::sync::Arc;
impl VM {
// ---- Helpers (PIC/VTable bookkeeping) ----
pub(super) fn build_pic_key(
&self,
recv: &VMValue,
method: &str,
method_id: Option<u16>,
) -> String {
let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label);
if let Some(mid) = method_id {
format!("v{}:{}#{}", ver, label, mid)
} else {
format!("v{}:{}#{}", ver, label, method)
}
}
pub(super) fn pic_record_hit(&mut self, key: &str) {
use std::collections::hash_map::Entry;
match self.boxcall_pic_hits.entry(key.to_string()) {
Entry::Occupied(mut e) => {
let v = e.get_mut();
*v = v.saturating_add(1);
if std::env::var("NYASH_VM_PIC_DEBUG").ok().as_deref() == Some("1") {
if *v == 8 || *v == 32 {
eprintln!("[PIC] Hot BoxCall site '{}' hits={} (skeleton)", key, v);
}
}
}
Entry::Vacant(v) => {
v.insert(1);
}
}
}
pub(super) fn pic_hits(&self, key: &str) -> u32 {
*self.boxcall_pic_hits.get(key).unwrap_or(&0)
}
pub(super) fn build_vtable_key(
&self,
class_name: &str,
method_id: u16,
arity: usize,
) -> String {
let label = format!("BoxRef:{}", class_name);
let ver = self.cache_version_for_label(&label);
format!(
"VT@v{}:{}#{}{}",
ver,
class_name,
method_id,
format!("/{}", arity)
)
}
pub(super) fn try_poly_pic(&mut self, pic_site_key: &str, recv: &VMValue) -> Option<String> {
let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label);
if let Some(entries) = self.boxcall_poly_pic.get_mut(pic_site_key) {
if let Some(idx) = entries
.iter()
.position(|(l, v, _)| *l == label && *v == ver)
{
let entry = entries.remove(idx);
entries.push(entry.clone());
return Some(entry.2);
}
}
None
}
pub(super) fn record_poly_pic(&mut self, pic_site_key: &str, recv: &VMValue, func_name: &str) {
let label = self.cache_label_for_recv(recv);
let ver = self.cache_version_for_label(&label);
use std::collections::hash_map::Entry;
match self.boxcall_poly_pic.entry(pic_site_key.to_string()) {
Entry::Occupied(mut e) => {
let v = e.get_mut();
if let Some(idx) = v.iter().position(|(l, vv, _)| *l == label && *vv == ver) {
v.remove(idx);
}
if v.len() >= 4 {
v.remove(0);
}
v.push((label.clone(), ver, func_name.to_string()));
}
Entry::Vacant(v) => {
v.insert(vec![(label.clone(), ver, func_name.to_string())]);
}
}
if crate::config::env::vm_pic_stats() {
if let Some(v) = self.boxcall_poly_pic.get(pic_site_key) {
eprintln!(
"[PIC] site={} size={} last=({}, v{}) -> {}",
pic_site_key,
v.len(),
label,
ver,
func_name
);
}
}
}
pub(super) fn cache_label_for_recv(&self, recv: &VMValue) -> String {
match recv {
VMValue::Integer(_) => "Int".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(),
VMValue::BoxRef(b) => format!("BoxRef:{}", b.type_name()),
}
}
pub(super) fn cache_version_for_label(&self, label: &str) -> u32 {
crate::runtime::cache_versions::get_version(label)
}
#[allow(dead_code)]
pub fn bump_cache_version(&mut self, label: &str) {
crate::runtime::cache_versions::bump_version(label)
}
// ---- Basics ----
pub(crate) fn execute_const(
&mut self,
dst: ValueId,
value: &ConstValue,
) -> Result<ControlFlow, VMError> {
let vm_value = VMValue::from(value);
self.set_value(dst, vm_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_binop(
&mut self,
dst: ValueId,
op: &BinaryOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<ControlFlow, VMError> {
match *op {
BinaryOp::And | BinaryOp::Or => {
if std::env::var("NYASH_VM_DEBUG_ANDOR").ok().as_deref() == Some("1") {
eprintln!("[VM] And/Or short-circuit path");
}
let left = self.get_value(lhs)?;
let right = self.get_value(rhs)?;
let lb = left.as_bool()?;
let rb = right.as_bool()?;
let out = match *op {
BinaryOp::And => lb && rb,
BinaryOp::Or => lb || rb,
_ => unreachable!(),
};
self.set_value(dst, VMValue::Bool(out));
Ok(ControlFlow::Continue)
}
_ => {
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)
}
}
}
pub(crate) fn execute_unaryop(
&mut self,
dst: ValueId,
op: &UnaryOp,
operand: ValueId,
) -> Result<ControlFlow, VMError> {
let operand_val = self.get_value(operand)?;
let result = self.execute_unary_op(op, &operand_val)?;
self.set_value(dst, result);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_compare(
&mut self,
dst: ValueId,
op: &CompareOp,
lhs: ValueId,
rhs: ValueId,
) -> Result<ControlFlow, VMError> {
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] execute_compare enter op={:?} lhs={:?} rhs={:?}",
op, lhs, rhs
);
}
let mut left = self.get_value(lhs)?;
let mut right = self.get_value(rhs)?;
if debug_cmp {
eprintln!(
"[VM] execute_compare values: left={:?} right={:?}",
left, right
);
}
left = match left {
VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
VMValue::Integer(ib.value)
} else {
match b.to_string_box().value.trim().parse::<i64>() {
Ok(n) => VMValue::Integer(n),
Err(_) => VMValue::BoxRef(b),
}
}
}
other => other,
};
right = match right {
VMValue::BoxRef(b) => {
if let Some(ib) = b.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
VMValue::Integer(ib.value)
} else {
match b.to_string_box().value.trim().parse::<i64>() {
Ok(n) => VMValue::Integer(n),
Err(_) => VMValue::BoxRef(b),
}
}
}
other => other,
};
let result = self.execute_compare_op(op, &left, &right)?;
self.set_value(dst, VMValue::Bool(result));
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_print(&self, value: ValueId) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
println!("{}", val.to_string());
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_jump(&self, target: BasicBlockId) -> Result<ControlFlow, VMError> {
Ok(ControlFlow::Jump(target))
}
pub(crate) fn execute_branch(
&self,
condition: ValueId,
then_bb: BasicBlockId,
else_bb: BasicBlockId,
) -> Result<ControlFlow, VMError> {
let cond_val = self.get_value(condition)?;
let should_branch = match &cond_val {
VMValue::Bool(b) => *b,
VMValue::Void => false,
VMValue::Integer(i) => *i != 0,
VMValue::BoxRef(b) => {
if let Some(bool_box) = b.as_any().downcast_ref::<BoolBox>() {
bool_box.value
} else if b.as_any().downcast_ref::<VoidBox>().is_some() {
false
} else {
return Err(VMError::TypeError(format!(
"Branch condition must be bool, void, or integer, got BoxRef({})",
b.type_name()
)));
}
}
_ => {
return Err(VMError::TypeError(format!(
"Branch condition must be bool, void, or integer, got {:?}",
cond_val
)))
}
};
Ok(ControlFlow::Jump(if should_branch {
then_bb
} else {
else_bb
}))
}
pub(crate) fn execute_return(&self, value: Option<ValueId>) -> Result<ControlFlow, VMError> {
if let Some(val_id) = value {
let return_val = self.get_value(val_id)?;
if crate::config::env::vm_vt_trace() {
eprintln!(
"[VT] Return id={} val={}",
val_id.to_usize(),
return_val.to_string()
);
}
Ok(ControlFlow::Return(return_val))
} else {
if crate::config::env::vm_vt_trace() {
eprintln!("[VT] Return void");
}
Ok(ControlFlow::Return(VMValue::Void))
}
}
pub(crate) fn execute_typeop(
&mut self,
dst: ValueId,
op: &TypeOpKind,
value: ValueId,
ty: &MirType,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
match op {
TypeOpKind::Check => {
let is_type = match (&val, ty) {
(VMValue::Integer(_), MirType::Integer) => true,
(VMValue::Float(_), MirType::Float) => true,
(VMValue::Bool(_), MirType::Bool) => true,
(VMValue::String(_), MirType::String) => true,
(VMValue::Void, MirType::Void) => true,
(VMValue::BoxRef(arc_box), MirType::Box(box_name)) => {
arc_box.type_name() == box_name
}
_ => false,
};
self.set_value(dst, VMValue::Bool(is_type));
Ok(ControlFlow::Continue)
}
TypeOpKind::Cast => {
let result = match (&val, ty) {
(VMValue::Integer(i), MirType::Float) => VMValue::Float(*i as f64),
(VMValue::Float(f), MirType::Integer) => VMValue::Integer(*f as i64),
(VMValue::Integer(_), MirType::Integer)
| (VMValue::Float(_), MirType::Float)
| (VMValue::Bool(_), MirType::Bool)
| (VMValue::String(_), MirType::String) => val.clone(),
(VMValue::BoxRef(arc_box), MirType::Box(box_name))
if arc_box.type_name() == box_name =>
{
val.clone()
}
_ => {
return Err(VMError::TypeError(format!(
"Cannot cast {:?} to {:?}",
val, ty
)));
}
};
self.set_value(dst, result);
Ok(ControlFlow::Continue)
}
}
}
pub(crate) fn execute_phi(
&mut self,
dst: ValueId,
inputs: &[(BasicBlockId, ValueId)],
) -> Result<ControlFlow, VMError> {
let selected = self.loop_execute_phi(dst, inputs)?;
self.set_value(dst, selected);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_load(
&mut self,
dst: ValueId,
ptr: ValueId,
) -> Result<ControlFlow, VMError> {
let loaded_value = self.get_value(ptr)?;
self.set_value(dst, loaded_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_store(
&mut self,
value: ValueId,
ptr: ValueId,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
self.set_value(ptr, val);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_copy(
&mut self,
dst: ValueId,
src: ValueId,
) -> Result<ControlFlow, VMError> {
let value = self.get_value(src)?;
let cloned = match &value {
VMValue::BoxRef(arc_box) => {
let cloned_box = arc_box.clone_or_share();
VMValue::BoxRef(Arc::from(cloned_box))
}
other => other.clone(),
};
self.set_value(dst, cloned);
Ok(ControlFlow::Continue)
}
// ---- Arrays ----
pub(crate) fn execute_array_get(
&mut self,
dst: ValueId,
array: ValueId,
index: ValueId,
) -> Result<ControlFlow, VMError> {
let array_val = self.get_value(array)?;
let index_val = self.get_value(index)?;
if let VMValue::BoxRef(array_box) = &array_val {
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
let index_box = index_val.to_nyash_box();
let result = array.get(index_box);
self.set_value(dst, VMValue::BoxRef(Arc::from(result)));
Ok(ControlFlow::Continue)
} else {
Err(VMError::TypeError(
"ArrayGet requires an ArrayBox".to_string(),
))
}
} else {
Err(VMError::TypeError(
"ArrayGet requires array and integer index".to_string(),
))
}
}
pub(crate) fn execute_array_set(
&mut self,
array: ValueId,
index: ValueId,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let array_val = self.get_value(array)?;
let index_val = self.get_value(index)?;
let value_val = self.get_value(value)?;
if let VMValue::BoxRef(array_box) = &array_val {
if let Some(array) = array_box.as_any().downcast_ref::<ArrayBox>() {
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "ArraySet");
let index_box = index_val.to_nyash_box();
let box_value = value_val.to_nyash_box();
array.set(index_box, box_value);
Ok(ControlFlow::Continue)
} else {
Err(VMError::TypeError(
"ArraySet requires an ArrayBox".to_string(),
))
}
} else {
Err(VMError::TypeError(
"ArraySet requires array and integer index".to_string(),
))
}
}
// ---- Refs/Weak/Barriers ----
pub(crate) fn execute_ref_new(
&mut self,
dst: ValueId,
box_val: ValueId,
) -> Result<ControlFlow, VMError> {
let box_value = self.get_value(box_val)?;
self.set_value(dst, box_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_ref_get(
&mut self,
dst: ValueId,
reference: ValueId,
field: &str,
) -> Result<ControlFlow, VMError> {
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
if debug_ref {
eprintln!("[VM] RefGet ref={:?} field={}", reference, field);
}
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.iter().any(|f| f == field) {
return Err(VMError::TypeError(format!(
"Field '{}' is private in {}",
field, class_name
)));
}
}
}
}
}
let mut field_value = if let Some(fields) = self.object_fields.get(&reference) {
if let Some(value) = fields.get(field) {
if debug_ref {
eprintln!("[VM] RefGet hit: {} -> {:?}", field, value);
}
value.clone()
} else {
if debug_ref {
eprintln!("[VM] RefGet miss: {} -> default 0", field);
}
VMValue::Integer(0)
}
} else {
if debug_ref {
eprintln!("[VM] RefGet no fields: -> default 0");
}
VMValue::Integer(0)
};
if matches!(field_value, VMValue::Integer(0)) && field == "console" {
if debug_ref {
eprintln!("[VM] RefGet special binding: console -> Plugin ConsoleBox");
}
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(pbox) = host.create_box("ConsoleBox", &[]) {
field_value = VMValue::from_nyash_box(pbox);
if !self.object_fields.contains_key(&reference) {
self.object_fields
.insert(reference, std::collections::HashMap::new());
}
if let Some(fields) = self.object_fields.get_mut(&reference) {
fields.insert(field.to_string(), field_value.clone());
}
}
}
self.set_value(dst, field_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_ref_set(
&mut self,
reference: ValueId,
field: &str,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let debug_ref = std::env::var("NYASH_VM_DEBUG_REF").ok().as_deref() == Some("1");
let new_value = self.get_value(value)?;
if debug_ref {
eprintln!(
"[VM] RefSet ref={:?} field={} value={:?}",
reference, field, new_value
);
}
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.iter().any(|f| f == field) {
return Err(VMError::TypeError(format!(
"Field '{}' is private in {}",
field, class_name
)));
}
}
}
}
}
if !self.object_fields.contains_key(&reference) {
self.object_fields
.insert(reference, std::collections::HashMap::new());
}
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "RefSet");
if let Some(fields) = self.object_fields.get_mut(&reference) {
fields.insert(field.to_string(), new_value);
if debug_ref {
eprintln!("[VM] RefSet stored: {}", field);
}
}
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_weak_new(
&mut self,
dst: ValueId,
box_val: ValueId,
) -> Result<ControlFlow, VMError> {
let box_value = self.get_value(box_val)?;
self.set_value(dst, box_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_weak_load(
&mut self,
dst: ValueId,
weak_ref: ValueId,
) -> Result<ControlFlow, VMError> {
let weak_value = self.get_value(weak_ref)?;
self.set_value(dst, weak_value);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_barrier_read(
&mut self,
dst: ValueId,
value: ValueId,
) -> Result<ControlFlow, VMError> {
let val = self.get_value(value)?;
self.set_value(dst, val);
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_barrier_write(
&mut self,
_value: ValueId,
) -> Result<ControlFlow, VMError> {
Ok(ControlFlow::Continue)
}
pub(crate) fn execute_throw(&mut self, exception: ValueId) -> Result<ControlFlow, VMError> {
let exc_value = self.get_value(exception)?;
Err(VMError::InvalidInstruction(format!(
"Exception thrown: {:?}",
exc_value
)))
}
pub(crate) fn execute_catch(
&mut self,
exception_value: ValueId,
) -> Result<ControlFlow, VMError> {
self.set_value(exception_value, VMValue::Void);
Ok(ControlFlow::Continue)
}
// ---- Futures ----
pub(crate) fn execute_await(
&mut self,
dst: ValueId,
future: ValueId,
) -> Result<ControlFlow, VMError> {
let future_val = self.get_value(future)?;
if let VMValue::Future(ref future_box) = future_val {
let max_ms: u64 = crate::config::env::await_max_ms();
let start = std::time::Instant::now();
let mut spins = 0usize;
while !future_box.ready() {
self.runtime.gc.safepoint();
if let Some(s) = &self.runtime.scheduler {
s.poll();
}
std::thread::yield_now();
spins += 1;
if spins % 1024 == 0 {
std::thread::sleep(std::time::Duration::from_millis(1));
}
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
let err = Box::new(crate::box_trait::StringBox::new("Timeout"));
let rb = crate::boxes::result::NyashResultBox::new_err(err);
let vm_value = VMValue::from_nyash_box(Box::new(rb));
self.set_value(dst, vm_value);
return Ok(ControlFlow::Continue);
}
}
let result = future_box.get();
let ok = crate::boxes::result::NyashResultBox::new_ok(result);
let vm_value = VMValue::from_nyash_box(Box::new(ok));
self.set_value(dst, vm_value);
Ok(ControlFlow::Continue)
} else {
Err(VMError::TypeError(format!(
"Expected Future, got {:?}",
future_val
)))
}
}
}

View File

@ -1,431 +0,0 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
impl VM {
/// Execute ExternCall instruction
pub(crate) fn execute_extern_call(
&mut self,
dst: Option<ValueId>,
iface_name: &str,
method_name: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Core-13 pure shims: env.local.{get,set}, env.box.new
match (iface_name, method_name) {
("env.local", "get") => {
if args.len() != 1 {
return Err(VMError::InvalidInstruction("env.local.get arity".into()));
}
let ptr = args[0];
let v = self
.get_value(ptr)
.unwrap_or(crate::backend::vm::VMValue::Void);
if let Some(d) = dst {
self.set_value(d, v);
}
return Ok(ControlFlow::Continue);
}
("env.local", "set") => {
if args.len() != 2 {
return Err(VMError::InvalidInstruction("env.local.set arity".into()));
}
let ptr = args[0];
let val = self.get_value(args[1])?;
self.set_value(ptr, val);
if let Some(d) = dst {
self.set_value(d, crate::backend::vm::VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.box", "new") => {
if args.is_empty() {
return Err(VMError::InvalidInstruction(
"env.box.new requires type name".into(),
));
}
// first arg must be Const String type name
let ty = self.get_value(args[0])?;
let ty_name = match ty {
crate::backend::vm::VMValue::String(s) => s,
_ => {
return Err(VMError::InvalidInstruction(
"env.box.new first arg must be string".into(),
))
}
};
// remaining args as NyashBox
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for id in args.iter().skip(1) {
let v = self.get_value(*id)?;
ny_args.push(v.to_nyash_box());
}
let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) {
Ok(b) => {
if let Some(d) = dst {
self.set_value(d, crate::backend::vm::VMValue::from_nyash_box(b));
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"env.box.new failed for {}: {}",
ty_name, e
)));
}
}
return Ok(ControlFlow::Continue);
}
_ => {}
}
// Optional routing to name→slot handlers for stability and diagnostics
if crate::config::env::extern_route_slots() {
if let Some(slot) =
crate::runtime::extern_registry::resolve_slot(iface_name, method_name)
{
// Decode args to VMValue as needed by handlers below
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
match (iface_name, method_name, slot) {
("env.local", "get", 40) => {
if let Some(d) = dst {
if let Some(a0) = args.get(0) {
let v = self.get_value(*a0).unwrap_or(VMValue::Void);
self.set_value(d, v);
}
}
return Ok(ControlFlow::Continue);
}
("env.local", "set", 41) => {
if args.len() >= 2 {
let ptr = args[0];
let val = vm_args.get(1).cloned().unwrap_or(VMValue::Void);
self.set_value(ptr, val);
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.box", "new", 50) => {
if vm_args.is_empty() {
return Err(VMError::InvalidInstruction(
"env.box.new requires type".into(),
));
}
let ty = &vm_args[0];
let ty_name = match ty {
VMValue::String(s) => s.clone(),
_ => {
return Err(VMError::InvalidInstruction(
"env.box.new first arg must be string".into(),
))
}
};
let mut ny_args: Vec<Box<dyn NyashBox>> = Vec::new();
for v in vm_args.iter().skip(1) {
ny_args.push(v.to_nyash_box());
}
let reg = crate::runtime::box_registry::get_global_registry();
match reg.create_box(&ty_name, &ny_args) {
Ok(b) => {
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(b));
}
}
Err(e) => {
return Err(VMError::InvalidInstruction(format!(
"env.box.new failed for {}: {}",
ty_name, e
)));
}
}
return Ok(ControlFlow::Continue);
}
("env.console", m @ ("log" | "warn" | "error" | "println"), 10) => {
if let Some(a0) = vm_args.get(0) {
match m {
"warn" => eprintln!("[warn] {}", a0.to_string()),
"error" => eprintln!("[error] {}", a0.to_string()),
_ => println!("{}", a0.to_string()),
}
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.debug", "trace", 11) => {
if let Some(a0) = vm_args.get(0) {
eprintln!("[trace] {}", a0.to_string());
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.runtime", "checkpoint", 12) => {
if crate::config::env::runtime_checkpoint_trace() {
let (func, bb, pc) = self.gc_site_info();
eprintln!("[rt] checkpoint @{} bb={} pc={}", func, bb, pc);
}
self.runtime.gc.safepoint();
if let Some(s) = &self.runtime.scheduler {
s.poll();
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.future", "new", 20) | ("env.future", "birth", 20) => {
// Create a new Future and optionally set initial value from arg0
let fut = crate::boxes::future::FutureBox::new();
if let Some(a0) = vm_args.get(0) {
fut.set_result(a0.to_nyash_box());
}
if let Some(d) = dst {
self.set_value(d, VMValue::Future(fut));
}
return Ok(ControlFlow::Continue);
}
("env.future", "set", 21) => {
// set(future, value)
if vm_args.len() >= 2 {
if let VMValue::Future(f) = &vm_args[0] {
f.set_result(vm_args[1].to_nyash_box());
}
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.future", "await", 22) => {
if let Some(VMValue::Future(fb)) = vm_args.get(0) {
// Simple blocking await
let start = std::time::Instant::now();
let max_ms = crate::config::env::await_max_ms();
while !fb.ready() {
std::thread::yield_now();
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
break;
}
}
let result = if fb.ready() {
fb.get()
} else {
Box::new(crate::box_trait::StringBox::new("Timeout"))
};
let ok = crate::boxes::result::NyashResultBox::new_ok(result);
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(Box::new(ok)));
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "cancelCurrent", 30) => {
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "currentToken", 31) => {
if let Some(d) = dst {
self.set_value(d, VMValue::Integer(0));
}
return Ok(ControlFlow::Continue);
}
("env.task", "yieldNow", 32) => {
std::thread::yield_now();
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.task", "sleepMs", 33) => {
let ms = vm_args
.get(0)
.map(|v| match v {
VMValue::Integer(i) => *i,
_ => 0,
})
.unwrap_or(0);
if ms > 0 {
std::thread::sleep(std::time::Duration::from_millis(ms as u64));
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
_ => { /* fallthrough to host */ }
}
}
// Name-route minimal registry without slot
// env.modules.set(key:any->string, value:any) ; env.modules.get(key) -> any
match (iface_name, method_name) {
("env.modules", "set") => {
// Expect two args
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
if vm_args.len() >= 2 {
let key = vm_args[0].to_string();
let val_box = vm_args[1].to_nyash_box();
crate::runtime::modules_registry::set(key, val_box);
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
("env.modules", "get") => {
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
if let Some(k) = vm_args.get(0) {
let key = k.to_string();
if let Some(v) = crate::runtime::modules_registry::get(&key) {
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(v));
} else { /* no dst */
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
_ => {}
}
}
// Name-route minimal registry even when slot routing is disabled
if iface_name == "env.modules" {
// Decode args as VMValue for convenience
let vm_args: Vec<VMValue> = args
.iter()
.filter_map(|a| self.get_value(*a).ok())
.collect();
match method_name {
"set" => {
if vm_args.len() >= 2 {
let key = vm_args[0].to_string();
let val_box = vm_args[1].to_nyash_box();
crate::runtime::modules_registry::set(key, val_box);
}
if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
"get" => {
if let Some(k) = vm_args.get(0) {
let key = k.to_string();
if let Some(v) = crate::runtime::modules_registry::get(&key) {
if let Some(d) = dst {
self.set_value(d, VMValue::from_nyash_box(v));
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
} else if let Some(d) = dst {
self.set_value(d, VMValue::Void);
}
return Ok(ControlFlow::Continue);
}
_ => {}
}
}
// Evaluate arguments as NyashBox for loader
let mut nyash_args: Vec<Box<dyn NyashBox>> = Vec::new();
for arg_id in args {
let arg_value = self.get_value(*arg_id)?;
nyash_args.push(arg_value.to_nyash_box());
}
if crate::config::env::extern_trace() {
if let Some(slot) =
crate::runtime::extern_registry::resolve_slot(iface_name, method_name)
{
eprintln!(
"[EXT] call {}.{} slot={} argc={}",
iface_name,
method_name,
slot,
nyash_args.len()
);
} else {
eprintln!(
"[EXT] call {}.{} argc={}",
iface_name,
method_name,
nyash_args.len()
);
}
}
let host = crate::runtime::get_global_plugin_host();
let host = host
.read()
.map_err(|_| VMError::InvalidInstruction("Plugin host lock poisoned".into()))?;
match host.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(_) => {
let strict =
crate::config::env::extern_strict() || crate::config::env::abi_strict();
let mut msg = String::new();
if strict {
msg.push_str("ExternCall STRICT: unregistered or unsupported call ");
} else {
msg.push_str("ExternCall failed: ");
}
msg.push_str(&format!("{}.{}", iface_name, method_name));
if let Err(detail) = crate::runtime::extern_registry::check_arity(
iface_name,
method_name,
nyash_args.len(),
) {
msg.push_str(&format!(" ({})", detail));
}
if let Some(spec) =
crate::runtime::extern_registry::resolve(iface_name, method_name)
{
msg.push_str(&format!(
" (expected arity {}..{})",
spec.min_arity, spec.max_arity
));
} else {
let known = crate::runtime::extern_registry::known_for_iface(iface_name);
if !known.is_empty() {
msg.push_str(&format!("; known methods: {}", known.join(", ")));
} else {
let ifaces = crate::runtime::extern_registry::all_ifaces();
msg.push_str(&format!("; known interfaces: {}", ifaces.join(", ")));
}
}
return Err(VMError::InvalidInstruction(msg));
}
}
Ok(ControlFlow::Continue)
}
}

View File

@ -1,37 +0,0 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::mir::ValueId;
use std::sync::Arc;
impl VM {
/// Execute FunctionNew instruction (construct a FunctionBox value)
pub(crate) fn execute_function_new(
&mut self,
dst: ValueId,
params: &[String],
body: &[crate::ast::ASTNode],
captures: &[(String, ValueId)],
me: &Option<ValueId>,
) -> Result<ControlFlow, VMError> {
// Build ClosureEnv
let mut env = crate::boxes::function_box::ClosureEnv::new();
// Add captures by value
for (name, vid) in captures.iter() {
let v = self.get_value(*vid)?;
env.captures.insert(name.clone(), v.to_nyash_box());
}
// Capture 'me' weakly if provided and is a BoxRef
if let Some(m) = me {
match self.get_value(*m)? {
VMValue::BoxRef(b) => {
env.me_value = Some(Arc::downgrade(&b));
}
_ => {}
}
}
let fun =
crate::boxes::function_box::FunctionBox::with_env(params.to_vec(), body.to_vec(), env);
self.set_value(dst, VMValue::BoxRef(Arc::new(fun)));
Ok(ControlFlow::Continue)
}
}

View File

@ -1,14 +0,0 @@
/*!
* VM Instruction Handlers (split modules)
*
* This module was split to keep files under ~1000 lines while preserving
* behavior and public APIs. Methods remain as `impl VM` across submodules.
*/
pub mod boxcall; // execute_boxcall + vtable stub
pub mod call; // execute_call (Function name or FunctionBox)
pub mod core; // const/binop/unary/compare/print/ctrl/type/phi/mem/array/refs/weak/barriers/exn/await
pub mod extern_call; // execute_extern_call
pub mod function_new; // execute_function_new
pub mod newbox; // execute_newbox
pub mod plugin_invoke; // execute_plugin_invoke + helpers

View File

@ -1,61 +0,0 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::box_trait::NyashBox;
use crate::mir::ValueId;
use std::sync::Arc;
impl VM {
/// Execute NewBox instruction
pub(crate) fn execute_newbox(
&mut self,
dst: ValueId,
box_type: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Convert args to NyashBox values
let arg_values: Vec<Box<dyn NyashBox>> = args
.iter()
.map(|arg| {
let val = self.get_value(*arg)?;
Ok(val.to_nyash_box())
})
.collect::<Result<Vec<_>, VMError>>()?;
// Create new box using runtime's registry
let new_box = {
let registry = self.runtime.box_registry.lock().map_err(|_| {
VMError::InvalidInstruction("Failed to lock box registry".to_string())
})?;
registry.create_box(box_type, &arg_values).map_err(|e| {
VMError::InvalidInstruction(format!("Failed to create {}: {}", box_type, e))
})?
};
// 80/20: Basic boxes are stored as primitives in VMValue for simpler ops
if box_type == "IntegerBox" {
if let Some(ib) = new_box
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
{
self.set_value(dst, VMValue::Integer(ib.value));
return Ok(ControlFlow::Continue);
}
} else if box_type == "BoolBox" {
if let Some(bb) = new_box.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
self.set_value(dst, VMValue::Bool(bb.value));
return Ok(ControlFlow::Continue);
}
} else if box_type == "StringBox" {
if let Some(sb) = new_box
.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
self.set_value(dst, VMValue::String(sb.value.clone()));
return Ok(ControlFlow::Continue);
}
}
self.set_value(dst, VMValue::BoxRef(Arc::from(new_box)));
Ok(ControlFlow::Continue)
}
}

View File

@ -1,346 +0,0 @@
use crate::backend::vm::ControlFlow;
use crate::backend::{VMError, VMValue, VM};
use crate::mir::ValueId;
impl VM {
/// Execute a forced plugin invocation (no builtin fallback)
pub(crate) fn execute_plugin_invoke(
&mut self,
dst: Option<ValueId>,
box_val: ValueId,
method: &str,
args: &[ValueId],
) -> Result<ControlFlow, VMError> {
// Helper: extract UTF-8 string from internal StringBox, Result.Ok(String-like), or plugin StringBox via toUtf8
fn extract_string_from_box(bx: &dyn crate::box_trait::NyashBox) -> Option<String> {
if let Some(sb) = bx.as_any().downcast_ref::<crate::box_trait::StringBox>() {
return Some(sb.value.clone());
}
if let Some(res) = bx
.as_any()
.downcast_ref::<crate::boxes::result::NyashResultBox>()
{
if let crate::boxes::result::NyashResultBox::Ok(inner) = res {
return extract_string_from_box(inner.as_ref());
}
}
if let Some(p) = bx
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if p.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let tmp: Option<String> = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
p.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt {
if let Some(sb2) =
vb.as_any().downcast_ref::<crate::box_trait::StringBox>()
{
Some(sb2.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
};
if tmp.is_some() {
return tmp;
}
}
}
None
}
let recv = self.get_value(box_val)?;
if method == "birth"
&& !matches!(recv, VMValue::BoxRef(ref b) if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some())
{
eprintln!("[VM PluginInvoke] static birth fallback recv={:?}", recv);
let mut created: Option<VMValue> = None;
match &recv {
VMValue::String(s) => {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
let sb: Box<dyn crate::box_trait::NyashBox> =
Box::new(crate::box_trait::StringBox::new(s.clone()));
if let Ok(b) = host.create_box("StringBox", &[sb]) {
created = Some(VMValue::from_nyash_box(b));
}
}
VMValue::Integer(_n) => {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(b) = host.create_box("IntegerBox", &[]) {
created = Some(VMValue::from_nyash_box(b));
}
}
_ => {}
}
if let Some(val) = created {
if let Some(dst_id) = dst {
self.set_value(dst_id, val);
}
return Ok(ControlFlow::Continue);
}
}
if let VMValue::BoxRef(pbox) = &recv {
if let Some(p) = pbox
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
let mh = host.resolve_method(&p.box_type, method).map_err(|_| {
VMError::InvalidInstruction(format!(
"Plugin method not found: {}.{}",
p.box_type, method
))
})?;
let mut tlv =
crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
for (idx, a) in args.iter().enumerate() {
let v = self.get_value(*a)?;
match v {
VMValue::Integer(n) => {
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM→Plugin][vm] arg[{}] encode I64 {}", idx, n);
}
crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, n)
}
VMValue::Float(x) => {
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM→Plugin][vm] arg[{}] encode F64 {}", idx, x);
}
crate::runtime::plugin_ffi_common::encode::f64(&mut tlv, x)
}
VMValue::Bool(b) => {
crate::runtime::plugin_ffi_common::encode::bool(&mut tlv, b)
}
VMValue::String(ref s) => {
crate::runtime::plugin_ffi_common::encode::string(&mut tlv, s)
}
VMValue::BoxRef(ref b) => {
if let Some(h) = b
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if h.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(val_opt) = host.invoke_instance_method(
"StringBox",
"toUtf8",
h.inner.instance_id,
&[],
) {
if let Some(sb) = val_opt.and_then(|bx| {
bx.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
.map(|s| s.value.clone())
}) {
crate::runtime::plugin_ffi_common::encode::string(
&mut tlv, &sb,
);
continue;
}
}
} else if h.box_type == "IntegerBox" {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
if let Ok(val_opt) = host.invoke_instance_method(
"IntegerBox",
"get",
h.inner.instance_id,
&[],
) {
if let Some(ib) = val_opt.and_then(|bx| {
bx.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|i| i.value)
}) {
crate::runtime::plugin_ffi_common::encode::i64(
&mut tlv, ib,
);
continue;
}
}
}
crate::runtime::plugin_ffi_common::encode::plugin_handle(
&mut tlv,
h.inner.type_id,
h.inner.instance_id,
);
} else {
let h = crate::runtime::host_handles::to_handle_arc(b.clone());
crate::runtime::plugin_ffi_common::encode::host_handle(&mut tlv, h);
}
}
VMValue::Future(_) | VMValue::Void => {}
}
}
let mut out = vec![0u8; 32768];
let mut out_len: usize = out.len();
unsafe {
(p.inner.invoke_fn)(
p.inner.type_id,
mh.method_id as u32,
p.inner.instance_id,
tlv.as_ptr(),
tlv.len(),
out.as_mut_ptr(),
&mut out_len,
)
};
let vm_out_raw: VMValue = if let Some((tag, _sz, payload)) =
crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
{
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!(
"[VM←Plugin] tag={} size={} bytes={}",
tag,
_sz,
payload.len()
);
}
match tag {
1 => crate::runtime::plugin_ffi_common::decode::bool(payload)
.map(VMValue::Bool)
.unwrap_or(VMValue::Void),
2 => {
let v = crate::runtime::plugin_ffi_common::decode::i32(payload)
.unwrap_or_default();
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
eprintln!("[VM←Plugin] decode i32={}", v);
}
VMValue::Integer(v as i64)
}
5 => crate::runtime::plugin_ffi_common::decode::f64(payload)
.map(VMValue::Float)
.unwrap_or(VMValue::Void),
6 | 7 => VMValue::String(
crate::runtime::plugin_ffi_common::decode::string(payload),
),
8 => {
if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload)
{
if let Some(arc) = crate::runtime::host_handles::get(u) {
VMValue::BoxRef(arc)
} else {
VMValue::Void
}
} else {
VMValue::Void
}
}
_ => VMValue::Void,
}
} else {
VMValue::Void
};
// Wrap into Result.Ok when method is declared returns_result
let vm_out = {
let host = crate::runtime::get_global_plugin_host();
let host = host.read().unwrap();
let rr = host.method_returns_result(&p.box_type, method);
if rr {
let boxed: Box<dyn crate::box_trait::NyashBox> = match vm_out_raw {
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)),
VMValue::BoxRef(b) => b.share_box(),
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
_ => Box::new(crate::box_trait::StringBox::new(vm_out_raw.to_string())),
};
let res = crate::boxes::result::NyashResultBox::new_ok(boxed);
VMValue::BoxRef(std::sync::Arc::from(
Box::new(res) as Box<dyn crate::box_trait::NyashBox>
))
} else {
vm_out_raw
}
};
if let Some(dst_id) = dst {
self.set_value(dst_id, vm_out);
}
return Ok(ControlFlow::Continue);
}
}
// Fallback: support common string-like methods without requiring PluginBox receiver
if let VMValue::BoxRef(ref bx) = recv {
if let Some(s) = extract_string_from_box(bx.as_ref()) {
match method {
"length" => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Integer(s.len() as i64));
}
return Ok(ControlFlow::Continue);
}
"is_empty" | "isEmpty" => {
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Bool(s.is_empty()));
}
return Ok(ControlFlow::Continue);
}
"charCodeAt" => {
let idx_v = if let Some(a0) = args.get(0) {
self.get_value(*a0)?
} else {
VMValue::Integer(0)
};
let idx = match idx_v {
VMValue::Integer(i) => i.max(0) as usize,
_ => 0,
};
let code = s.chars().nth(idx).map(|c| c as u32 as i64).unwrap_or(0);
if let Some(dst_id) = dst {
self.set_value(dst_id, VMValue::Integer(code));
}
return Ok(ControlFlow::Continue);
}
"concat" => {
let rhs_v = if let Some(a0) = args.get(0) {
self.get_value(*a0)?
} else {
VMValue::String(String::new())
};
let rhs_s = match rhs_v {
VMValue::String(ss) => ss,
VMValue::BoxRef(br) => extract_string_from_box(br.as_ref())
.unwrap_or_else(|| br.to_string_box().value),
_ => rhs_v.to_string(),
};
let mut new_s = s.clone();
new_s.push_str(&rhs_s);
let out = Box::new(crate::box_trait::StringBox::new(new_s));
if let Some(dst_id) = dst {
self.set_value(
dst_id,
VMValue::BoxRef(std::sync::Arc::from(
out as Box<dyn crate::box_trait::NyashBox>,
)),
);
}
return Ok(ControlFlow::Continue);
}
_ => {}
}
}
}
Err(VMError::InvalidInstruction(format!(
"PluginInvoke requires PluginBox receiver; method={} got {:?}",
method, recv
)))
}
}

View File

@ -1,33 +0,0 @@
/*!
* VM Methods Glue
*
* Extracted wrappers for Box method dispatch to keep vm.rs slim.
* These delegate to the real implementation in vm_boxcall.rs, preserving
* the existing VM API surface.
*/
use super::vm::{VMError, VM};
use crate::box_trait::NyashBox;
impl VM {
/// Unified method dispatch entry. Currently delegates to `call_box_method_impl`.
fn call_unified_method(
&self,
box_value: Box<dyn NyashBox>,
method: &str,
args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, VMError> {
self.call_box_method_impl(box_value, method, args)
}
/// Public-facing method call used by vm_instructions::boxcall.
/// Kept as a thin wrapper to the implementation in vm_boxcall.rs.
pub(super) fn call_box_method(
&self,
box_value: Box<dyn NyashBox>,
method: &str,
args: Vec<Box<dyn NyashBox>>,
) -> Result<Box<dyn NyashBox>, VMError> {
self.call_box_method_impl(box_value, method, args)
}
}

View File

@ -1,211 +0,0 @@
/*!
* VM Loop/Phi Utilities
*
* Purpose: Track loop transitions and assist phi node resolution
* Responsibilities: PhiHandler/LoopExecutorでprevious_block・ループイテレーションを管理
* Key APIs: LoopExecutor::{new,record_transition,execute_phi}
* Typical Callers: VM 実行ループ(ブロック遷移/phi評価
*/
use super::vm::{VMError, VMValue};
use crate::mir::{BasicBlockId, ValueId};
use std::collections::HashMap;
/// Phi nodeの実行ヘルパー
pub struct PhiHandler {
/// 現在のブロックに到達する前のブロック
previous_block: Option<BasicBlockId>,
/// Phi nodeの値キャッシュ最適化用
phi_cache: HashMap<ValueId, VMValue>,
}
impl PhiHandler {
/// 新しいPhiハンドラーを作成
pub fn new() -> Self {
Self {
previous_block: None,
phi_cache: HashMap::new(),
}
}
/// ブロック遷移を記録
pub fn record_block_transition(&mut self, from: BasicBlockId, to: BasicBlockId) {
self.previous_block = Some(from);
// ブロック遷移時にキャッシュをクリア(新しいイテレーション)
if self.is_loop_header(to) {
self.phi_cache.clear();
}
}
/// 初期ブロックへのエントリを記録
pub fn record_entry(&mut self) {
self.previous_block = None;
self.phi_cache.clear();
}
/// Phi命令を実行
pub fn execute_phi(
&mut self,
_dst: ValueId,
inputs: &[(BasicBlockId, ValueId)],
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
) -> Result<VMValue, VMError> {
// キャッシュは使わない - Phi nodeは毎回新しい値を計算する必要がある
// if let Some(cached) = self.phi_cache.get(&dst) {
// return Ok(cached.clone());
// }
// Phi nodeの入力を選択
let selected_value = self.select_phi_input(inputs, get_value_fn)?;
// キャッシュに保存(デバッグ用に残すが使わない)
// self.phi_cache.insert(dst, selected_value.clone());
Ok(selected_value)
}
/// Phi nodeの適切な入力を選択
fn select_phi_input(
&self,
inputs: &[(BasicBlockId, ValueId)],
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
) -> Result<VMValue, VMError> {
if inputs.is_empty() {
return Err(VMError::InvalidInstruction(
"Phi node has no inputs".to_string(),
));
}
// previous_blockに基づいて入力を選択
if let Some(prev_block) = self.previous_block {
// 対応するブロックからの入力を探す
for (block_id, value_id) in inputs {
if *block_id == prev_block {
let value = get_value_fn(*value_id)?;
return Ok(value);
}
}
// フォールバック:見つからない場合は最初の入力を使用
// これは通常起こらないはずだが、安全のため
}
// previous_blockがない場合エントリポイントは最初の入力を使用
let (_, value_id) = &inputs[0];
get_value_fn(*value_id)
}
/// ループヘッダーかどうかを判定(簡易版)
fn is_loop_header(&self, _block_id: BasicBlockId) -> bool {
// TODO: MIR情報からループヘッダーを判定する機能を追加
// 現在は常にfalseキャッシュクリアしない
false
}
}
/// ループ実行ヘルパー - ループ特有の処理を管理
pub struct LoopExecutor {
/// Phiハンドラー
phi_handler: PhiHandler,
/// ループイテレーション数(デバッグ用)
iteration_count: HashMap<BasicBlockId, usize>,
}
impl LoopExecutor {
/// 新しいループ実行ヘルパーを作成
pub fn new() -> Self {
Self {
phi_handler: PhiHandler::new(),
iteration_count: HashMap::new(),
}
}
/// ブロック遷移を記録
pub fn record_transition(&mut self, from: BasicBlockId, to: BasicBlockId) {
self.phi_handler.record_block_transition(from, to);
// ループイテレーション数を更新(デバッグ用)
if from > to {
// 単純なバックエッジ検出
*self.iteration_count.entry(to).or_insert(0) += 1;
}
}
/// エントリポイントでの初期化
pub fn initialize(&mut self) {
self.phi_handler.record_entry();
self.iteration_count.clear();
}
/// Phi命令を実行
pub fn execute_phi(
&mut self,
dst: ValueId,
inputs: &[(BasicBlockId, ValueId)],
get_value_fn: impl Fn(ValueId) -> Result<VMValue, VMError>,
) -> Result<VMValue, VMError> {
self.phi_handler.execute_phi(dst, inputs, get_value_fn)
}
/// デバッグ情報を取得
pub fn debug_info(&self) -> String {
let mut info = String::new();
info.push_str("Loop Executor Debug Info:\n");
if let Some(prev) = self.phi_handler.previous_block {
info.push_str(&format!(" Previous block: {:?}\n", prev));
} else {
info.push_str(" Previous block: None (entry)\n");
}
if !self.iteration_count.is_empty() {
info.push_str(" Loop iterations:\n");
for (block, count) in &self.iteration_count {
info.push_str(&format!(" Block {:?}: {} iterations\n", block, count));
}
}
info
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_phi_selection() {
let mut handler = PhiHandler::new();
// テスト用の値
let inputs = vec![
(BasicBlockId::new(0), ValueId::new(1)), // エントリブロックからの初期値
(BasicBlockId::new(2), ValueId::new(2)), // ループボディからの更新値
];
// エントリポイントからの実行
handler.record_entry();
let result = handler.execute_phi(ValueId::new(3), &inputs, |id| {
if id == ValueId::new(1) {
Ok(VMValue::Integer(0))
} else {
Ok(VMValue::Integer(10))
}
});
assert_eq!(result.unwrap(), VMValue::Integer(0));
// ループボディからの実行
handler.record_block_transition(BasicBlockId::new(2), BasicBlockId::new(1));
handler.phi_cache.clear(); // テスト用にキャッシュクリア
let result = handler.execute_phi(ValueId::new(3), &inputs, |id| {
if id == ValueId::new(1) {
Ok(VMValue::Integer(0))
} else {
Ok(VMValue::Integer(10))
}
});
assert_eq!(result.unwrap(), VMValue::Integer(10));
}
}

View File

@ -1,210 +0,0 @@
/*!
* VM State & Basics
*
* Contains constructor helpers, basic value storage, instruction accounting,
* phi selection delegation, and small utilities that support the exec loop.
*/
use super::frame::ExecutionFrame;
use super::vm::{VMError, VMValue, VM};
use super::vm_phi::LoopExecutor;
use crate::mir::{BasicBlockId, ValueId};
use crate::runtime::NyashRuntime;
use crate::scope_tracker::ScopeTracker;
use std::collections::HashMap;
// use std::time::Instant; // not used in this module
impl VM {
fn jit_threshold_from_env() -> u32 {
std::env::var("NYASH_JIT_THRESHOLD")
.ok()
.and_then(|s| s.parse::<u32>().ok())
.filter(|&v| v > 0)
.unwrap_or(64)
}
/// Helper: execute phi via LoopExecutor with previous_block-based selection
pub(super) fn loop_execute_phi(
&mut self,
dst: ValueId,
inputs: &[(BasicBlockId, ValueId)],
) -> Result<VMValue, VMError> {
if inputs.is_empty() {
return Err(VMError::InvalidInstruction(
"Phi node has no inputs".to_string(),
));
}
let debug_phi = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_PHI").ok().as_deref() == Some("1");
if debug_phi {
eprintln!(
"[VM] phi-select (delegated) prev={:?} inputs={:?}",
self.previous_block, inputs
);
}
let values_ref = &self.values;
let res = self.loop_executor.execute_phi(dst, inputs, |val_id| {
let index = val_id.to_usize();
if index < values_ref.len() {
if let Some(ref value) = values_ref[index] {
Ok(value.clone())
} else {
Err(VMError::InvalidValue(format!("Value {} not set", val_id)))
}
} else {
Err(VMError::InvalidValue(format!(
"Value {} out of bounds",
val_id
)))
}
});
if debug_phi {
match &res {
Ok(v) => eprintln!("[VM] phi-result -> {:?}", v),
Err(e) => eprintln!("[VM] phi-error -> {:?}", e),
}
}
res
}
/// Create a new VM instance
pub fn new() -> Self {
Self {
values: Vec::new(),
current_function: None,
frame: ExecutionFrame::new(),
previous_block: 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,
boxcall_hits_vtable: 0,
boxcall_hits_poly_pic: 0,
boxcall_hits_mono_pic: 0,
boxcall_hits_generic: 0,
boxcall_pic_hits: std::collections::HashMap::new(),
boxcall_pic_funcname: std::collections::HashMap::new(),
boxcall_poly_pic: std::collections::HashMap::new(),
boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(),
#[cfg(not(feature = "jit-direct-only"))]
jit_manager: Some(crate::jit::manager::JitManager::new(
Self::jit_threshold_from_env(),
)),
#[cfg(feature = "jit-direct-only")]
jit_manager: None,
}
}
/// Create a VM with an external runtime (dependency injection)
pub fn with_runtime(runtime: NyashRuntime) -> Self {
Self {
values: Vec::new(),
current_function: None,
frame: ExecutionFrame::new(),
previous_block: 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,
boxcall_hits_vtable: 0,
boxcall_hits_poly_pic: 0,
boxcall_hits_mono_pic: 0,
boxcall_hits_generic: 0,
boxcall_pic_hits: std::collections::HashMap::new(),
boxcall_pic_funcname: std::collections::HashMap::new(),
boxcall_poly_pic: std::collections::HashMap::new(),
boxcall_vtable_funcname: std::collections::HashMap::new(),
type_versions: std::collections::HashMap::new(),
#[cfg(not(feature = "jit-direct-only"))]
jit_manager: Some(crate::jit::manager::JitManager::new(
Self::jit_threshold_from_env(),
)),
#[cfg(feature = "jit-direct-only")]
jit_manager: None,
}
}
/// Get a value from storage
pub(super) fn get_value(&self, value_id: ValueId) -> Result<VMValue, VMError> {
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
pub(super) fn set_value(&mut self, value_id: ValueId, value: VMValue) {
let index = value_id.to_usize();
if index >= self.values.len() {
self.values.resize(index + 1, None);
}
self.values[index] = Some(value);
}
/// Record an instruction execution for statistics
pub(super) fn record_instruction(&mut self, instruction: &crate::mir::MirInstruction) {
let key: &'static str = match instruction {
crate::mir::MirInstruction::Const { .. } => "Const",
crate::mir::MirInstruction::BinOp { .. } => "BinOp",
crate::mir::MirInstruction::UnaryOp { .. } => "UnaryOp",
crate::mir::MirInstruction::Compare { .. } => "Compare",
crate::mir::MirInstruction::Load { .. } => "Load",
crate::mir::MirInstruction::Store { .. } => "Store",
crate::mir::MirInstruction::Call { .. } => "Call",
crate::mir::MirInstruction::FunctionNew { .. } => "FunctionNew",
crate::mir::MirInstruction::BoxCall { .. } => "BoxCall",
crate::mir::MirInstruction::Branch { .. } => "Branch",
crate::mir::MirInstruction::Jump { .. } => "Jump",
crate::mir::MirInstruction::Return { .. } => "Return",
crate::mir::MirInstruction::Phi { .. } => "Phi",
crate::mir::MirInstruction::NewBox { .. } => "NewBox",
crate::mir::MirInstruction::TypeCheck { .. } => "TypeCheck",
crate::mir::MirInstruction::Cast { .. } => "Cast",
crate::mir::MirInstruction::TypeOp { .. } => "TypeOp",
crate::mir::MirInstruction::ArrayGet { .. } => "ArrayGet",
crate::mir::MirInstruction::ArraySet { .. } => "ArraySet",
crate::mir::MirInstruction::Copy { .. } => "Copy",
crate::mir::MirInstruction::Debug { .. } => "Debug",
crate::mir::MirInstruction::Print { .. } => "Print",
crate::mir::MirInstruction::Nop => "Nop",
crate::mir::MirInstruction::Throw { .. } => "Throw",
crate::mir::MirInstruction::Catch { .. } => "Catch",
crate::mir::MirInstruction::Safepoint => "Safepoint",
crate::mir::MirInstruction::RefNew { .. } => "RefNew",
crate::mir::MirInstruction::RefGet { .. } => "RefGet",
crate::mir::MirInstruction::RefSet { .. } => "RefSet",
crate::mir::MirInstruction::WeakNew { .. } => "WeakNew",
crate::mir::MirInstruction::WeakLoad { .. } => "WeakLoad",
crate::mir::MirInstruction::BarrierRead { .. } => "BarrierRead",
crate::mir::MirInstruction::BarrierWrite { .. } => "BarrierWrite",
crate::mir::MirInstruction::WeakRef { .. } => "WeakRef",
crate::mir::MirInstruction::Barrier { .. } => "Barrier",
crate::mir::MirInstruction::FutureNew { .. } => "FutureNew",
crate::mir::MirInstruction::FutureSet { .. } => "FutureSet",
crate::mir::MirInstruction::Await { .. } => "Await",
crate::mir::MirInstruction::ExternCall { .. } => "ExternCall",
crate::mir::MirInstruction::PluginInvoke { .. } => "PluginInvoke",
};
*self.instr_counter.entry(key).or_insert(0) += 1;
}
}

View File

@ -1,152 +0,0 @@
/*!
* VM Stats & Diagnostics - prints per-instruction counters and timing
*
* Responsibility:
* - Aggregate instruction counts (by MIR opcode)
* - Print text or JSON summary based on env vars
* - Stay side-effect free w.r.t. VM execution semantics
*/
use super::vm::VM;
impl VM {
/// Print simple VM execution statistics when enabled via env var
pub(super) 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 {
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);
}
}
}
/// Print a concise unified JIT summary alongside VM stats when enabled
pub(super) fn maybe_print_jit_unified_stats(&self) {
// Show when either JIT stats requested or VM stats are on
let jit_enabled = std::env::var("NYASH_JIT_STATS").ok().as_deref() == Some("1");
let vm_enabled = std::env::var("NYASH_VM_STATS")
.ok()
.map(|v| v != "0")
.unwrap_or(false);
let jit_json = std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1");
if !jit_enabled && !vm_enabled {
return;
}
if let Some(jm) = &self.jit_manager {
// Gather basic counters
let sites = jm.sites();
let compiled = jm.compiled_count();
let total_hits: u64 = jm.total_hits();
let ok = jm.exec_ok_count();
let tr = jm.exec_trap_count();
let total_exec = ok + tr;
let fb_rate = if total_exec > 0 {
(tr as f64) / (total_exec as f64)
} else {
0.0
};
let handles = crate::jit::rt::handles::len();
let cfg = crate::jit::config::current();
let caps = crate::jit::config::probe_capabilities();
let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig {
"b1_bool"
} else {
"i64_bool"
};
let b1_norm = crate::jit::rt::b1_norm_get();
let ret_b1_hints = crate::jit::rt::ret_bool_hint_get();
if jit_json {
let payload = serde_json::json!({
"version": 1,
"sites": sites,
"compiled": compiled,
"hits": total_hits,
"exec_ok": ok,
"trap": tr,
"fallback_rate": fb_rate,
"handles": handles,
"abi_mode": abi_mode,
"abi_b1_enabled": cfg.native_bool_abi,
"abi_b1_supported": caps.supports_b1_sig,
"b1_norm_count": b1_norm,
"ret_bool_hint_count": ret_b1_hints,
"phi_total_slots": crate::jit::rt::phi_total_get(),
"phi_b1_slots": crate::jit::rt::phi_b1_get(),
"top5": jm.top_hits(5).into_iter().map(|(name, hits, compiled, handle)| {
serde_json::json!({
"name": name,
"hits": hits,
"compiled": compiled,
"handle": handle
})
}).collect::<Vec<_>>()
});
println!(
"{}",
serde_json::to_string_pretty(&payload).unwrap_or_else(|_| String::from("{}"))
);
} else {
eprintln!("[JIT] summary: sites={} compiled={} hits={} exec_ok={} trap={} fallback_rate={:.2} handles={}",
sites, compiled, total_hits, ok, tr, fb_rate, handles);
eprintln!(
" abi_mode={} b1_norm_count={} ret_bool_hint_count={}",
abi_mode, b1_norm, ret_b1_hints
);
}
}
}
}

View File

@ -1,897 +0,0 @@
/*!
* VM Value Operations
*
* Purpose: Arithmetic/logical/comparison helpers and boolean coercions
* Responsibilities: execute_binary_op / execute_unary_op / execute_compare_op
* Key APIs: execute_binary_op, execute_unary_op, execute_compare_op
* Typical Callers: vm_instructions::{execute_binop, execute_unaryop, execute_compare}
*/
use super::vm::VM;
use super::vm_types::{VMError, VMValue};
use crate::mir::{BinaryOp, CompareOp, UnaryOp};
impl VM {
/// Try to view a BoxRef as a UTF-8 string using unified semantics
fn try_boxref_to_string(&self, b: &dyn crate::box_trait::NyashBox) -> Option<String> {
// Avoid recursion via PluginHost<->Loader for PluginBoxV2 during VM add/string ops
if b.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
.is_some()
{
return None;
}
crate::runtime::semantics::coerce_to_string(b)
}
/// Execute binary operation
pub(super) fn execute_binary_op(
&self,
op: &BinaryOp,
left: &VMValue,
right: &VMValue,
) -> Result<VMValue, VMError> {
let debug_bin = std::env::var("NYASH_VM_DEBUG_BIN").ok().as_deref() == Some("1");
if debug_bin {
eprintln!("[VM] binop {:?} {:?} {:?}", op, left, right);
}
if std::env::var("NYASH_GRAMMAR_DIFF").ok().as_deref() == Some("1") {
let lty = match left {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
let rty = match right {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
match *op {
BinaryOp::Add => {
let strat = crate::grammar::engine::get().add_coercion_strategy();
let rule = crate::grammar::engine::get().decide_add_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][VM] add.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
}
BinaryOp::Sub => {
let strat = crate::grammar::engine::get().sub_coercion_strategy();
let rule = crate::grammar::engine::get().decide_sub_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][VM] sub.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
}
BinaryOp::Mul => {
let strat = crate::grammar::engine::get().mul_coercion_strategy();
let rule = crate::grammar::engine::get().decide_mul_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][VM] mul.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
}
BinaryOp::Div => {
let strat = crate::grammar::engine::get().div_coercion_strategy();
let rule = crate::grammar::engine::get().decide_div_result(lty, rty);
eprintln!(
"[GRAMMAR-DIFF][VM] div.coercion_strategy={} left={} right={} rule={:?}",
strat, lty, rty, rule
);
}
_ => {}
}
}
if matches!(*op, BinaryOp::Add)
&& std::env::var("NYASH_GRAMMAR_ENFORCE_ADD").ok().as_deref() == Some("1")
{
let lty = match left {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
let rty = match right {
VMValue::String(_) => "String",
VMValue::Integer(_) => "Integer",
VMValue::Float(_) => "Float",
VMValue::Bool(_) => "Bool",
_ => "Other",
};
if let Some((res, _)) = crate::grammar::engine::get().decide_add_result(lty, rty) {
match res {
"String" => {
// Best-effort toString concat
fn vmv_to_string(v: &VMValue) -> String {
match v {
VMValue::String(s) => s.clone(),
VMValue::Integer(i) => i.to_string(),
VMValue::Float(f) => f.to_string(),
VMValue::Bool(b) => b.to_string(),
VMValue::Void => "void".to_string(),
VMValue::BoxRef(b) => b.to_string_box().value,
VMValue::Future(_) => "<future>".to_string(),
}
}
let ls = vmv_to_string(left);
let rs = vmv_to_string(right);
return Ok(VMValue::String(format!("{}{}", ls, rs)));
}
"Integer" => {
if let (VMValue::Integer(l), VMValue::Integer(r)) = (left, right) {
return Ok(VMValue::Integer(l + r));
}
}
_ => {}
}
}
}
// Fast path: logical AND/OR accept any truthy via as_bool
if matches!(*op, BinaryOp::And | BinaryOp::Or) {
let l = left.as_bool()?;
let r = right.as_bool()?;
return Ok(VMValue::Bool(match *op {
BinaryOp::And => l && r,
BinaryOp::Or => l || r,
_ => unreachable!(),
}));
}
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)) => 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)) => 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)) => 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)) => {
let rs = self
.try_boxref_to_string(r.as_ref())
.unwrap_or_else(|| r.to_string_box().value);
match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", l, rs))),
_ => Err(VMError::TypeError(
"String-BoxRef operations only support addition".to_string(),
)),
}
}
// BoxRef + String concatenation
(VMValue::BoxRef(l), VMValue::String(r)) => {
let ls = self
.try_boxref_to_string(l.as_ref())
.unwrap_or_else(|| l.to_string_box().value);
match op {
BinaryOp::Add => Ok(VMValue::String(format!("{}{}", ls, r))),
_ => Err(VMError::TypeError(
"BoxRef-String operations only support addition".to_string(),
)),
}
}
// Arithmetic with BoxRef(IntegerBox) — support both legacy and new IntegerBox
(VMValue::BoxRef(li), VMValue::BoxRef(ri))
if {
li.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| li
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some()
} && {
ri.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| ri
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some()
} =>
{
let l = li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap();
let r = ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap();
let res = 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 BoxRef operation: {:?}",
op
)))
}
};
Ok(VMValue::Integer(res))
}
// BoxRef + BoxRef string-like concatenation
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
if matches!(*op, BinaryOp::Add) {
let ls = self
.try_boxref_to_string(li.as_ref())
.unwrap_or_else(|| li.to_string_box().value);
let rs = self
.try_boxref_to_string(ri.as_ref())
.unwrap_or_else(|| ri.to_string_box().value);
return Ok(VMValue::String(format!("{}{}", ls, rs)));
}
Err(VMError::TypeError(
"Unsupported BoxRef+BoxRef operation".to_string(),
))
}
// Mixed Integer forms
(VMValue::BoxRef(li), VMValue::Integer(r))
if li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| li
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some() =>
{
let l = li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap();
let res = 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(res))
}
(VMValue::Integer(l), VMValue::BoxRef(ri))
if ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.is_some()
|| ri
.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.is_some() =>
{
let r = ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.unwrap();
let res = 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(res))
}
// 80/20 fallback: BoxRef(any) numeric via toString().parse::<i64>()
(VMValue::BoxRef(lb), VMValue::BoxRef(rb)) => {
let li = lb.to_string_box().value.trim().parse::<i64>().ok();
let ri = rb.to_string_box().value.trim().parse::<i64>().ok();
if let (Some(l), Some(r)) = (li, ri) {
let res = 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
)))
}
};
if debug_bin {
eprintln!("[VM] binop fallback BoxRef-BoxRef -> {}", res);
}
Ok(VMValue::Integer(res))
} else {
Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
)))
}
}
(VMValue::BoxRef(lb), VMValue::Integer(r)) => {
if let Ok(l) = lb.to_string_box().value.trim().parse::<i64>() {
let res = 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
)))
}
};
if debug_bin {
eprintln!("[VM] binop fallback BoxRef-Int -> {}", res);
}
Ok(VMValue::Integer(res))
} else {
Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
)))
}
}
(VMValue::Integer(l), VMValue::BoxRef(rb)) => {
if let Ok(r) = rb.to_string_box().value.trim().parse::<i64>() {
let res = 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
)))
}
};
if debug_bin {
eprintln!("[VM] binop fallback Int-BoxRef -> {}", res);
}
Ok(VMValue::Integer(res))
} else {
Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
)))
}
}
_ => Err(VMError::TypeError(format!(
"Unsupported binary operation: {:?} on {:?} and {:?}",
op, left, right
))),
}
}
/// Execute unary operation
pub(super) fn execute_unary_op(
&self,
op: &UnaryOp,
operand: &VMValue,
) -> Result<VMValue, VMError> {
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
pub(super) fn execute_compare_op(
&self,
op: &CompareOp,
left: &VMValue,
right: &VMValue,
) -> Result<bool, VMError> {
let debug_cmp = std::env::var("NYASH_VM_DEBUG").ok().as_deref() == Some("1")
|| std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1");
if debug_cmp {
eprintln!(
"[VM] execute_compare_op enter: op={:?}, left={:?}, right={:?}",
op, left, right
);
}
match (left, right) {
// Mixed numeric
(VMValue::Integer(l), VMValue::Float(r)) => {
let l = *l as f64;
let r = *r;
Ok(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,
})
}
(VMValue::Float(l), VMValue::Integer(r)) => {
let l = *l;
let r = *r as f64;
Ok(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,
})
}
// Bool
(VMValue::Bool(l), VMValue::Bool(r)) => Ok(match op {
CompareOp::Eq => l == r,
CompareOp::Ne => l != r,
_ => {
return Err(VMError::TypeError(format!(
"Unsupported boolean comparison: {:?}",
op
)))
}
}),
// Void
(VMValue::Void, VMValue::Void) => Ok(match op {
CompareOp::Eq => true,
CompareOp::Ne => false,
_ => return Err(VMError::TypeError("Cannot order Void".to_string())),
}),
(VMValue::Void, _) | (_, VMValue::Void) => Ok(match op {
CompareOp::Eq => false,
CompareOp::Ne => true,
_ => return Err(VMError::TypeError("Cannot order Void".to_string())),
}),
// Homogeneous
(VMValue::Integer(l), VMValue::Integer(r)) => Ok(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,
}),
(VMValue::Float(l), VMValue::Float(r)) => Ok(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,
}),
(VMValue::String(l), VMValue::String(r)) => Ok(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,
}),
// BoxRef(IntegerBox) comparisons (homogeneous)
(VMValue::BoxRef(li), VMValue::BoxRef(ri)) => {
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!(
"[VM] arm BoxRef-BoxRef: lt={}, rt={}",
li.type_name(),
ri.type_name()
);
}
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
eprintln!(
"[VM] compare BoxRef vs BoxRef: left_type={}, right_type={}, left_str={}, right_str={}",
li.type_name(), ri.type_name(), li.to_string_box().value, ri.to_string_box().value
);
}
// String-like comparison: internal StringBox or Plugin StringBox
fn boxref_to_string(b: &dyn crate::box_trait::NyashBox) -> Option<String> {
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
return Some(sb.value.clone());
}
if let Some(pb) = b
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let s_opt: Option<String> = {
if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
pb.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt {
if let Some(sbb) =
vb.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
Some(sbb.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
}
};
if s_opt.is_some() {
return s_opt;
}
}
}
None
}
if let (Some(ls), Some(rs)) =
(boxref_to_string(li.as_ref()), boxref_to_string(ri.as_ref()))
{
return Ok(match op {
CompareOp::Eq => ls == rs,
CompareOp::Ne => ls != rs,
CompareOp::Lt => ls < rs,
CompareOp::Le => ls <= rs,
CompareOp::Gt => ls > rs,
CompareOp::Ge => ls >= rs,
});
}
// Try integer comparisons via downcast or parse fallback
let l_opt = li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| li.to_string_box().value.parse::<i64>().ok());
let r_opt = ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| ri.to_string_box().value.parse::<i64>().ok());
if let (Some(l), Some(r)) = (l_opt, r_opt) {
return Ok(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,
});
}
Err(VMError::TypeError(format!(
"[BoxRef-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
}
// Mixed String vs BoxRef (string-like)
(VMValue::String(ls), VMValue::BoxRef(ri)) => {
let rs_opt =
if let Some(sb) = ri.as_any().downcast_ref::<crate::box_trait::StringBox>() {
Some(sb.value.clone())
} else {
if let Some(pb) = ri
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let tmp = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
pb.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt {
if let Some(sbb) =
vb.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
Some(sbb.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
};
tmp
} else {
None
}
} else {
None
}
};
if let Some(rs) = rs_opt {
return Ok(match op {
CompareOp::Eq => *ls == rs,
CompareOp::Ne => *ls != rs,
CompareOp::Lt => *ls < rs,
CompareOp::Le => *ls <= rs,
CompareOp::Gt => *ls > rs,
CompareOp::Ge => *ls >= rs,
});
}
Err(VMError::TypeError(format!(
"[String-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
}
(VMValue::BoxRef(li), VMValue::String(rs)) => {
let ls_opt =
if let Some(sb) = li.as_any().downcast_ref::<crate::box_trait::StringBox>() {
Some(sb.value.clone())
} else {
if let Some(pb) = li
.as_any()
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
{
if pb.box_type == "StringBox" {
let host = crate::runtime::get_global_plugin_host();
let tmp = if let Ok(ro) = host.read() {
if let Ok(val_opt) = ro.invoke_instance_method(
"StringBox",
"toUtf8",
pb.inner.instance_id,
&[],
) {
if let Some(vb) = val_opt {
if let Some(sbb) =
vb.as_any()
.downcast_ref::<crate::box_trait::StringBox>()
{
Some(sbb.value.clone())
} else {
None
}
} else {
None
}
} else {
None
}
} else {
None
};
tmp
} else {
None
}
} else {
None
}
};
if let Some(ls) = ls_opt {
return Ok(match op {
CompareOp::Eq => ls == *rs,
CompareOp::Ne => ls != *rs,
CompareOp::Lt => ls < *rs,
CompareOp::Le => ls <= *rs,
CompareOp::Gt => ls > *rs,
CompareOp::Ge => ls >= *rs,
});
}
Err(VMError::TypeError(format!(
"[BoxRef-String] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
}
// Mixed Integer (BoxRef vs Integer)
(VMValue::BoxRef(li), VMValue::Integer(r)) => {
let l_opt = li
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
li.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| li.to_string_box().value.parse::<i64>().ok());
if let Some(l) = l_opt {
return Ok(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,
});
}
Err(VMError::TypeError(format!(
"[BoxRef-Integer] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
}
(VMValue::Integer(l), VMValue::BoxRef(ri)) => {
let r_opt = ri
.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.map(|x| x.value)
.or_else(|| {
ri.as_any()
.downcast_ref::<crate::boxes::integer_box::IntegerBox>()
.map(|x| x.value)
})
.or_else(|| ri.to_string_box().value.parse::<i64>().ok());
if let Some(r) = r_opt {
return Ok(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,
});
}
Err(VMError::TypeError(format!(
"[Integer-BoxRef] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
}
_ => {
// 80/20 numeric fallback: coerce via to_string when possible
fn to_i64(v: &VMValue) -> Option<i64> {
match v {
VMValue::Integer(i) => Some(*i),
VMValue::Bool(b) => Some(if *b { 1 } else { 0 }),
VMValue::String(s) => s.trim().parse::<i64>().ok(),
VMValue::Float(f) => Some(*f as i64),
VMValue::BoxRef(b) => b.to_string_box().value.trim().parse::<i64>().ok(),
_ => None,
}
}
if let (Some(l), Some(r)) = (to_i64(left), to_i64(right)) {
return Ok(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,
});
}
if std::env::var("NYASH_VM_DEBUG_CMP").ok().as_deref() == Some("1") {
let lty = match left {
VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()),
other => format!("{:?}", other),
};
let rty = match right {
VMValue::BoxRef(b) => format!("BoxRef({})", b.type_name()),
other => format!("{:?}", other),
};
eprintln!(
"[VM] compare default arm: op={:?}, left={}, right={}",
op, lty, rty
);
}
Err(VMError::TypeError(format!(
"[Default] Unsupported comparison: {:?} on {:?} and {:?}",
op, left, right
)))
}
}
}
}