/*! * CoreExecutor — JSON v0 → Execute (boxed) * * Responsibility * - Single entry to execute a MIR(JSON) payload under Gate‑C/Core policy. * - Encapsulates: optional canonicalize, v1-bridge try, v0-parse fallback, * OOB strict observation, and rc mapping via MIR Interpreter. * * Notes * - For now, execution uses the existing MIR Interpreter runner * (execute_mir_module_quiet_exit). Later we can swap internals to call * the Core Dispatcher directly without touching callers. */ use super::NyashRunner; use std::io::Write; pub fn run_json_v0(runner: &NyashRunner, json: &str) -> i32 { // Optional: direct Core Dispatcher via child nyash (boxed) // Toggle: HAKO_CORE_DIRECT=1 (alias: NYASH_CORE_DIRECT) let core_direct = std::env::var("HAKO_CORE_DIRECT").ok().as_deref() == Some("1") || std::env::var("NYASH_CORE_DIRECT").ok().as_deref() == Some("1"); if core_direct { // Only attempt Core-Direct when payload already looks like MIR(JSON v0) // i.e., has functions/blocks keys. Stage‑B Program(JSON v0) must go through bridge first. let looks_like_mir = json.contains("\"functions\"") && json.contains("\"blocks\""); if looks_like_mir { // In-proc prototype (opt-in): HAKO_CORE_DIRECT_INPROC=1 (alias NYASH_CORE_DIRECT_INPROC) let core_direct_inproc = std::env::var("HAKO_CORE_DIRECT_INPROC").ok().as_deref() == Some("1") || std::env::var("NYASH_CORE_DIRECT_INPROC").ok().as_deref() == Some("1"); if core_direct_inproc { if let Some(rc) = try_run_core_direct_inproc(runner, json) { return rc; } eprintln!("[core-exec] direct Core (inproc) failed; trying child wrapper"); } if let Some(rc) = try_run_core_direct(json) { return rc; } eprintln!("[core-exec] direct Core (child) failed; falling back to VM interpreter"); } // else: skip direct Core and continue to bridge/VM path } let mut payload = json.to_string(); // Fast-path: accept MIR(JSON v0) directly when it looks like a module (functions/blocks) if payload.contains("\"functions\"") && payload.contains("\"blocks\"") { match super::mir_json_v0::parse_mir_v0_to_module(&payload) { Ok(module) => { super::json_v0_bridge::maybe_dump_mir(&module); crate::runner::child_env::pre_run_reset_oob_if_strict(); let rc = runner.execute_mir_module_quiet_exit(&module); if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); return 1; } return rc; } Err(e) => { eprintln!("❌ MIR JSON v0 parse error: {}", e); return 1; } } } // Always try the v1 bridge first (Stage‑B Program JSON → MIR module). // This is no‑op when input is already MIR(JSON v0) with functions/blocks. if let Ok(j) = crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload) { payload = j; } match crate::runner::json_v1_bridge::try_parse_v1_to_module(&payload) { Ok(Some(module)) => { super::json_v0_bridge::maybe_dump_mir(&module); // OOB strict: reset observation flag crate::runner::child_env::pre_run_reset_oob_if_strict(); let rc = runner.execute_mir_module_quiet_exit(&module); if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); return 1; } return rc; } Ok(None) => { /* fall through to v0 parse/execute */ } Err(e) => { eprintln!("❌ JSON v1 bridge error: {}", e); return 1; } } match super::json_v0_bridge::parse_json_v0_to_module(&payload) { Ok(module) => { super::json_v0_bridge::maybe_dump_mir(&module); crate::runner::child_env::pre_run_reset_oob_if_strict(); let rc = runner.execute_mir_module_quiet_exit(&module); if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); return 1; } rc } Err(e) => { eprintln!("❌ JSON v0 bridge error: {}", e); 1 } } } fn try_run_core_direct(json: &str) -> Option { // Generate a temporary Hako program that includes the Core dispatcher // and calls NyVmDispatcher.run(json), printing the numeric result. let tmp_dir = std::path::Path::new("tmp"); let _ = std::fs::create_dir_all(tmp_dir); let script_path = tmp_dir.join("core_exec_direct.hako"); // Escape JSON into Hako string literal (simple backslash+quote escaping) let mut j = String::new(); for ch in json.chars() { match ch { '\\' => j.push_str("\\\\"), '"' => j.push_str("\\\""), _ => j.push(ch), } } let code = format!( "include \"lang/src/vm/core/dispatcher.hako\"\nstatic box Main {{ method main(args) {{ local j=\"{}\"; local r=NyVmDispatcher.run(j); return r }} }}\n", j ); if let Ok(mut f) = std::fs::File::create(&script_path) { let _ = f.write_all(code.as_bytes()); } else { return None; } // Determine nyash binary (current executable) let exe = std::env::current_exe().ok()?; let mut cmd = std::process::Command::new(exe); crate::runner::child_env::apply_core_wrapper_env(&mut cmd); let out = cmd .args(["--backend", "vm", script_path.to_string_lossy().as_ref()]) .output() .ok()?; if !out.stdout.is_empty() { let _ = std::io::stdout().write_all(&out.stdout); } let rc = out.status.code().unwrap_or(1); Some(rc) } fn try_run_core_direct_inproc(runner: &NyashRunner, json: &str) -> Option { // Parse MIR(JSON v0) in-proc and execute via MIR Interpreter quietly. // This bypasses the child Hako wrapper and reduces latency/recursion risks. match crate::runner::json_v0_bridge::parse_json_v0_to_module(json) { Ok(module) => { crate::runner::child_env::pre_run_reset_oob_if_strict(); let rc = runner.execute_mir_module_quiet_exit(&module); if crate::config::env::oob_strict_fail() && crate::runtime::observe::oob_seen() { eprintln!("[gate-c][oob-strict] Out-of-bounds observed → exit(1)"); return Some(1); } Some(rc) } Err(_) => None, } }