builder/vm: stabilize json_lint_vm under unified calls

- Fix condition_fn resolution: Value call path + dev safety + stub injection
- VM bridge: handle Method::birth via BoxCall; ArrayBox push/get/length/set direct bridge
- Receiver safety: pin receiver in method_call_handlers to avoid undefined use across blocks
- Local vars: materialize on declaration (use init ValueId; void for uninit)
- Prefer legacy BoxCall for Array/Map/String/user boxes in emit_box_or_plugin_call (stability-first)
- Test runner: update LLVM hint to llvmlite harness (remove LLVM_SYS_180_PREFIX guidance)
- Docs/roadmap: update CURRENT_TASK with unified default-ON + guards

Note: NYASH_DEV_BIRTH_INJECT_BUILTINS=1 can re-enable builtin birth() injection during migration.
This commit is contained in:
nyash-codex
2025-09-28 12:19:49 +09:00
parent 41a46b433d
commit 510f4cf523
74 changed files with 2846 additions and 825 deletions

View File

@ -26,15 +26,52 @@ impl MirInterpreter {
) -> Result<VMValue, VMError> {
match callee {
Callee::Global(func_name) => self.execute_global_function(func_name, args),
Callee::Method {
box_name: _,
method,
receiver,
certainty: _,
} => {
Callee::Method { box_name: _, method, receiver, certainty: _, } => {
if let Some(recv_id) = receiver {
let recv_val = self.reg_load(*recv_id)?;
let dev_trace = std::env::var("NYASH_VM_TRACE").ok().as_deref() == Some("1");
// Fast bridge for builtin boxes (Array) and common methods.
// Preserve legacy semantics when plugins are absent.
if let VMValue::BoxRef(bx) = &recv_val {
// ArrayBox bridge
if let Some(arr) = bx.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
match method.as_str() {
"birth" => { return Ok(VMValue::Void); }
"push" => {
if let Some(a0) = args.get(0) {
let v = self.reg_load(*a0)?.to_nyash_box();
let _ = arr.push(v);
return Ok(VMValue::Void);
}
}
"len" | "length" | "size" => {
let ret = arr.length();
return Ok(VMValue::from_nyash_box(ret));
}
"get" => {
if let Some(a0) = args.get(0) {
let idx = self.reg_load(*a0)?.to_nyash_box();
let ret = arr.get(idx);
return Ok(VMValue::from_nyash_box(ret));
}
}
"set" => {
if args.len() >= 2 {
let idx = self.reg_load(args[0])?.to_nyash_box();
let val = self.reg_load(args[1])?.to_nyash_box();
let _ = arr.set(idx, val);
return Ok(VMValue::Void);
}
}
_ => {}
}
}
}
// Minimal bridge for birth(): delegate to BoxCall handler and return Void
if method == &"birth" {
let _ = self.handle_box_call(None, *recv_id, method, args)?;
return Ok(VMValue::Void);
}
let is_kw = method == &"keyword_to_token_type";
if dev_trace && is_kw {
let a0 = args.get(0).and_then(|id| self.reg_load(*id).ok());

View File

@ -62,18 +62,20 @@ fn reroute_to_correct_method(
}
/// Try mapping special methods to canonical targets (table-driven).
/// Example: toString/0 → stringify/0 (prefer instance class, then base class without "Instance" suffix).
/// Example: toString/0 → str/0互換: stringify/0(prefer instance class, then base class without "Instance" suffix).
fn try_special_reroute(
interp: &mut MirInterpreter,
recv_cls: &str,
parsed: &ParsedSig<'_>,
arg_vals: Option<&[VMValue]>,
) -> Option<Result<VMValue, VMError>> {
// toString → stringify
// toString → str互換: stringify
if parsed.method == "toString" && parsed.arity_str == "0" {
// Prefer instance class stringify first, then base (strip trailing "Instance")
// Prefer instance class 'str' first, then basestrip trailing "Instance")。なければ 'stringify' を互換で探す
let base = recv_cls.strip_suffix("Instance").unwrap_or(recv_cls);
let candidates = [
format!("{}.str/0", recv_cls),
format!("{}.str/0", base),
format!("{}.stringify/0", recv_cls),
format!("{}.stringify/0", base),
];
@ -91,7 +93,7 @@ fn try_special_reroute(
"method": parsed.method,
"arity": parsed.arity_str,
"target": name,
"reason": "toString->stringify",
"reason": if name.ends_with(".str/0") { "toString->str" } else { "toString->stringify" },
}),
);
return Some(interp.exec_function_inner(&f, arg_vals));