Files
hakorune/src/runner/core_executor.rs
nyash-codex f9d100ce01 chore: Phase 25.1 完了 - LoopForm v2/Stage1 CLI/環境変数削減 + Phase 26-D からの変更
Phase 25.1 完了成果:
-  LoopForm v2 テスト・ドキュメント・コメント完備
  - 4ケース(A/B/C/D)完全テストカバレッジ
  - 最小再現ケース作成(SSAバグ調査用)
  - SSOT文書作成(loopform_ssot.md)
  - 全ソースに [LoopForm] コメントタグ追加

-  Stage-1 CLI デバッグ環境構築
  - stage1_cli.hako 実装
  - stage1_bridge.rs ブリッジ実装
  - デバッグツール作成(stage1_debug.sh/stage1_minimal.sh)
  - アーキテクチャ改善提案文書

-  環境変数削減計画策定
  - 25変数の完全調査・分類
  - 6段階削減ロードマップ(25→5、80%削減)
  - 即時削除可能変数特定(NYASH_CONFIG/NYASH_DEBUG)

Phase 26-D からの累積変更:
- PHI実装改善(ExitPhiBuilder/HeaderPhiBuilder等)
- MIRビルダーリファクタリング
- 型伝播・最適化パス改善
- その他約300ファイルの累積変更

🎯 技術的成果:
- SSAバグ根本原因特定(条件分岐内loop変数変更)
- Region+next_iパターン適用完了(UsingCollectorBox等)
- LoopFormパターン文書化・テスト化完了
- セルフホスティング基盤強化

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: ChatGPT <noreply@openai.com>
Co-Authored-By: Task Assistant <task@anthropic.com>
2025-11-21 06:25:17 +09:00

196 lines
8.1 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*!
* CoreExecutor — JSON v0 → Execute (boxed)
*
* Responsibility
* - Single entry to execute a MIR(JSON) payload under GateC/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. StageB 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();
// Optional: downconvert/canonicalize even for v1 when requested (dev diagnostics)
if crate::config::env::nyvm_v1_downconvert() {
if let Ok(j) =
crate::runner::modes::common_util::core_bridge::canonicalize_module_json(&payload)
{
payload = j;
}
}
// Prefer v1 bridge when schema_version is present (JSON v1). This must run
// before the v0 fast-path because v1 payloads also contain `functions` and
// `blocks`, which would otherwise be misrouted to the v0 loader.
if payload.contains("\"schema_version\"") {
match crate::runner::json_v1_bridge::try_parse_v1_to_module(&payload) {
Ok(Some(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;
}
Ok(None) => { /* fall through to v0 path */ }
Err(e) => {
eprintln!("❌ JSON v1 bridge error: {}", e);
return 1;
}
}
}
// 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;
}
}
}
// For nonv1 input, attempt canonicalization and v1 bridge (StageB program → MIR).
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);
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,
}
}