Phase 22.1 WIP: SSOT resolver + TLV infrastructure + Hako MIR builder setup

Setup infrastructure for Phase 22.1 (TLV C shim & Resolver SSOT):

Core changes:
- Add nyash_tlv, nyash_c_core, nyash_kernel_min_c crates (opt-in)
- Implement SSOT resolver bridge (src/using/ssot_bridge.rs)
- Add HAKO_USING_SSOT=1 / HAKO_USING_SSOT_HAKO=1 env support
- Add HAKO_TLV_SHIM=1 infrastructure (requires --features tlv-shim)

MIR builder improvements:
- Fix using/alias consistency in Hako MIR builder
- Add hako.mir.builder.internal.{prog_scan,pattern_util} to nyash.toml
- Normalize LLVM extern calls: nyash.console.* → nyash_console_*

Smoke tests:
- Add phase2211 tests (using_ssot_hako_parity_canary_vm.sh)
- Add phase2220, phase2230, phase2231 test structure
- Add phase2100 S3 backend selector tests
- Improve test_runner.sh with quiet/timeout controls

Documentation:
- Add docs/ENV_VARS.md (Phase 22.1 env vars reference)
- Add docs/development/runtime/C_CORE_ABI.md
- Update de-rust-roadmap.md with Phase 22.x details

Tools:
- Add tools/hakorune_emit_mir.sh (Hako-first MIR emission wrapper)
- Add tools/tlv_roundtrip_smoke.sh placeholder
- Improve ny_mir_builder.sh with better backend selection

Known issues (to be fixed):
- Parser infinite loop in static method parameter parsing
- Stage-B output contamination with "RC: 0" (needs NYASH_JSON_ONLY=1)
- phase2211/using_ssot_hako_parity_canary_vm.sh fork bomb (needs recursion guard)

Next steps: Fix parser infinite loop + Stage-B quiet mode for green tests
This commit is contained in:
nyash-codex
2025-11-09 15:11:18 +09:00
parent 5d2cd5bad0
commit 981ddd890c
62 changed files with 1981 additions and 103 deletions

View File

@ -17,6 +17,11 @@ pub fn extern_call(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
if std::env::var("HAKO_CALL_TRACE").ok().as_deref() == Some("1") {
if should_trace_call_extern(iface_name, method_name) {
eprintln!("[call:{}.{}]", iface_name, method_name);
}
}
match iface_name {
"env.console" => handle_console(method_name, args),
"env.result" => handle_result(method_name, args),
@ -31,6 +36,19 @@ pub fn extern_call(
}
}
fn should_trace_call_extern(target: &str, method: &str) -> bool {
if let Ok(flt) = std::env::var("HAKO_CALL_TRACE_FILTER") {
let key = format!("{}.{}", target, method);
for pat in flt.split(',') {
let p = pat.trim();
if p.is_empty() { continue; }
if p == method || p == key { return true; }
}
return false;
}
true
}
/// Handle env.console.* methods
fn handle_console(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {

View File

@ -4,6 +4,7 @@ use crate::bid::{BidError, BidResult};
use crate::box_trait::NyashBox;
use crate::runtime::plugin_loader_v2::enabled::PluginLoaderV2;
use std::sync::Arc;
use std::env;
fn dbg_on() -> bool {
std::env::var("PLUGIN_DEBUG").is_ok()
@ -39,9 +40,60 @@ impl PluginLoaderV2 {
let plugins = self.plugins.read().map_err(|_| BidError::PluginError)?;
let _plugin = plugins.get(&lib_name).ok_or(BidError::PluginError)?;
// Optional C wrapper (Phase 22.2: design insertion point; default OFF)
if env::var("HAKO_PLUGIN_LOADER_C_WRAP").ok().as_deref() == Some("1") {
if should_trace_cwrap(box_type, method_name) {
eprintln!("[cwrap:invoke:{}.{}]", box_type, method_name);
}
// Future: route into a thin C shim here. For now, fall through to normal path.
}
// Optional C-core probe (design): emit tag and optionally call into c-core when enabled
if env::var("HAKO_C_CORE_ENABLE").ok().as_deref() == Some("1") && should_route_ccore(box_type, method_name) {
eprintln!("[c-core:invoke:{}.{}]", box_type, method_name);
#[cfg(feature = "c-core")]
{
// MapBox.set: call C-core stub (no-op) with available info
if box_type == "MapBox" && method_name == "set" {
let key = args.get(0).map(|b| b.to_string_box().value).unwrap_or_default();
let val = args.get(1).map(|b| b.to_string_box().value).unwrap_or_default();
let _ = nyash_c_core::core_map_set(type_id as i32, instance_id, &key, &val);
} else if box_type == "ArrayBox" && method_name == "push" {
// For design stage, pass 0 (we don't rely on c-core result)
let _ = nyash_c_core::core_array_push(type_id as i32, instance_id, 0);
} else if box_type == "ArrayBox" && method_name == "get" {
let _ = nyash_c_core::core_array_get(type_id as i32, instance_id, 0);
} else if box_type == "ArrayBox" && (method_name == "size" || method_name == "len" || method_name == "length") {
let _ = nyash_c_core::core_array_len(type_id as i32, instance_id);
} else {
// Generic probe
let _ = nyash_c_core::core_probe_invoke(box_type, method_name, args.len() as i32);
}
}
}
// Encode TLV args via shared helper (numeric→string→toString)
let tlv = crate::runtime::plugin_ffi_common::encode_args(args);
// Unified call trace (optional): plugin calls
if env::var("HAKO_CALL_TRACE").ok().as_deref() == Some("1") {
if should_trace_call(box_type, method_name) {
eprintln!("[call:{}.{}]", box_type, method_name);
}
}
// Optional trace for TLV shim path (debug only; default OFF)
if env::var("HAKO_TLV_SHIM_TRACE").ok().as_deref() == Some("1")
&& env::var("HAKO_TLV_SHIM").ok().as_deref() == Some("1")
{
if should_trace_tlv_shim(box_type, method_name) {
eprintln!("[tlv/shim:{}.{}]", box_type, method_name);
if env::var("HAKO_TLV_SHIM_TRACE_DETAIL").ok().as_deref() == Some("1") {
eprintln!("[tlv/shim:detail argc={}]", args.len());
}
}
}
if dbg_on() {
eprintln!(
"[PluginLoaderV2] call {}.{}: type_id={} method_id={} instance_id={}",
@ -62,6 +114,63 @@ impl PluginLoaderV2 {
}
}
fn should_trace_tlv_shim(box_type: &str, method: &str) -> bool {
// Filter provided → honor it
if let Ok(flt) = env::var("HAKO_TLV_SHIM_FILTER") {
let key = format!("{}.{}", box_type, method);
for pat in flt.split(',') {
let p = pat.trim();
if p.is_empty() { continue; }
if p == method || p == key { return true; }
}
return false;
}
// Default (minimal noise): only trace MapBox.set to begin with
box_type == "MapBox" && method == "set"
}
fn should_trace_cwrap(box_type: &str, method: &str) -> bool {
// Filter provided → honor it
if let Ok(flt) = env::var("HAKO_PLUGIN_LOADER_C_WRAP_FILTER") {
let key = format!("{}.{}", box_type, method);
for pat in flt.split(',') {
let p = pat.trim();
if p.is_empty() { continue; }
if p == method || p == key { return true; }
}
return false;
}
// Default (minimal noise): only trace MapBox.set to begin with
box_type == "MapBox" && method == "set"
}
fn should_trace_call(target: &str, method: &str) -> bool {
if let Ok(flt) = env::var("HAKO_CALL_TRACE_FILTER") {
let key = format!("{}.{}", target, method);
for pat in flt.split(',') {
let p = pat.trim();
if p.is_empty() { continue; }
if p == method || p == key { return true; }
}
return false;
}
true
}
fn should_route_ccore(box_type: &str, method: &str) -> bool {
if let Ok(flt) = env::var("HAKO_C_CORE_TARGETS") {
let key = format!("{}.{}", box_type, method);
for pat in flt.split(',') {
let p = pat.trim();
if p.is_empty() { continue; }
if p == method || p == key { return true; }
}
return false;
}
// Default minimal scope: MapBox.set only
box_type == "MapBox" && method == "set"
}
/// Resolve type information for a box
fn resolve_type_info(loader: &PluginLoaderV2, box_type: &str) -> BidResult<(String, u32)> {
if let Some(cfg) = loader.config.as_ref() {
@ -156,4 +265,4 @@ fn decode_tlv_result(box_type: &str, data: &[u8]) -> BidResult<Option<Box<dyn Ny
return Ok(Some(bx));
}
Ok(Some(Box::new(crate::box_trait::VoidBox::new())))
}
}