fix(bridge): implement env.box_introspect.kind lowering + Stage0 build fixes

Phase 25.1b type system groundwork - env.* namespace support in Bridge layer

Changes:
- Bridge layer (JSON v0 → MIR):
  - Add 'env' as well-known variable in MapVars::resolve()
  - Implement env.box_introspect.kind(value) → ExternCall lowering
  - Pattern: Method { recv: Method { recv: Var("env"), method: "box_introspect" }, method: "kind" }

- VM/extern fixes:
  - Add Arc::from() conversion for env.box_introspect.kind result
  - Fix MapBox API usage in extern_functions.rs logging

- Build fixes:
  - Comment out missing llvm_legacy/llvm modules in src/backend/mod.rs
  - Comment out missing gui_visual_node_prototype in Cargo.toml

- New files:
  - lang/src/shared/common/box_type_inspector_box.hako (type introspection API)

Context:
- Enables BoxTypeInspectorBox to query runtime Box types via env.box_introspect.kind
- Required for selfhost MirBuilder type-aware lowering (multi-carrier loops, etc.)
- Part of Phase 25.1b "no fallback" selfhosting strategy

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-11-16 17:19:56 +09:00
parent 5f06d82ee5
commit fbf4687ea1
6 changed files with 391 additions and 34 deletions

View File

@ -4,12 +4,18 @@
//! that were previously in a large switch statement in loader.rs
use crate::bid::{BidError, BidResult};
use crate::box_trait::{NyashBox, StringBox, VoidBox};
use crate::boxes::result::NyashResultBox;
use crate::box_trait::IntegerBox;
use crate::box_trait::{BoolBox, NyashBox, StringBox, VoidBox};
use crate::boxes::array::ArrayBox;
use crate::boxes::future::FutureBox;
use crate::boxes::map_box::MapBox;
use crate::boxes::null_box::NullBox;
use crate::boxes::result::NyashResultBox;
use crate::boxes::token_box::TokenBox;
use crate::runtime::modules_registry;
use crate::runtime::global_hooks;
use crate::runtime::modules_registry;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
/// Handle external function calls from the runtime
pub fn extern_call(
@ -32,6 +38,7 @@ pub fn extern_call(
"env.future" => handle_future(method_name, args),
"env.mirbuilder" => handle_mirbuilder(method_name, args),
"env.codegen" => handle_codegen(method_name, args),
"env.box_introspect" => handle_box_introspect(method_name, args),
_ => Err(BidError::PluginError),
}
}
@ -41,8 +48,12 @@ fn should_trace_call_extern(target: &str, method: &str) -> bool {
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; }
if p.is_empty() {
continue;
}
if p == method || p == key {
return true;
}
}
return false;
}
@ -50,7 +61,10 @@ fn should_trace_call_extern(target: &str, method: &str) -> bool {
}
/// Handle env.console.* methods
fn handle_console(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_console(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"log" => {
let trace = std::env::var("NYASH_CONSOLE_TRACE").ok().as_deref() == Some("1");
@ -68,7 +82,10 @@ fn handle_console(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Op
}
/// Handle env.result.* methods
fn handle_result(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_result(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"ok" => {
// Wrap the first argument as Result.Ok; if missing, use Void
@ -91,7 +108,10 @@ fn handle_result(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Opt
}
/// Handle env.modules.* methods
fn handle_modules(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_modules(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"set" => {
if args.len() >= 2 {
@ -115,7 +135,10 @@ fn handle_modules(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Op
}
/// Handle env.task.* methods
fn handle_task(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_task(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"cancelCurrent" => {
let tok = global_hooks::current_group_token();
@ -153,7 +176,10 @@ fn handle_task_wait(_args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn Nya
}
/// Handle env.debug.* methods
fn handle_debug(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_debug(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"trace" => {
if std::env::var("NYASH_DEBUG_TRACE").ok().as_deref() == Some("1") {
@ -168,7 +194,10 @@ fn handle_debug(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Opti
}
/// Handle env.runtime.* methods
fn handle_runtime(method_name: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_runtime(
method_name: &str,
_args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"checkpoint" => {
if crate::config::env::runtime_checkpoint_trace() {
@ -182,7 +211,10 @@ fn handle_runtime(method_name: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<O
}
/// Handle env.future.* methods
fn handle_future(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_future(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"new" | "birth" => {
let fut = FutureBox::new();
@ -193,10 +225,7 @@ fn handle_future(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Opt
}
"set" => {
if args.len() >= 2 {
if let Some(fut) = args[0]
.as_any()
.downcast_ref::<FutureBox>()
{
if let Some(fut) = args[0].as_any().downcast_ref::<FutureBox>() {
fut.set_result(args[1].clone_box());
}
}
@ -207,11 +236,110 @@ fn handle_future(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Opt
}
}
/// Handle env.box_introspect.* methods
pub fn handle_box_introspect(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"kind" => {
let value = args.get(0).ok_or(BidError::PluginError)?;
let info = build_box_info(value.as_ref());
if std::env::var("NYASH_BOX_INTROSPECT_TRACE")
.ok()
.as_deref() == Some("1")
{
eprintln!(
"[box_introspect:plugin] kind={} type_name={} is_map={} is_array={}",
info.get(Box::new(StringBox::new("kind"))).to_string_box().value,
info.get(Box::new(StringBox::new("type_name"))).to_string_box().value,
info.get(Box::new(StringBox::new("is_map"))).to_string_box().value,
info.get(Box::new(StringBox::new("is_array"))).to_string_box().value,
);
}
Ok(Some(Box::new(info)))
}
_ => Err(BidError::PluginError),
}
}
fn build_box_info(value: &dyn NyashBox) -> MapBox {
let info = MapBox::new();
insert_string(&info, "kind", &classify_kind(value));
insert_string(&info, "type_name", value.type_name());
insert_string(&info, "type_id", &format!("{:016x}", type_id_hash(value)));
insert_bool(
&info,
"is_map",
value.as_any().downcast_ref::<MapBox>().is_some(),
);
insert_bool(
&info,
"is_array",
value.as_any().downcast_ref::<ArrayBox>().is_some(),
);
insert_bool(
&info,
"is_null",
value.as_any().downcast_ref::<NullBox>().is_some(),
);
info
}
fn insert_string(target: &MapBox, key: &str, value: &str) {
let _ = target.set(
Box::new(StringBox::new(key)),
Box::new(StringBox::new(value)),
);
}
fn insert_bool(target: &MapBox, key: &str, value: bool) {
let _ = target.set(Box::new(StringBox::new(key)), Box::new(BoolBox::new(value)));
}
fn classify_kind(value: &dyn NyashBox) -> String {
if value.as_any().downcast_ref::<MapBox>().is_some() {
return "MapBox".to_string();
}
if value.as_any().downcast_ref::<ArrayBox>().is_some() {
return "ArrayBox".to_string();
}
if value.as_any().downcast_ref::<StringBox>().is_some() {
return "StringBox".to_string();
}
if value.as_any().downcast_ref::<IntegerBox>().is_some() {
return "IntegerBox".to_string();
}
if value.as_any().downcast_ref::<BoolBox>().is_some() {
return "BoolBox".to_string();
}
if value.as_any().downcast_ref::<NullBox>().is_some() {
return "NullBox".to_string();
}
simplify_type_name(value.type_name())
}
fn simplify_type_name(full: &str) -> String {
full.rsplit("::").next().unwrap_or(full).to_string()
}
fn type_id_hash(value: &dyn NyashBox) -> u64 {
let mut hasher = DefaultHasher::new();
value.as_any().type_id().hash(&mut hasher);
hasher.finish()
}
/// Handle env.mirbuilder.* methods (Program(JSON v0) → MIR(JSON v0))
fn handle_mirbuilder(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_mirbuilder(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"emit" => {
let program_json = args.get(0).map(|b| b.to_string_box().value).unwrap_or_default();
let program_json = args
.get(0)
.map(|b| b.to_string_box().value)
.unwrap_or_default();
match crate::host_providers::mir_builder::program_json_to_mir_json(&program_json) {
Ok(s) => Ok(Some(Box::new(StringBox::new(&s)) as Box<dyn NyashBox>)),
Err(_e) => Ok(None),
@ -222,21 +350,36 @@ fn handle_mirbuilder(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult
}
/// Handle env.codegen.* methods (MIR(JSON v0) → object via ny-llvmc)
fn handle_codegen(method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
fn handle_codegen(
method_name: &str,
args: &[Box<dyn NyashBox>],
) -> BidResult<Option<Box<dyn NyashBox>>> {
match method_name {
"emit_object" => {
let mir_json = args.get(0).map(|b| b.to_string_box().value).unwrap_or_default();
let mir_json = args
.get(0)
.map(|b| b.to_string_box().value)
.unwrap_or_default();
// Collect minimal options from env (optional)
let opt_level = std::env::var("HAKO_LLVM_OPT_LEVEL").ok().or_else(|| std::env::var("NYASH_LLVM_OPT_LEVEL").ok());
let opt_level = std::env::var("HAKO_LLVM_OPT_LEVEL")
.ok()
.or_else(|| std::env::var("NYASH_LLVM_OPT_LEVEL").ok());
let out = None;
let nyrt = std::env::var("NYASH_EMIT_EXE_NYRT").ok().map(std::path::PathBuf::from);
let opts = crate::host_providers::llvm_codegen::Opts { out, nyrt, opt_level, timeout_ms: None };
let nyrt = std::env::var("NYASH_EMIT_EXE_NYRT")
.ok()
.map(std::path::PathBuf::from);
let opts = crate::host_providers::llvm_codegen::Opts {
out,
nyrt,
opt_level,
timeout_ms: None,
};
match crate::host_providers::llvm_codegen::mir_json_to_object(&mir_json, opts) {
Ok(p) => {
// Convert PathBuf → String via lossy conversion (owned)
let s = p.to_string_lossy().into_owned();
Ok(Some(Box::new(StringBox::new(s)) as Box<dyn NyashBox>))
},
}
Err(_e) => Ok(None),
}
}