diff --git a/src/jit/extern/collections.rs b/src/jit/extern/collections.rs index 008d7273..7a4a1ec7 100644 --- a/src/jit/extern/collections.rs +++ b/src/jit/extern/collections.rs @@ -28,6 +28,7 @@ pub const SYM_MAP_HAS_H: &str = "nyash.map.has_h"; pub const SYM_ANY_LEN_H: &str = "nyash.any.length_h"; pub const SYM_ANY_IS_EMPTY_H: &str = "nyash.any.is_empty_h"; pub const SYM_STRING_CHARCODE_AT_H: &str = "nyash.string.charCodeAt_h"; +pub const SYM_STRING_LEN_H: &str = "nyash.string.len_h"; pub const SYM_STRING_BIRTH_H: &str = "nyash.string.birth_h"; pub const SYM_INTEGER_BIRTH_H: &str = "nyash.integer.birth_h"; // String-like operations (handle, handle) diff --git a/src/jit/lower/builder/cranelift.rs b/src/jit/lower/builder/cranelift.rs index fcc0c8a1..94feaaa8 100644 --- a/src/jit/lower/builder/cranelift.rs +++ b/src/jit/lower/builder/cranelift.rs @@ -24,7 +24,7 @@ use super::super::extern_thunks::{ nyash_array_len_h, nyash_array_get_h, nyash_array_set_h, nyash_array_push_h, nyash_array_last_h, nyash_map_size_h, nyash_map_get_h, nyash_map_get_hh, nyash_map_set_h, nyash_map_has_h, nyash_any_length_h, nyash_any_is_empty_h, - nyash_string_charcode_at_h, nyash_string_birth_h, nyash_integer_birth_h, + nyash_string_charcode_at_h, nyash_string_len_h, nyash_string_birth_h, nyash_integer_birth_h, nyash_string_concat_hh, nyash_string_eq_hh, nyash_string_lt_hh, nyash_box_birth_h, nyash_box_birth_i64, nyash_handle_of, @@ -752,6 +752,7 @@ impl CraneliftBuilder { builder.symbol(c::SYM_MAP_SET_H, nyash_map_set_h as *const u8); builder.symbol(c::SYM_MAP_HAS_H, nyash_map_has_h as *const u8); builder.symbol(c::SYM_ANY_LEN_H, nyash_any_length_h as *const u8); + builder.symbol(c::SYM_STRING_LEN_H, nyash_string_len_h as *const u8); builder.symbol(c::SYM_ANY_IS_EMPTY_H, nyash_any_is_empty_h as *const u8); builder.symbol(c::SYM_STRING_CHARCODE_AT_H, nyash_string_charcode_at_h as *const u8); builder.symbol(c::SYM_STRING_BIRTH_H, nyash_string_birth_h as *const u8); diff --git a/src/jit/lower/core.rs b/src/jit/lower/core.rs index a16f2404..232a99c1 100644 --- a/src/jit/lower/core.rs +++ b/src/jit/lower/core.rs @@ -189,15 +189,20 @@ impl LowerCore { fn emit_len_with_fallback_param(&mut self, b: &mut dyn IRBuilder, pidx: usize) { use super::builder::CmpKind; // Temp locals + let hslot = self.next_local; self.next_local += 1; // receiver handle slot let t_string = self.next_local; self.next_local += 1; let t_any = self.next_local; self.next_local += 1; let t_cond = self.next_local; self.next_local += 1; - // String.len_h + // Materialize receiver handle from param index b.emit_param_i64(pidx); + b.emit_host_call("nyash.handle.of", 1, true); + b.store_local_i64(hslot); + // String.len_h + b.load_local_i64(hslot); b.emit_host_call("nyash.string.len_h", 1, true); b.store_local_i64(t_string); // Any.length_h - b.emit_param_i64(pidx); + b.load_local_i64(hslot); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); b.store_local_i64(t_any); // cond = (string_len == 0) diff --git a/src/jit/lower/core/ops_ext.rs b/src/jit/lower/core/ops_ext.rs index 3ed87e81..f933beec 100644 --- a/src/jit/lower/core/ops_ext.rs +++ b/src/jit/lower/core/ops_ext.rs @@ -177,25 +177,30 @@ impl LowerCore { if std::env::var("NYASH_USE_PLUGIN_BUILTINS").ok().as_deref() == Some("1") { // StringBox (length/is_empty/charCodeAt) if matches!(method, "length" | "is_empty" | "charCodeAt") { - if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); } else { b.emit_const_i64(-1); } - let mut argc = 1usize; - if method == "charCodeAt" { - if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } - argc = 2; + if method == "length" { + // Prefer robust fallback path (param/local/literal/handle.of) + if let Some(pidx) = self.param_index.get(array).copied() { self.emit_len_with_fallback_param(b, pidx); return Ok(true); } + if let Some(slot) = self.local_index.get(array).copied() { self.emit_len_with_fallback_local_handle(b, slot); return Ok(true); } + // literal? + let mut lit: Option = None; + for (_bid, bb) in func.blocks.iter() { for ins in bb.instructions.iter() { if let crate::mir::MirInstruction::NewBox { dst, box_type, args } = ins { if dst == array && box_type == "StringBox" && args.len() == 1 { if let Some(src) = args.get(0) { if let Some(s) = self.known_str.get(src).cloned() { lit = Some(s); break; } } } } } if lit.is_some() { break; } } + if let Some(s) = lit { self.emit_len_with_fallback_literal(b, &s); return Ok(true); } + // last resort: handle.of + any.length_h + self.push_value_if_known_or_param(b, array); b.emit_host_call("nyash.handle.of", 1, true); b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); + return Ok(true); } + // is_empty / charCodeAt: keep mapped hostcall path + // Ensure receiver is a valid runtime handle (param or materialized via handle.of) + if let Some(pidx) = self.param_index.get(array).copied() { b.emit_param_i64(pidx); b.emit_host_call("nyash.handle.of", 1, true); } + else if let Some(slot) = self.local_index.get(array).copied() { b.load_local_i64(slot); } + else { self.push_value_if_known_or_param(b, array); b.emit_host_call("nyash.handle.of", 1, true); } + let mut argc = 1usize; + if method == "charCodeAt" { if let Some(v) = args.get(0) { self.push_value_if_known_or_param(b, v); } else { b.emit_const_i64(0); } argc = 2; } if method == "is_empty" { b.hint_ret_bool(true); } let decision = crate::jit::policy::invoke::decide_box_method("StringBox", method, argc, dst.is_some()); match decision { - crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { - b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); - crate::jit::observe::lower_plugin_invoke(&box_type, method, type_id, method_id, argc); - return Ok(true); - } - crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { - crate::jit::observe::lower_hostcall(&symbol, argc, &if argc==1 { ["Handle"][..].to_vec() } else { ["Handle","I64"][..].to_vec() }, "allow", "mapped_symbol"); - b.emit_host_call(&symbol, argc, dst.is_some()); - return Ok(true); - } + crate::jit::policy::invoke::InvokeDecision::HostCall { symbol, .. } => { crate::jit::observe::lower_hostcall(&symbol, argc, &if argc==1 { ["Handle"][..].to_vec() } else { ["Handle","I64"][..].to_vec() }, "allow", "mapped_symbol"); b.emit_host_call(&symbol, argc, dst.is_some()); return Ok(true); } + crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, method_id, box_type, .. } => { b.emit_plugin_invoke(type_id, method_id, argc, dst.is_some()); crate::jit::observe::lower_plugin_invoke(&box_type, method, type_id, method_id, argc); return Ok(true); } _ => {} } } @@ -335,7 +340,13 @@ impl LowerCore { return Ok(true); } Some("ArrayBox") => {}, - _ => { return Ok(false); } + _ => { + // Unknown receiver type: generic Any.length_h on a handle + self.push_value_if_known_or_param(b, array); + b.emit_host_call("nyash.handle.of", 1, true); + b.emit_host_call(crate::jit::r#extern::collections::SYM_ANY_LEN_H, 1, true); + return Ok(true); + } } if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() { if let Ok(h) = ph.resolve_method("ArrayBox", "length") { diff --git a/src/jit/lower/extern_thunks.rs b/src/jit/lower/extern_thunks.rs index 17a6d7b5..4c36ed9b 100644 --- a/src/jit/lower/extern_thunks.rs +++ b/src/jit/lower/extern_thunks.rs @@ -539,6 +539,34 @@ pub(super) extern "C" fn nyash_string_charcode_at_h(handle: u64, idx: i64) -> i6 -1 } +// String.len_h(handle) -> i64 with param-index fallback (JIT bridge) +#[cfg(feature = "cranelift-jit")] +pub(super) extern "C" fn nyash_string_len_h(handle: u64) -> i64 { + events::emit_runtime(serde_json::json!({"id": c::SYM_STRING_LEN_H, "decision":"allow", "argc":1, "arg_types":["Handle"]}), "hostcall", ""); + if handle > 0 { + if let Some(obj) = crate::jit::rt::handles::get(handle) { + if let Some(sb) = obj.as_any().downcast_ref::() { return sb.value.len() as i64; } + } + // Fallback to any.length_h for non-string handles + return nyash_any_length_h(handle); + } + // Legacy param index fallback (0..16): read from VM args + if handle <= 16 { + let idx = handle as usize; + let val = crate::jit::rt::with_legacy_vm_args(|args| args.get(idx).cloned()); + if let Some(v) = val { + match v { + crate::backend::vm::VMValue::BoxRef(b) => { + if let Some(sb) = b.as_any().downcast_ref::() { return sb.value.len() as i64; } + } + crate::backend::vm::VMValue::String(s) => { return s.len() as i64; } + _ => {} + } + } + } + 0 +} + // ---- Birth (handle) ---- #[cfg(feature = "cranelift-jit")] pub(super) extern "C" fn nyash_string_birth_h() -> i64 {