/*! * Execution Runner Module - Nyash File and Mode Execution Coordinator * * This module handles all execution logic, backend selection, and mode coordination, * separated from CLI parsing and the main entry point. */ use nyash_rust::cli::CliConfig; // prune heavy unused imports here; modules import what they need locally // pruned unused runtime imports in this module #[cfg(feature = "wasm-backend")] use nyash_rust::backend::{aot::AotBackend, wasm::WasmBackend}; #[cfg(feature = "llvm-inkwell-legacy")] use nyash_rust::backend::llvm_compile_and_execute; use std::{fs, process}; mod box_index; mod build; mod cli_directives; mod demos; mod dispatch; mod json_v0_bridge; mod mir_json_emit; pub mod modes; mod pipe_io; mod pipeline; mod jit_direct; mod selfhost; mod tasks; mod trace; mod plugins; // v2 plugin system imports use nyash_rust::runner_plugin_init; use nyash_rust::runtime; // use std::path::PathBuf; // not used in current runner /// Resolve a using target according to priority: modules > relative > using-paths /// Returns Ok(resolved_path_or_token). On strict mode, ambiguous matches cause error. // use pipeline::resolve_using_target; // resolved within helpers; avoid unused warning /// Main execution coordinator pub struct NyashRunner { config: CliConfig, } /// Minimal task runner: read nyash.toml [env] and [tasks], run the named task via shell use tasks::run_named_task; #[cfg(not(feature = "jit-direct-only"))] impl NyashRunner { /// Create a new runner with the given configuration pub fn new(config: CliConfig) -> Self { Self { config } } /// Run Nyash based on the configuration pub fn run(&self) { // New behavior-preserving delegator self.run_refactored(); } // init_bid_plugins moved to runner_plugin_init.rs /// Execute file-based mode with backend selection pub(crate) fn run_file(&self, filename: &str) { dispatch::execute_file_with_backend(self, filename); } /// Minimal AOT build pipeline driven by nyash.toml (mvp) fn run_build_mvp(&self, cfg_path: &str) -> Result<(), String> { build::run_build_mvp_impl(self, cfg_path) } } #[cfg(not(feature = "jit-direct-only"))] impl NyashRunner { /// New behavior-preserving refactor of run(): structured into smaller helpers fn run_refactored(&self) { // Early: macro child if let Some(ref macro_file) = self.config.macro_expand_child { crate::runner::modes::macro_child::run_macro_child(macro_file); return; } let groups = self.config.as_groups(); // Early: build if let Some(cfg_path) = groups.build.path.clone() { if let Err(e) = self.run_build_mvp(&cfg_path) { eprintln!("โŒ build error: {}", e); std::process::exit(1); } return; } // Preprocess usings and directives (includes dep-tree log) self.preprocess_usings_and_directives(&groups); // JSON v0 bridge if self.try_run_json_v0_pipe() { return; } // Named task if let Some(task) = groups.run_task.clone() { if let Err(e) = run_named_task(&task) { eprintln!("โŒ Task error: {}", e); process::exit(1); } return; } // Common env + runtime/plugins self.apply_common_env(&groups); self.init_runtime_and_plugins(&groups); // Backend config + policy self.configure_backend(&groups); self.enforce_runtime_jit_policy(&groups); // Benchmark if self.maybe_run_benchmark(&groups) { return; } // Dispatch self.dispatch_entry(&groups); } // ---- Helpers (extracted from original run) ---- fn preprocess_usings_and_directives(&self, groups: &crate::cli::CliGroups) { use pipeline::resolve_using_target; // Initialize UsingContext (defaults + nyash.toml + env) let mut using_ctx = self.init_using_context(); // Collect CLI --using SPEC into (target, alias) let mut pending_using: Vec<(String, Option)> = Vec::new(); for spec in &groups.input.cli_usings { let s = spec.trim(); if s.is_empty() { continue; } let (target, alias) = if let Some(pos) = s.find(" as ") { (s[..pos].trim().to_string(), Some(s[pos + 4..].trim().to_string())) } else { (s.to_string(), None) }; let is_path = target.starts_with('"') || target.starts_with("./") || target.starts_with('/') || target.ends_with(".nyash"); if is_path { let path = target.trim_matches('"').to_string(); let name = alias.clone().unwrap_or_else(|| { std::path::Path::new(&path).file_stem().and_then(|s| s.to_str()).unwrap_or("module").to_string() }); pending_using.push((name, Some(path))); } else { pending_using.push((target, alias)); } } // Apply pending modules (from context) to registry as StringBox for (ns, path) in using_ctx.pending_modules.iter() { let sb = crate::box_trait::StringBox::new(path.clone()); crate::runtime::modules_registry::set(ns.clone(), Box::new(sb)); } // Optional dependency tree bridge (log-only) if let Ok(dep_path) = std::env::var("NYASH_DEPS_JSON") { match std::fs::read_to_string(&dep_path) { Ok(s) => { let bytes = s.as_bytes().len(); let mut root_info = String::new(); if let Ok(v) = serde_json::from_str::(&s) { if let Some(r) = v.get("root_path").and_then(|x| x.as_str()) { root_info = format!(" root='{}'", r); } } crate::cli_v!("[deps] loaded {} bytes from{} {}", bytes, if root_info.is_empty() { "" } else { ":" }, root_info); } Err(e) => { crate::cli_v!("[deps] read error: {}", e); } } } // If a file is provided, apply script-level directives and late using/env merges if let Some(ref filename) = groups.input.file { if let Ok(code) = fs::read_to_string(filename) { // Apply directives and lint let strict_fields = std::env::var("NYASH_FIELDS_TOP_STRICT").ok().as_deref() == Some("1"); if let Err(e) = cli_directives::apply_cli_directives_from_source(&code, strict_fields, groups.debug.cli_verbose) { eprintln!("โŒ Lint/Directive error: {}", e); std::process::exit(1); } // Late env overrides (paths/modules) if let Ok(paths) = std::env::var("NYASH_USING_PATH") { for p in paths.split(':') { let p = p.trim(); if !p.is_empty() { using_ctx.using_paths.push(p.to_string()); } } } if let Ok(mods) = std::env::var("NYASH_MODULES") { for ent in mods.split(',') { if let Some((k, v)) = ent.split_once('=') { let k = k.trim(); let v = v.trim(); if !k.is_empty() && !v.is_empty() { using_ctx.pending_modules.push((k.to_string(), v.to_string())); } } } } // Re-apply pending modules in case env added more (idempotent) for (ns, path) in using_ctx.pending_modules.iter() { let sb = nyash_rust::box_trait::StringBox::new(path.clone()); nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb)); } // Resolve CLI --using entries against context and register values (with aliasing) let strict = std::env::var("NYASH_USING_STRICT").ok().as_deref() == Some("1"); let verbose = crate::config::env::cli_verbose(); let ctx = std::path::Path::new(filename).parent(); for (ns, alias) in pending_using.iter() { let value = match resolve_using_target(ns, false, &using_ctx.pending_modules, &using_ctx.using_paths, &using_ctx.aliases, ctx, strict, verbose) { Ok(v) => v, Err(e) => { eprintln!("โŒ using: {}", e); std::process::exit(1); } }; let sb = nyash_rust::box_trait::StringBox::new(value.clone()); nyash_rust::runtime::modules_registry::set(ns.clone(), Box::new(sb)); if let Some(a) = alias { let sb2 = nyash_rust::box_trait::StringBox::new(value); nyash_rust::runtime::modules_registry::set(a.clone(), Box::new(sb2)); } } } } } /// Apply early environment toggles that affect CLI behavior and VM stats. /// Side effects: sets `NYASH_CLI_VERBOSE`, `NYASH_GC_MODE` when specified by CLI groups. fn apply_common_env(&self, groups: &crate::cli::CliGroups) { if groups.debug.cli_verbose { std::env::set_var("NYASH_CLI_VERBOSE", "1"); } if let Some(ref m) = groups.gc_mode { if !m.trim().is_empty() { std::env::set_var("NYASH_GC_MODE", m); } } } // init_runtime_and_plugins moved to runner/plugins.rs /// Configure backend knobs (VM/JIT) from CLI flags and env, merging with runtime capabilities. /// Side effects: writes environment variables for flags and applies JIT config via nyash_rust::jit::config. fn configure_backend(&self, groups: &crate::cli::CliGroups) { if groups.backend.vm_stats { std::env::set_var("NYASH_VM_STATS", "1"); } if groups.backend.vm_stats_json { std::env::set_var("NYASH_VM_STATS_JSON", "1"); } { if groups.backend.jit.events { std::env::set_var("NYASH_JIT_EVENTS", "1"); } if groups.backend.jit.events_compile { std::env::set_var("NYASH_JIT_EVENTS_COMPILE", "1"); } if groups.backend.jit.events_runtime { std::env::set_var("NYASH_JIT_EVENTS_RUNTIME", "1"); } if let Some(ref p) = groups.backend.jit.events_path { std::env::set_var("NYASH_JIT_EVENTS_PATH", p); } let mut jc = nyash_rust::jit::config::JitConfig::from_env(); jc.exec |= groups.backend.jit.exec; jc.stats |= groups.backend.jit.stats; jc.stats_json |= groups.backend.jit.stats_json; jc.dump |= groups.backend.jit.dump; if groups.backend.jit.threshold.is_some() { jc.threshold = groups.backend.jit.threshold; } jc.phi_min |= groups.backend.jit.phi_min; jc.hostcall |= groups.backend.jit.hostcall; jc.handle_debug |= groups.backend.jit.handle_debug; jc.native_f64 |= groups.backend.jit.native_f64; jc.native_bool |= groups.backend.jit.native_bool; let events_on = std::env::var("NYASH_JIT_EVENTS").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_EVENTS_COMPILE").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_EVENTS_RUNTIME").ok().as_deref() == Some("1"); if events_on && jc.threshold.is_none() { jc.threshold = Some(1); } if groups.backend.jit.only { std::env::set_var("NYASH_JIT_ONLY", "1"); } let caps = nyash_rust::jit::config::probe_capabilities(); jc = nyash_rust::jit::config::apply_runtime_caps(jc, caps); if let Some(path) = &groups.emit.emit_cfg { std::env::set_var("NYASH_JIT_DOT", path); jc.dump = true; } jc.apply_env(); nyash_rust::jit::config::set_current(jc.clone()); } if std::env::var("NYASH_JIT_STRICT").ok().as_deref() == Some("1") { if std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ARGS_HANDLE_ONLY", "1"); } if std::env::var("NYASH_JIT_ONLY").ok().is_none() { std::env::set_var("NYASH_JIT_ONLY", "1"); } } } /// Enforce runtime policy for JIT execution when AOT object output is absent. /// Side effects: may set `NYASH_JIT_EXEC=0` when policy requires. fn enforce_runtime_jit_policy(&self, groups: &crate::cli::CliGroups) { if !groups.compile_native && !groups.backend.jit.direct { let aot_obj = std::env::var("NYASH_AOT_OBJECT_OUT").ok(); if aot_obj.is_none() || aot_obj.as_deref() == Some("") { std::env::set_var("NYASH_JIT_EXEC", "0"); } } } /// Optionally run the benchmark suite and exit, depending on CLI flags. /// Returns true when a benchmark run occurred. fn maybe_run_benchmark(&self, groups: &crate::cli::CliGroups) -> bool { if groups.benchmark { println!("๐Ÿ“Š Nyash Performance Benchmark Suite"); println!("===================================="); println!("Running {} iterations per test...", groups.iterations); println!(); #[cfg(feature = "vm-legacy")] { self.execute_benchmark_mode(); return true; } #[cfg(not(feature = "vm-legacy"))] { eprintln!("โŒ Benchmark mode requires VM backend. Rebuild with --features vm-legacy."); std::process::exit(1); } } false } /// Final dispatch to selected execution mode (file/JIT-direct or demos). fn dispatch_entry(&self, groups: &crate::cli::CliGroups) { if let Some(ref filename) = groups.input.file { if groups.backend.jit.direct { self.run_file_jit_direct(filename); return; } self.run_file(filename); } else { demos::run_all_demos(); } } } impl NyashRunner { /// Run a file through independent JIT engine (no VM execute loop) fn run_file_jit_direct(&self, filename: &str) { use nyash_rust::{mir::MirCompiler, parser::NyashParser}; use std::fs; // Small helper for unified error output (text or JSON) let emit_err = |phase: &str, code: &str, msg: &str| { if std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1") || std::env::var("NYASH_JIT_ERROR_JSON").ok().as_deref() == Some("1") { let payload = serde_json::json!({ "kind": "jit_direct_error", "phase": phase, "code": code, "message": msg, "file": filename, }); println!("{}", payload.to_string()); } else { eprintln!("[JIT-direct][{}][{}] {}", phase, code, msg); } }; // Require cranelift feature at runtime by attempting compile; if unavailable compile_function returns None let code = match fs::read_to_string(filename) { Ok(s) => s, Err(e) => { emit_err("read_file", "IO", &format!("{}", e)); std::process::exit(1); } }; let ast = match NyashParser::parse_from_string(&code) { Ok(a) => a, Err(e) => { emit_err("parse", "SYNTAX", &format!("{}", e)); std::process::exit(1); } }; let mut mc = MirCompiler::new(); let cr = match mc.compile(ast) { Ok(m) => m, Err(e) => { emit_err("mir", "MIR_COMPILE", &format!("{}", e)); std::process::exit(1); } }; let func = match cr.module.functions.get("main") { Some(f) => f, None => { emit_err("mir", "NO_MAIN", "No main function found"); std::process::exit(1); } }; // Guard: refuse write-effects in jit-direct when policy.read_only { use nyash_rust::mir::effect::Effect; let policy = nyash_rust::jit::policy::current(); let mut writes = 0usize; for (_bbid, bb) in func.blocks.iter() { for inst in bb.instructions.iter() { let mask = inst.effects(); if mask.contains(Effect::WriteHeap) { writes += 1; } } if let Some(term) = &bb.terminator { if term.effects().contains(Effect::WriteHeap) { writes += 1; } } } if policy.read_only && writes > 0 { emit_err( "policy", "WRITE_EFFECTS", &format!( "write-effects detected ({} ops). jit-direct is read-only at this stage.", writes ), ); std::process::exit(1); } } // jit-direct ๅฎ‰ๅฎšๅŒ–: ๅˆ†ๅฒๅˆๆต(PHI)ใฏๆ˜Ž็คบใƒ–ใƒญใƒƒใ‚ฏๅผ•ๆ•ฐใง้…็ทš { let mut cfg = nyash_rust::jit::config::current(); cfg.phi_min = true; // enable multi-PHI arg passing/join nyash_rust::jit::config::set_current(cfg); } // Prepare minimal runtime hooks so JIT externs (checkpoint/await) can reach GC/scheduler { let rt = nyash_rust::runtime::NyashRuntime::new(); nyash_rust::runtime::global_hooks::set_from_runtime(&rt); } let mut engine = nyash_rust::jit::engine::JitEngine::new(); match engine.compile_function("main", func) { Some(h) => { // Optional event: compile nyash_rust::jit::events::emit( "compile", &func.signature.name, Some(h), None, serde_json::json!({}), ); // Parse JIT args from env: NYASH_JIT_ARGS (comma-separated), with optional type prefixes // Formats per arg: i:123, f:3.14, b:true/false, h:42 (handle), or bare numbers (int), true/false (bool) let mut jit_args: Vec = Vec::new(); if let Ok(s) = std::env::var("NYASH_JIT_ARGS") { for raw in s.split(',') { let t = raw.trim(); if t.is_empty() { continue; } let v = if let Some(rest) = t.strip_prefix("i:") { rest.parse::() .ok() .map(nyash_rust::jit::abi::JitValue::I64) } else if let Some(rest) = t.strip_prefix("f:") { rest.parse::() .ok() .map(nyash_rust::jit::abi::JitValue::F64) } else if let Some(rest) = t.strip_prefix("b:") { let b = matches!(rest, "1" | "true" | "True" | "TRUE"); Some(nyash_rust::jit::abi::JitValue::Bool(b)) } else if let Some(rest) = t.strip_prefix("h:") { rest.parse::() .ok() .map(nyash_rust::jit::abi::JitValue::Handle) } else if t.eq_ignore_ascii_case("true") || t == "1" { Some(nyash_rust::jit::abi::JitValue::Bool(true)) } else if t.eq_ignore_ascii_case("false") || t == "0" { Some(nyash_rust::jit::abi::JitValue::Bool(false)) } else if let Ok(iv) = t.parse::() { Some(nyash_rust::jit::abi::JitValue::I64(iv)) } else if let Ok(fv) = t.parse::() { Some(nyash_rust::jit::abi::JitValue::F64(fv)) } else { None }; if let Some(jv) = v { jit_args.push(jv); } } } // Coerce args to expected MIR types use nyash_rust::mir::MirType; let expected = &func.signature.params; if expected.len() != jit_args.len() { emit_err( "args", "COUNT_MISMATCH", &format!("expected={}, passed={}", expected.len(), jit_args.len()), ); eprintln!("Hint: set NYASH_JIT_ARGS as comma-separated values, e.g., i:42,f:3.14,b:true"); std::process::exit(1); } let mut coerced: Vec = Vec::with_capacity(jit_args.len()); for (i, (exp, got)) in expected.iter().zip(jit_args.iter()).enumerate() { let cv = match exp { MirType::Integer => match got { nyash_rust::jit::abi::JitValue::I64(v) => { nyash_rust::jit::abi::JitValue::I64(*v) } nyash_rust::jit::abi::JitValue::F64(f) => { nyash_rust::jit::abi::JitValue::I64(*f as i64) } nyash_rust::jit::abi::JitValue::Bool(b) => { nyash_rust::jit::abi::JitValue::I64(if *b { 1 } else { 0 }) } _ => { emit_err( "args", "TYPE_MISMATCH", &format!("param#{} expects Integer", i), ); std::process::exit(1); } }, MirType::Float => match got { nyash_rust::jit::abi::JitValue::F64(f) => { nyash_rust::jit::abi::JitValue::F64(*f) } nyash_rust::jit::abi::JitValue::I64(v) => { nyash_rust::jit::abi::JitValue::F64(*v as f64) } nyash_rust::jit::abi::JitValue::Bool(b) => { nyash_rust::jit::abi::JitValue::F64(if *b { 1.0 } else { 0.0 }) } _ => { emit_err( "args", "TYPE_MISMATCH", &format!("param#{} expects Float", i), ); std::process::exit(1); } }, MirType::Bool => match got { nyash_rust::jit::abi::JitValue::Bool(b) => { nyash_rust::jit::abi::JitValue::Bool(*b) } nyash_rust::jit::abi::JitValue::I64(v) => { nyash_rust::jit::abi::JitValue::Bool(*v != 0) } nyash_rust::jit::abi::JitValue::F64(f) => { nyash_rust::jit::abi::JitValue::Bool(*f != 0.0) } _ => { emit_err( "args", "TYPE_MISMATCH", &format!("param#{} expects Bool", i), ); std::process::exit(1); } }, MirType::String | MirType::Box(_) | MirType::Array(_) | MirType::Future(_) => match got { nyash_rust::jit::abi::JitValue::Handle(h) => { nyash_rust::jit::abi::JitValue::Handle(*h) } _ => { emit_err( "args", "TYPE_MISMATCH", &format!("param#{} expects handle (h:)", i), ); std::process::exit(1); } }, MirType::Void | MirType::Unknown => { // Keep as-is *got } }; coerced.push(cv); } nyash_rust::jit::rt::set_current_jit_args(&coerced); let t0 = std::time::Instant::now(); let out = engine.execute_handle(h, &coerced); match out { Some(v) => { let ms = t0.elapsed().as_millis(); nyash_rust::jit::events::emit( "execute", &func.signature.name, Some(h), Some(ms), serde_json::json!({}), ); // Normalize result according to MIR return type for friendly output use nyash_rust::mir::MirType; let ret_ty = &func.signature.return_type; let vmv = match (ret_ty, v) { (MirType::Bool, nyash_rust::jit::abi::JitValue::I64(i)) => { nyash_rust::backend::vm::VMValue::Bool(i != 0) } (MirType::Bool, nyash_rust::jit::abi::JitValue::Bool(b)) => { nyash_rust::backend::vm::VMValue::Bool(b) } (MirType::Float, nyash_rust::jit::abi::JitValue::F64(f)) => { nyash_rust::backend::vm::VMValue::Float(f) } (MirType::Float, nyash_rust::jit::abi::JitValue::I64(i)) => { nyash_rust::backend::vm::VMValue::Float(i as f64) } // Default adapter for other combos _ => nyash_rust::jit::abi::adapter::from_jit_value(v), }; println!("โœ… JIT-direct execution completed successfully!"); // Pretty print with expected type tag let (ety, sval) = match (ret_ty, &vmv) { (MirType::Bool, nyash_rust::backend::vm::VMValue::Bool(b)) => { ("Bool", b.to_string()) } (MirType::Float, nyash_rust::backend::vm::VMValue::Float(f)) => { ("Float", format!("{}", f)) } (MirType::Integer, nyash_rust::backend::vm::VMValue::Integer(i)) => { ("Integer", i.to_string()) } // Fallbacks (_, nyash_rust::backend::vm::VMValue::Integer(i)) => { ("Integer", i.to_string()) } (_, nyash_rust::backend::vm::VMValue::Float(f)) => { ("Float", format!("{}", f)) } (_, nyash_rust::backend::vm::VMValue::Bool(b)) => { ("Bool", b.to_string()) } (_, nyash_rust::backend::vm::VMValue::String(s)) => { ("String", s.clone()) } (_, nyash_rust::backend::vm::VMValue::BoxRef(arc)) => { ("BoxRef", arc.type_name().to_string()) } (_, nyash_rust::backend::vm::VMValue::Future(_)) => { ("Future", "".to_string()) } (_, nyash_rust::backend::vm::VMValue::Void) => { ("Void", "void".to_string()) } }; println!("ResultType(MIR): {}", ety); println!("Result: {}", sval); // Optional JSON stats if std::env::var("NYASH_JIT_STATS_JSON").ok().as_deref() == Some("1") { let cfg = nyash_rust::jit::config::current(); let caps = nyash_rust::jit::config::probe_capabilities(); let (phi_t, phi_b1, ret_b) = engine.last_lower_stats(); let abi_mode = if cfg.native_bool_abi && caps.supports_b1_sig { "b1_bool" } else { "i64_bool" }; let payload = serde_json::json!({ "version": 1, "function": func.signature.name, "abi_mode": abi_mode, "abi_b1_enabled": cfg.native_bool_abi, "abi_b1_supported": caps.supports_b1_sig, "b1_norm_count": nyash_rust::jit::rt::b1_norm_get(), "ret_bool_hint_count": nyash_rust::jit::rt::ret_bool_hint_get(), "phi_total_slots": phi_t, "phi_b1_slots": phi_b1, "ret_bool_hint_used": ret_b, }); println!("{}", payload.to_string()); } } None => { nyash_rust::jit::events::emit( "fallback", &func.signature.name, Some(h), None, serde_json::json!({"reason":"trap_or_missing"}), ); emit_err( "execute", "TRAP_OR_MISSING", "execution failed (trap or missing handle)", ); std::process::exit(1); } } } None => { emit_err( "compile", "UNAVAILABLE", "Build with --features cranelift-jit", ); std::process::exit(1); } } } } // Demo functions (moved from main.rs) // moved to demos.rs // moved to demos.rs // moved to demos.rs // moved to demos.rs // moved to demos.rs // moved to demos.rs