159 lines
6.7 KiB
Rust
159 lines
6.7 KiB
Rust
/*!
|
||
* 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<i32> {
|
||
// 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<i32> {
|
||
// 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,
|
||
}
|
||
}
|