diff --git a/crates/nyrt/src/plugin.rs b/crates/nyrt/src/plugin.rs deleted file mode 100644 index 4dbf74e0..00000000 --- a/crates/nyrt/src/plugin.rs +++ /dev/null @@ -1,2723 +0,0 @@ -use crate::encode::{nyrt_encode_arg_or_legacy, nyrt_encode_from_legacy_at}; -#[no_mangle] -pub extern "C" fn nyash_plugin_invoke3_i64( - type_id: i64, - method_id: i64, - argc: i64, - a0: i64, - a1: i64, - a2: i64, -) -> i64 { - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - // Resolve receiver instance from handle first; fallback to legacy VM args (param index) - let mut instance_id: u32 = 0; - let mut real_type_id: u32 = 0; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if a0 > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - real_type_id = p.inner.type_id; - invoke = Some(p.inner.invoke_fn); - } - } - } - if invoke.is_none() - && a0 >= 0 - && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") - { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - let idx = a0 as usize; - if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } - } - }); - } - if invoke.is_none() { - return 0; - } - // Build TLV args from a1/a2 if present. Prefer handles/StringBox/IntegerBox via runtime host. - use nyash_rust::{backend::vm::VMValue, jit::rt::handles}; - // argc from LLVM lowering is explicit arg count (excludes receiver) - let nargs = argc.max(0) as usize; - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - // Encode legacy VM arg at position into provided buffer (avoid capturing &mut buf) - let mut encode_from_legacy_into = |dst: &mut Vec, arg_pos: usize| { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - if let Some(v) = args.get(arg_pos) { - match v { - VMValue::String(s) => { - nyash_rust::runtime::plugin_ffi_common::encode::string(dst, s) - } - VMValue::Integer(i) => { - nyash_rust::runtime::plugin_ffi_common::encode::i64(dst, *i) - } - VMValue::Float(f) => { - nyash_rust::runtime::plugin_ffi_common::encode::f64(dst, *f) - } - VMValue::Bool(b) => { - nyash_rust::runtime::plugin_ffi_common::encode::bool(dst, *b) - } - VMValue::BoxRef(b) => { - // BufferBox → TLV bytes - if let Some(bufbox) = b - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::bytes( - dst, - &bufbox.to_vec(), - ); - return; - } - if let Some(p) = b.as_any().downcast_ref::() { - // Prefer StringBox/IntegerBox primitives when possible - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::string( - dst, &s.value, - ); - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method( - "IntegerBox", - "get", - p.instance_id(), - &[], - ) { - if let Some(i) = - ibx.as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - dst, i.value, - ); - return; - } - } - } - } - // Fallback: pass handle as plugin-handle TLV - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - dst, - p.inner.type_id, - p.instance_id(), - ); - } else { - // Stringify unknown boxes - let s = b.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(dst, &s) - } - } - _ => {} - } - } - }); - }; - // Encode argument value or fallback to legacy slot (avoid capturing &mut buf) - let mut encode_arg_into = |dst: &mut Vec, val: i64, pos: usize| { - let mut appended = false; - // Try handle first - if val > 0 { - if let Some(obj) = handles::get(val as u64) { - // BufferBox handle → TLV bytes - if let Some(bufbox) = obj - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::bytes(dst, &bufbox.to_vec()); - appended = true; - return; - } - if let Some(p) = obj.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::string( - dst, &s.value, - ); - appended = true; - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = - hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) - { - if let Some(i) = ibx - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - dst, i.value, - ); - appended = true; - return; - } - } - } - } - // Otherwise, pass as handle TLV - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - dst, - p.inner.type_id, - p.instance_id(), - ); - appended = true; - return; - } - } - } - // Legacy VM args by positional index (1-based for a1) - let before = dst.len(); - encode_from_legacy_into(dst, pos); - if dst.len() != before { - appended = true; - } - // If still nothing appended (no-op), fallback to raw i64 - if !appended { - nyash_rust::runtime::plugin_ffi_common::encode::i64(dst, val); - } - }; - if nargs >= 1 { - encode_arg_into(&mut buf, a1, 1); - } - if nargs >= 2 { - encode_arg_into(&mut buf, a2, 2); - } - // Extra args from legacy VM args (positions 3..nargs) - if nargs > 2 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - for pos in 3..=nargs { - encode_from_legacy_into(&mut buf, pos); - } - } - // Prepare output buffer (dynamic growth on short buffer) - let mut cap: usize = 256; - let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec) = (0, 0, Vec::new()); - loop { - let mut out = vec![0u8; cap]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - type_id as u32, - method_id as u32, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - // Retry on short buffer hint (-1) or when plugin wrote beyond capacity (len > cap) - if rc == -1 || out_len > cap { - cap = cap.saturating_mul(2).max(out_len + 16); - if cap > 1 << 20 { - break; - } - continue; - } - return 0; - } - let slice = &out[..out_len]; - if let Some((t, s, p)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) { - tag_ret = t; - sz_ret = s; - payload_ret = p.to_vec(); - } - break; - } - if payload_ret.is_empty() { - return 0; - } - if let Some((tag, sz, payload)) = Some((tag_ret, sz_ret, payload_ret.as_slice())) { - match tag { - 2 => { - // I32 - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as i64; - } - } - 3 => { - // I64 - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as i64; - } - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return i64::from_le_bytes(b); - } - } - 6 | 7 => { - // String/Bytes -> register StringBox handle - use nyash_rust::box_trait::{NyashBox, StringBox}; - let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload); - let arc: std::sync::Arc = std::sync::Arc::new(StringBox::new(s)); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - 8 => { - // Handle(tag=8) -> register and return handle id (i64) - if sz == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - // Build PluginBoxV2 and register into handle-registry - let box_type_name = - nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host() - .read() - .ok() - .and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())) - .and_then(|m| { - m.into_iter().find(|(_k, v)| *v == r_type).map(|(k, _v)| k) - }) - .unwrap_or_else(|| "PluginBox".to_string()); - let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2( - box_type_name.clone(), - r_type, - r_inst, - invoke.unwrap(), - ); - let arc: std::sync::Arc = - std::sync::Arc::new(pb); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - } - 1 => { - // Bool - return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false) - { - 1 - } else { - 0 - }; - } - 5 => { - // F64 → optional conversion to i64 - if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { - if sz == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - return f as i64; - } - } - } - _ => {} - } - } - 0 -} - -// F64-typed shim: decode TLV first entry and return f64 when possible -#[no_mangle] -pub extern "C" fn nyash_plugin_invoke3_f64( - type_id: i64, - method_id: i64, - argc: i64, - a0: i64, - a1: i64, - a2: i64, -) -> f64 { - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - // Resolve receiver from legacy VM args or handle registry - let mut instance_id: u32 = 0; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if a0 > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } - } - } - if a0 >= 0 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - let idx = a0 as usize; - if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - } - } - }); - } - if invoke.is_none() { - // Fallback scan for any PluginBoxV2 in args to pick invoke_fn - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - for v in args.iter() { - if let nyash_rust::backend::vm::VMValue::BoxRef(b) = v { - if let Some(p) = b.as_any().downcast_ref::() { - if p.inner.type_id == (type_id as u32) || invoke.is_none() { - instance_id = p.instance_id(); - invoke = Some(p.inner.invoke_fn); - if p.inner.type_id == (type_id as u32) { - break; - } - } - } - } - } - }); - } - if invoke.is_none() { - return 0.0; - } - // Build TLV args from a1/a2 with String/Integer support - use nyash_rust::{backend::vm::VMValue, jit::rt::handles}; - // argc from LLVM lowering is explicit arg count (excludes receiver) - let nargs = argc.max(0) as usize; - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - let mut encode_from_legacy = |arg_pos: usize| { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - if let Some(v) = args.get(arg_pos) { - match v { - VMValue::String(s) => { - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, s) - } - VMValue::Integer(i) => { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, *i) - } - VMValue::Float(f) => { - nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f) - } - VMValue::Bool(b) => { - nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b) - } - VMValue::BoxRef(b) => { - if let Some(bufbox) = b - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::bytes( - &mut buf, - &bufbox.to_vec(), - ); - return; - } - if let Some(p) = b.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::string( - &mut buf, &s.value, - ); - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method( - "IntegerBox", - "get", - p.instance_id(), - &[], - ) { - if let Some(i) = - ibx.as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - &mut buf, i.value, - ); - return; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - } else { - let s = b.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s) - } - } - _ => {} - } - } - }); - }; - let mut encode_arg = |val: i64, pos: usize| { - let mut appended = false; - if val > 0 { - if let Some(obj) = handles::get(val as u64) { - if let Some(bufbox) = obj - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::bytes( - &mut buf, - &bufbox.to_vec(), - ); - appended = true; - return; - } - if let Some(p) = obj.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::string( - &mut buf, &s.value, - ); - appended = true; - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = - hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) - { - if let Some(i) = ibx - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - &mut buf, i.value, - ); - appended = true; - return; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - appended = true; - return; - } - } - } - let before = buf.len(); - // Use global helper to avoid nested mutable borrows on buf - nyrt_encode_from_legacy_at(&mut buf, pos); - if buf.len() != before { - appended = true; - } - if !appended { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val); - } - }; - if nargs >= 1 { - encode_arg(a1, 1); - } - if nargs >= 2 { - encode_arg(a2, 2); - } - if nargs > 2 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - for pos in 3..=nargs { - nyrt_encode_from_legacy_at(&mut buf, pos); - } - } - // Prepare output buffer (dynamic growth on short buffer) - let mut cap: usize = 256; - let (mut tag_ret, mut sz_ret, mut payload_ret): (u8, usize, Vec) = (0, 0, Vec::new()); - loop { - let mut out = vec![0u8; cap]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - type_id as u32, - method_id as u32, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - // Retry on short buffer (-1) or when plugin wrote beyond capacity - if rc == -1 || out_len > cap { - cap = cap.saturating_mul(2).max(out_len + 16); - if cap > 1 << 20 { - break; - } - continue; - } - return 0.0; - } - let slice = &out[..out_len]; - if let Some((t, s, p)) = nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) { - tag_ret = t; - sz_ret = s; - payload_ret = p.to_vec(); - } - break; - } - if payload_ret.is_empty() { - return 0.0; - } - if let Some((tag, sz, payload)) = Some((tag_ret, sz_ret, payload_ret.as_slice())) { - match tag { - 5 => { - // F64 - if sz == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return f64::from_le_bytes(b); - } - } - 3 => { - // I64 -> f64 - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as f64; - } - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return (i64::from_le_bytes(b)) as f64; - } - } - 1 => { - // Bool -> f64 - return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false) - { - 1.0 - } else { - 0.0 - }; - } - _ => {} - } - } - 0.0 -} -// By-name shims for common method names (getattr/call) -#[no_mangle] -pub extern "C" fn nyash_plugin_invoke_name_getattr_i64( - argc: i64, - a0: i64, - a1: i64, - a2: i64, -) -> i64 { - nyash_plugin_invoke_name_common_i64("getattr", argc, a0, a1, a2) -} - -#[no_mangle] -pub extern "C" fn nyash_plugin_invoke_name_call_i64(argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { - nyash_plugin_invoke_name_common_i64("call", argc, a0, a1, a2) -} - -fn nyash_plugin_invoke_name_common_i64(method: &str, argc: i64, a0: i64, a1: i64, a2: i64) -> i64 { - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - // Resolve receiver - let mut instance_id: u32 = 0; - let mut type_id: u32 = 0; - let mut box_type: Option = None; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if a0 > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - type_id = p.inner.type_id; - box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); - } - } - } - if invoke.is_none() && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") - { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - let idx = a0.max(0) as usize; - if let Some(nyash_rust::backend::vm::VMValue::BoxRef(b)) = args.get(idx) { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); - type_id = p.inner.type_id; - box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); - } - } - }); - } - if invoke.is_none() { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - for v in args.iter() { - if let nyash_rust::backend::vm::VMValue::BoxRef(b) = v { - if let Some(p) = b.as_any().downcast_ref::() { - instance_id = p.instance_id(); - type_id = p.inner.type_id; - box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); - break; - } - } - } - }); - } - if invoke.is_none() { - return 0; - } - let box_type = box_type.unwrap_or_default(); - // Resolve method_id via PluginHost by name - let mh = if let Ok(host) = - nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host().read() - { - host.resolve_method(&box_type, method) - } else { - return 0; - }; - let method_id = match mh { - Ok(h) => h.method_id, - Err(_) => return 0, - } as u32; - // Build TLV args from legacy VM args (skip receiver slot) - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header( - argc.saturating_sub(1).max(0) as u16, - ); - let mut add_from_legacy = |pos: usize| { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - if let Some(v) = args.get(pos) { - use nyash_rust::backend::vm::VMValue as V; - match v { - V::String(s) => { - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, s) - } - V::Integer(i) => { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, *i) - } - V::Float(f) => { - nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f) - } - V::Bool(b) => { - nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b) - } - V::BoxRef(b) => { - if let Some(bufbox) = b - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::bytes( - &mut buf, - &bufbox.to_vec(), - ); - return; - } - if let Some(p) = b.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::string( - &mut buf, &s.value, - ); - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method( - "IntegerBox", - "get", - p.instance_id(), - &[], - ) { - if let Some(i) = - ibx.as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - &mut buf, i.value, - ); - return; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - } else { - let s = b.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s) - } - } - _ => {} - } - } - }); - }; - if argc >= 2 { - add_from_legacy(1); - } - if argc >= 3 { - add_from_legacy(2); - } - if argc > 3 { - for pos in 3..(argc as usize) { - add_from_legacy(pos); - } - } - let mut out = vec![0u8; 4096]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - type_id as u32, - method_id, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - return 0; - } - let out_slice = &out[..out_len]; - if let Some((tag, _sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(out_slice) - { - match tag { - 3 => { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return i64::from_le_bytes(b); - } - } - 1 => { - return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false) - { - 1 - } else { - 0 - }; - } - 5 => { - if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - return f as i64; - } - } - } - _ => {} - } - } - 0 -} - -// General by-name invoke: (recv_handle, method_cstr, argc, a1, a2) -> i64 -// Export name: nyash.plugin.invoke_by_name_i64 -#[export_name = "nyash.plugin.invoke_by_name_i64"] -pub extern "C" fn nyash_plugin_invoke_by_name_i64( - recv_handle: i64, - method: *const i8, - argc: i64, - a1: i64, - a2: i64, -) -> i64 { - if method.is_null() { - return 0; - } - let mname = unsafe { std::ffi::CStr::from_ptr(method) }; - let Ok(method_str) = mname.to_str() else { - return 0; - }; - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - let mut instance_id: u32 = 0; - let mut type_id: u32 = 0; - let mut box_type: Option = None; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if recv_handle > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_handle as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - type_id = p.inner.type_id; - box_type = Some(p.box_type.clone()); - invoke = Some(p.inner.invoke_fn); - } - } - } - if invoke.is_none() { - return 0; - } - let box_type = box_type.unwrap_or_default(); - // Resolve method_id via PluginHost by name - let mh = if let Ok(host) = - nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host().read() - { - host.resolve_method(&box_type, method_str) - } else { - return 0; - }; - let method_id = match mh { - Ok(h) => h.method_id, - Err(_) => return 0, - } as u32; - // Build TLV args from a1/a2 (no legacy in LLVM path) - // argc is the number of explicit arguments (receiver excluded) - let nargs = argc.max(0) as usize; - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - nyrt_encode_arg_or_legacy(&mut buf, a1, 1); - if nargs >= 2 { - nyrt_encode_arg_or_legacy(&mut buf, a2, 2); - } - // Execute - let mut out = vec![0u8; 512]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - type_id as u32, - method_id, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - return 0; - } - if let Some((tag, _sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) - { - match tag { - 3 => { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return i64::from_le_bytes(b); - } - } - 1 => { - return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false) - { - 1 - } else { - 0 - }; - } - 8 => { - if payload.len() == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2( - box_type.clone(), - r_type, - r_inst, - invoke.unwrap(), - ); - let arc: std::sync::Arc = - std::sync::Arc::new(pb); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - } - 5 => { - if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - return f as i64; - } - } - } - _ => {} - } - } - 0 -} - -// Tagged by-id invoke (supports f64/int/handle for first two args) -// tag: 3=I64, 5=F64(bits), 8=Handle -#[export_name = "nyash_plugin_invoke3_tagged_i64"] -pub extern "C" fn nyash_plugin_invoke3_tagged_i64( - type_id: i64, - method_id: i64, - argc: i64, - a0: i64, - a1: i64, - tag1: i64, - a2: i64, - tag2: i64, - a3: i64, - tag3: i64, - a4: i64, - tag4: i64, -) -> i64 { - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - // Resolve receiver invoke and actual plugin type_id - let mut instance_id: u32 = 0; - let mut real_type_id: u32 = type_id as u32; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if a0 > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - real_type_id = p.inner.type_id; - invoke = Some(p.inner.invoke_fn); - } - } - } - if invoke.is_none() { - return 0; - } - // Build TLV from tags - // argc is the number of explicit arguments (receiver excluded) - let nargs = argc.max(0) as usize; - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - let mut enc = |val: i64, tag: i64| match tag { - 3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val), - 5 => { - let bits = val as u64; - let f = f64::from_bits(bits); - nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); - } - 8 => { - if val > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(val as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - } else { - let s = obj.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s); - } - } - } else { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0); - } - } - _ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, val), - }; - if nargs >= 1 { - enc(a1, tag1); - } - if nargs >= 2 { - enc(a2, tag2); - } - if nargs >= 3 { - enc(a3, tag3); - } - if nargs >= 4 { - enc(a4, tag4); - } - // Invoke - let mut out = vec![0u8; 512]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - real_type_id, - method_id as u32, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - return 0; - } - if let Some((tag, _sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) - { - match tag { - 2 => { - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as i64; - } - } - 3 => { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return i64::from_le_bytes(b); - } - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as i64; - } - } - 6 | 7 => { - use nyash_rust::box_trait::{NyashBox, StringBox}; - let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload); - let arc: std::sync::Arc = std::sync::Arc::new(StringBox::new(s)); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - 1 => { - return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false) - { - 1 - } else { - 0 - }; - } - 8 => { - if payload.len() == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2( - "PluginBox".into(), - r_type, - r_inst, - invoke.unwrap(), - ); - let arc: std::sync::Arc = - std::sync::Arc::new(pb); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - } - 5 => { - if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - return f as i64; - } - } - } - _ => {} - } - } - 0 -} - -// Variable-length tagged invoke by-id -// Exported as: nyash.plugin.invoke_tagged_v_i64(i64 type_id, i64 method_id, i64 argc, i64 recv_h, i64* vals, i64* tags) -> i64 -#[export_name = "nyash.plugin.invoke_tagged_v_i64"] -pub extern "C" fn nyash_plugin_invoke_tagged_v_i64( - type_id: i64, - method_id: i64, - argc: i64, - recv_h: i64, - vals: *const i64, - tags: *const i64, -) -> i64 { - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - if recv_h <= 0 { - return 0; - } - // Resolve receiver invoke - let mut instance_id: u32 = 0; - let mut real_type_id: u32 = 0; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_h as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - real_type_id = p.inner.type_id; - invoke = Some(p.inner.invoke_fn); - } - } - if invoke.is_none() { - return 0; - } - let nargs = argc.saturating_sub(1).max(0) as usize; - let (vals, tags) = if nargs > 0 && !vals.is_null() && !tags.is_null() { - unsafe { - ( - std::slice::from_raw_parts(vals, nargs), - std::slice::from_raw_parts(tags, nargs), - ) - } - } else { - (&[][..], &[][..]) - }; - - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - for i in 0..nargs { - match tags[i] { - 3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, vals[i]), - 5 => { - let f = f64::from_bits(vals[i] as u64); - nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); - } - 8 => { - if let Some(obj) = nyash_rust::jit::rt::handles::get(vals[i] as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - } else { - let s = obj.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s); - } - } else { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0); - } - } - _ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, vals[i]), - } - } - let mut out = vec![0u8; 1024]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - real_type_id, - method_id as u32, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - return 0; - } - if let Some((tag, _sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) - { - match tag { - 2 => { - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as i64; - } - } - 3 => { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - return i64::from_le_bytes(b); - } - if let Some(v) = nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) { - return v as i64; - } - } - 6 | 7 => { - use nyash_rust::box_trait::{NyashBox, StringBox}; - let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload); - let arc: std::sync::Arc = std::sync::Arc::new(StringBox::new(s)); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - 1 => { - return if nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false) - { - 1 - } else { - 0 - }; - } - 8 => { - if payload.len() == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2( - "PluginBox".into(), - r_type, - r_inst, - invoke.unwrap(), - ); - let arc: std::sync::Arc = - std::sync::Arc::new(pb); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - } - 5 => { - if std::env::var("NYASH_JIT_NATIVE_F64").ok().as_deref() == Some("1") { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - return f as i64; - } - } - } - _ => {} - } - } - 0 -} - -// Spawn a plugin instance method asynchronously and return a Future handle (i64) -// Exported as: nyash.future.spawn_method_h(type_id, method_id, argc, recv_h, vals*, tags*) -> i64 (FutureBox handle) -#[export_name = "nyash.future.spawn_method_h"] -pub extern "C" fn nyash_future_spawn_method_h( - type_id: i64, - method_id: i64, - argc: i64, - recv_h: i64, - vals: *const i64, - tags: *const i64, -) -> i64 { - use nyash_rust::box_trait::{IntegerBox, NyashBox, StringBox}; - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - if recv_h <= 0 { - return 0; - } - // Resolve receiver invoke - let mut instance_id: u32 = 0; - let mut real_type_id: u32 = type_id as u32; - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - if let Some(obj) = nyash_rust::jit::rt::handles::get(recv_h as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - instance_id = p.instance_id(); - real_type_id = p.inner.type_id; - invoke = Some(p.inner.invoke_fn); - } - } - if invoke.is_none() { - return 0; - } - // Build TLV from tagged arrays (argc includes receiver) - let nargs = argc.saturating_sub(1).max(0) as usize; - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - let vals_slice = if !vals.is_null() && nargs > 0 { - unsafe { std::slice::from_raw_parts(vals, nargs) } - } else { - &[] - }; - let tags_slice = if !tags.is_null() && nargs > 0 { - unsafe { std::slice::from_raw_parts(tags, nargs) } - } else { - &[] - }; - for i in 0..nargs { - let v = vals_slice.get(i).copied().unwrap_or(0); - let t = tags_slice.get(i).copied().unwrap_or(3); // default i64 - match t { - 3 => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, v), - 5 => { - let bits = v as u64; - let f = f64::from_bits(bits); - nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, f); - } - 8 => { - if v > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(v as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - // Try common coercions: String/Integer to TLV primitives - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::string( - &mut buf, &s.value, - ); - continue; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method( - "IntegerBox", - "get", - p.instance_id(), - &[], - ) { - if let Some(i) = ibx.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - &mut buf, i.value, - ); - continue; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - } else { - let s = obj.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s); - } - } else { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, v); - } - } else { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, 0); - } - } - _ => nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, v), - } - } - // Prepare FutureBox and register handle - let fut_box = std::sync::Arc::new(nyash_rust::boxes::future::FutureBox::new()); - let handle = - nyash_rust::jit::rt::handles::to_handle(fut_box.clone() as std::sync::Arc); - // Copy data for async task - let mut cap: usize = 512; - let tlv = buf.clone(); - let inv = invoke.unwrap(); - nyash_rust::runtime::global_hooks::spawn_task( - "nyash.future.spawn_method_h", - Box::new(move || { - // Growable output buffer loop - let mut out = vec![0u8; cap]; - let mut out_len: usize = out.len(); - let rc = unsafe { - inv( - real_type_id, - method_id as u32, - instance_id, - tlv.as_ptr(), - tlv.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - // Set simple error string on failure - fut_box.set_result(Box::new(StringBox::new(format!("invoke_failed rc={}", rc)))); - return; - } - let slice = &out[..out_len]; - if let Some((tag, sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) - { - match tag { - 3 => { - // I64 - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let n = i64::from_le_bytes(b); - fut_box.set_result(Box::new(IntegerBox::new(n))); - return; - } - } - 2 => { - if let Some(v) = - nyash_rust::runtime::plugin_ffi_common::decode::i32(payload) - { - fut_box.set_result(Box::new(IntegerBox::new(v as i64))); - return; - } - } - 1 => { - // Bool - let v = nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false); - fut_box.set_result(Box::new(nyash_rust::box_trait::BoolBox::new(v))); - return; - } - 5 => { - // F64 - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - fut_box.set_result(Box::new( - nyash_rust::boxes::math_box::FloatBox::new(f), - )); - return; - } - } - 6 | 7 => { - // String/Bytes as string - let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload); - fut_box.set_result(Box::new(StringBox::new(s))); - return; - } - 8 => { - // Handle -> PluginBoxV2 boxed - if sz == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - // Map type_id -> box type name (best-effort) - let box_type_name = - nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host( - ) - .read() - .ok() - .and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())) - .and_then(|m| { - m.into_iter().find(|(_k, v)| *v == r_type).map(|(k, _v)| k) - }) - .unwrap_or_else(|| "PluginBox".to_string()); - let pb = nyash_rust::runtime::plugin_loader_v2::construct_plugin_box( - box_type_name, - r_type, - inv, - r_inst, - None, - ); - fut_box.set_result(Box::new(pb)); - return; - } - } - 9 => { - // Void - fut_box.set_result(Box::new(nyash_rust::box_trait::VoidBox::new())); - return; - } - _ => {} - } - } - // Fallback: store raw buffer as string preview - fut_box.set_result(Box::new(StringBox::new(""))); - }), - ); - handle as i64 -} - -// Simpler spawn shim for JIT: pass argc(total explicit args incl. method_name), -// receiver handle (a0), method name (a1), and first payload (a2). Extra args -// are read from legacy VM args, same as plugin_invoke3_*. -// Returns a handle (i64) to FutureBox. -#[export_name = "nyash.future.spawn_instance3_i64"] -pub extern "C" fn nyash_future_spawn_instance3_i64(a0: i64, a1: i64, a2: i64, argc: i64) -> i64 { - use nyash_rust::box_trait::{IntegerBox, NyashBox, StringBox}; - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - if a0 <= 0 { - return 0; - } - // Resolve receiver invoke and type id/name - let (instance_id, real_type_id, invoke) = - if let Some(obj) = nyash_rust::jit::rt::handles::get(a0 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - (p.instance_id(), p.inner.type_id, Some(p.inner.invoke_fn)) - } else { - (0, 0, None) - } - } else { - (0, 0, None) - }; - if invoke.is_none() { - return 0; - } - let invoke = invoke.unwrap(); - // Determine box type name from type_id - let box_type_name = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host() - .read() - .ok() - .and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())) - .and_then(|m| { - m.into_iter() - .find(|(_k, v)| *v == real_type_id) - .map(|(k, _v)| k) - }) - .unwrap_or_else(|| "PluginBox".to_string()); - // Determine method name string (from a1 handle→StringBox, or a1 as C string pointer, or legacy VM args) - let mut method_name: Option = None; - if a1 > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(a1 as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - if p.box_type == "StringBox" { - // Limit the lifetime of the read guard to this inner block by avoiding an outer binding - if let Ok(hg) = nyash_rust::runtime::get_global_plugin_host().read() { - if let Ok(Some(sb)) = - hg.invoke_instance_method("StringBox", "toUtf8", p.instance_id(), &[]) - { - if let Some(s) = sb.as_any().downcast_ref::() { - method_name = Some(s.value.clone()); - } - } - } - } - } - } - // If not a handle, try to decode as C string pointer (LLVM path) - if method_name.is_none() { - let cptr = a1 as *const i8; - if !cptr.is_null() { - unsafe { - if let Ok(cs) = std::ffi::CStr::from_ptr(cptr).to_str() { - method_name = Some(cs.to_string()); - } - } - } - } - } - if method_name.is_none() { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - // method name is explicit arg position 1 (after receiver) - if let Some(nyash_rust::backend::vm::VMValue::String(s)) = args.get(1) { - method_name = Some(s.clone()); - } - }); - } - let method_name = match method_name { - Some(s) => s, - None => return 0, - }; - // Resolve method_id via PluginHost - let mh_opt = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host() - .read() - .ok() - .and_then(|h| h.resolve_method(&box_type_name, &method_name).ok()); - let method_id = if let Some(mh) = mh_opt { - mh.method_id - } else { - 0 - }; - if method_id == 0 { /* dynamic plugins may use 0 for birth; disallow here */ } - // Build TLV args for payload (excluding method name) - let nargs_total = argc.max(0) as usize; // includes method_name - let nargs_payload = nargs_total.saturating_sub(1); - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs_payload as u16); - let mut encode_from_legacy_into = |dst: &mut Vec, pos: usize| { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - if let Some(v) = args.get(pos) { - use nyash_rust::backend::vm::VMValue; - match v { - VMValue::String(s) => { - nyash_rust::runtime::plugin_ffi_common::encode::string(dst, s) - } - VMValue::Integer(i) => { - nyash_rust::runtime::plugin_ffi_common::encode::i64(dst, *i) - } - VMValue::Float(f) => { - nyash_rust::runtime::plugin_ffi_common::encode::f64(dst, *f) - } - VMValue::Bool(b) => { - nyash_rust::runtime::plugin_ffi_common::encode::bool(dst, *b) - } - VMValue::BoxRef(b) => { - if let Some(p) = b.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::string( - dst, &s.value, - ); - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = hg.invoke_instance_method( - "IntegerBox", - "get", - p.instance_id(), - &[], - ) { - if let Some(i) = ibx.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - dst, i.value, - ); - return; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - dst, - p.inner.type_id, - p.instance_id(), - ); - return; - } - // Fallback: stringify - let s = b.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(dst, &s); - } - _ => {} - } - } - }); - }; - let mut encode_arg_into = |dst: &mut Vec, val: i64, pos: usize| { - let mut appended = false; - if val > 0 { - if let Some(obj) = nyash_rust::jit::rt::handles::get(val as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::string( - dst, &s.value, - ); - appended = true; - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = - hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) - { - if let Some(i) = ibx.as_any().downcast_ref::() { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - dst, i.value, - ); - appended = true; - return; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - dst, - p.inner.type_id, - p.instance_id(), - ); - appended = true; - return; - } - } - } - let before = dst.len(); - encode_from_legacy_into(dst, pos); - if dst.len() != before { - appended = true; - } - if !appended { - nyash_rust::runtime::plugin_ffi_common::encode::i64(dst, val); - } - }; - // a1 is method name; payload starts at position 2 - if nargs_payload >= 1 { - encode_arg_into(&mut buf, a2, 2); - } - if nargs_payload > 1 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") - { - for pos in 3..=nargs_payload { - encode_from_legacy_into(&mut buf, pos); - } - } - // Create Future and schedule async invoke - let fut_box = std::sync::Arc::new(nyash_rust::boxes::future::FutureBox::new()); - let handle = - nyash_rust::jit::rt::handles::to_handle(fut_box.clone() as std::sync::Arc); - let tlv = buf.clone(); - nyash_rust::runtime::global_hooks::spawn_task( - "nyash.future.spawn_instance3_i64", - Box::new(move || { - // Dynamic output buffer with growth - let mut cap: usize = 512; - loop { - let mut out = vec![0u8; cap]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke( - real_type_id, - method_id as u32, - instance_id, - tlv.as_ptr(), - tlv.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc == -1 || out_len > cap { - cap = cap.saturating_mul(2).max(out_len + 16); - if cap > 1 << 20 { - break; - } - continue; - } - if rc != 0 { - fut_box - .set_result(Box::new(StringBox::new(format!("invoke_failed rc={}", rc)))); - return; - } - let slice = &out[..out_len]; - if let Some((tag, sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(slice) - { - match tag { - 3 => { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let n = i64::from_le_bytes(b); - fut_box.set_result(Box::new(IntegerBox::new(n))); - return; - } - } - 1 => { - let v = nyash_rust::runtime::plugin_ffi_common::decode::bool(payload) - .unwrap_or(false); - fut_box.set_result(Box::new(nyash_rust::box_trait::BoolBox::new(v))); - return; - } - 5 => { - if payload.len() == 8 { - let mut b = [0u8; 8]; - b.copy_from_slice(payload); - let f = f64::from_le_bytes(b); - fut_box.set_result(Box::new( - nyash_rust::boxes::math_box::FloatBox::new(f), - )); - return; - } - } - 6 | 7 => { - let s = nyash_rust::runtime::plugin_ffi_common::decode::string(payload); - fut_box.set_result(Box::new(StringBox::new(s))); - return; - } - 8 => { - if sz == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - let pb = - nyash_rust::runtime::plugin_loader_v2::construct_plugin_box( - box_type_name.clone(), - r_type, - invoke, - r_inst, - None, - ); - fut_box.set_result(Box::new(pb)); - return; - } - } - 9 => { - fut_box.set_result(Box::new(nyash_rust::box_trait::VoidBox::new())); - return; - } - _ => {} - } - } - fut_box.set_result(Box::new(StringBox::new(""))); - return; - } - }), - ); - handle as i64 -} - -// ---- Handle-based birth shims for AOT/JIT object linkage ---- -// These resolve symbols like "nyash.string.birth_h" referenced by ObjectModule. - -// Generic birth by type_id -> handle (no args). Exported as nyash.box.birth_h -#[export_name = "nyash.box.birth_h"] -pub extern "C" fn nyash_box_birth_h_export(type_id: i64) -> i64 { - if type_id <= 0 { - return 0; - } - let tid = type_id as u32; - // Map type_id back to type name - let name_opt = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host() - .read() - .ok() - .and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())) - .and_then(|m| m.into_iter().find(|(_k, v)| *v == tid).map(|(k, _v)| k)); - if let Some(box_type) = name_opt { - if let Ok(host_g) = nyash_rust::runtime::get_global_plugin_host().read() { - if let Ok(b) = host_g.create_box(&box_type, &[]) { - let arc: std::sync::Arc = - std::sync::Arc::from(b); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!( - "nyrt: birth_h {} (type_id={}) -> handle={}", - box_type, tid, h - ); - } - return h as i64; - } else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!( - "nyrt: birth_h {} (type_id={}) FAILED: create_box", - box_type, tid - ); - } - } - } else if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("nyrt: birth_h (type_id={}) FAILED: type map not found", tid); - } - 0 -} -// Generic birth with args: (type_id, argc, a1, a2) -> handle -// Export name: nyash.box.birth_i64 -#[export_name = "nyash.box.birth_i64"] -pub extern "C" fn nyash_box_birth_i64_export(type_id: i64, argc: i64, a1: i64, a2: i64) -> i64 { - use nyash_rust::runtime::plugin_loader_v2::PluginBoxV2; - if type_id <= 0 { - return 0; - } - let mut invoke: Option< - unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32, - > = None; - // Resolve invoke_fn via temporary instance - let box_type_name = nyash_rust::runtime::plugin_loader_unified::get_global_plugin_host() - .read() - .ok() - .and_then(|h| h.config_ref().map(|cfg| cfg.box_types.clone())) - .and_then(|m| { - m.into_iter() - .find(|(_k, v)| *v == (type_id as u32)) - .map(|(k, _v)| k) - }) - .unwrap_or_else(|| "PluginBox".to_string()); - if let Ok(host_g) = nyash_rust::runtime::get_global_plugin_host().read() { - if let Ok(b) = host_g.create_box(&box_type_name, &[]) { - if let Some(p) = b.as_any().downcast_ref::() { - invoke = Some(p.inner.invoke_fn); - } - } - } - if invoke.is_none() { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("nyrt: birth_i64 (type_id={}) FAILED: no invoke", type_id); - } - return 0; - } - let method_id: u32 = 0; // birth - let instance_id: u32 = 0; // static - // Build TLV args - use nyash_rust::jit::rt::handles; - let nargs = argc.max(0) as usize; - let mut buf = nyash_rust::runtime::plugin_ffi_common::encode_tlv_header(nargs as u16); - let mut encode_handle = |h: i64| { - if h > 0 { - if let Some(obj) = handles::get(h as u64) { - if let Some(p) = obj.as_any().downcast_ref::() { - let host = nyash_rust::runtime::get_global_plugin_host(); - if let Ok(hg) = host.read() { - if p.box_type == "StringBox" { - if let Ok(Some(sb)) = hg.invoke_instance_method( - "StringBox", - "toUtf8", - p.instance_id(), - &[], - ) { - if let Some(s) = sb - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::string( - &mut buf, &s.value, - ); - return; - } - } - } else if p.box_type == "IntegerBox" { - if let Ok(Some(ibx)) = - hg.invoke_instance_method("IntegerBox", "get", p.instance_id(), &[]) - { - if let Some(i) = ibx - .as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::i64( - &mut buf, i.value, - ); - return; - } - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - p.inner.type_id, - p.instance_id(), - ); - return; - } - } - } - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, h); - }; - if nargs >= 1 { - encode_handle(a1); - } - if nargs >= 2 { - encode_handle(a2); - } - // Extra birth args from legacy VM when present - if nargs > 2 && std::env::var("NYASH_JIT_ARGS_HANDLE_ONLY").ok().as_deref() != Some("1") { - for pos in 3..=nargs { - nyash_rust::jit::rt::with_legacy_vm_args(|args| { - if let Some(v) = args.get(pos) { - use nyash_rust::backend::vm::VMValue as V; - match v { - V::String(s) => { - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, s) - } - V::Integer(i) => { - nyash_rust::runtime::plugin_ffi_common::encode::i64(&mut buf, *i) - } - V::Float(f) => { - nyash_rust::runtime::plugin_ffi_common::encode::f64(&mut buf, *f) - } - V::Bool(b) => { - nyash_rust::runtime::plugin_ffi_common::encode::bool(&mut buf, *b) - } - V::BoxRef(bx) => { - if let Some(pb) = bx.as_any().downcast_ref::() { - if let Some(bufbox) = - bx.as_any() - .downcast_ref::() - { - nyash_rust::runtime::plugin_ffi_common::encode::bytes( - &mut buf, - &bufbox.to_vec(), - ); - } else { - nyash_rust::runtime::plugin_ffi_common::encode::plugin_handle( - &mut buf, - pb.inner.type_id, - pb.instance_id(), - ); - } - } else { - let s = bx.to_string_box().value; - nyash_rust::runtime::plugin_ffi_common::encode::string(&mut buf, &s) - } - } - _ => {} - } - } - }); - } - } - let mut out = vec![0u8; 1024]; - let mut out_len: usize = out.len(); - let rc = unsafe { - invoke.unwrap()( - type_id as u32, - method_id, - instance_id, - buf.as_ptr(), - buf.len(), - out.as_mut_ptr(), - &mut out_len, - ) - }; - if rc != 0 { - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!( - "nyrt: birth_i64 (type_id={}) FAILED: invoke rc={}", - type_id, rc - ); - } - return 0; - } - if let Some((tag, _sz, payload)) = - nyash_rust::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) - { - if tag == 8 && payload.len() == 8 { - let mut t = [0u8; 4]; - t.copy_from_slice(&payload[0..4]); - let mut i = [0u8; 4]; - i.copy_from_slice(&payload[4..8]); - let r_type = u32::from_le_bytes(t); - let r_inst = u32::from_le_bytes(i); - let pb = nyash_rust::runtime::plugin_loader_v2::make_plugin_box_v2( - box_type_name.clone(), - r_type, - r_inst, - invoke.unwrap(), - ); - let arc: std::sync::Arc = std::sync::Arc::new(pb); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - println!( - "nyrt: birth_i64 {} (type_id={}) argc={} -> handle={}", - box_type_name, type_id, nargs, h - ); - } - return h as i64; - } - } - if std::env::var("NYASH_CLI_VERBOSE").ok().as_deref() == Some("1") { - eprintln!("nyrt: birth_i64 (type_id={}) FAILED: decode", type_id); - } - 0 -} - -// ---- String helpers for LLVM lowering ---- -// Exported as: nyash_string_new(i8* ptr, i32 len) -> i8* -#[no_mangle] -pub extern "C" fn nyash_string_new(ptr: *const u8, len: i32) -> *mut i8 { - use std::ptr; - if ptr.is_null() || len < 0 { - return std::ptr::null_mut(); - } - let n = len as usize; - // Allocate n+1 and null-terminate for C interop (puts, etc.) - let mut buf = Vec::::with_capacity(n + 1); - unsafe { - ptr::copy_nonoverlapping(ptr, buf.as_mut_ptr(), n); - buf.set_len(n); - } - buf.push(0); - let boxed = buf.into_boxed_slice(); - let raw = Box::into_raw(boxed) as *mut u8; - raw as *mut i8 -} - -// ---- Unified semantics shims (handle-based) ---- -// Exported as: nyash.semantics.add_hh(i64 lhs_handle, i64 rhs_handle) -> i64 (NyashBox handle) -#[export_name = "nyash.semantics.add_hh"] -pub extern "C" fn nyash_semantics_add_hh_export(lhs_h: i64, rhs_h: i64) -> i64 { - use nyash_rust::jit::rt::handles; - use nyash_rust::{ - box_trait::{IntegerBox, StringBox}, - runtime::semantics, - }; - if lhs_h <= 0 || rhs_h <= 0 { - return 0; - } - let lhs = if let Some(obj) = handles::get(lhs_h as u64) { - obj - } else { - return 0; - }; - let rhs = if let Some(obj) = handles::get(rhs_h as u64) { - obj - } else { - return 0; - }; - let ls_opt = semantics::coerce_to_string(lhs.as_ref()); - let rs_opt = semantics::coerce_to_string(rhs.as_ref()); - if ls_opt.is_some() || rs_opt.is_some() { - let ls = ls_opt.unwrap_or_else(|| lhs.to_string_box().value); - let rs = rs_opt.unwrap_or_else(|| rhs.to_string_box().value); - let s = format!("{}{}", ls, rs); - let arc: std::sync::Arc = - std::sync::Arc::new(StringBox::new(s)); - return handles::to_handle(arc) as i64; - } - if let (Some(li), Some(ri)) = ( - semantics::coerce_to_i64(lhs.as_ref()), - semantics::coerce_to_i64(rhs.as_ref()), - ) { - let arc: std::sync::Arc = - std::sync::Arc::new(IntegerBox::new(li + ri)); - return handles::to_handle(arc) as i64; - } - // Fallback: stringify both and concat to preserve total order - let ls = lhs.to_string_box().value; - let rs = rhs.to_string_box().value; - let arc: std::sync::Arc = - std::sync::Arc::new(StringBox::new(format!("{}{}", ls, rs))); - handles::to_handle(arc) as i64 -} - -// ---- Array helpers for LLVM lowering (handle-based) ---- -// Exported as: nyash_array_get_h(i64 handle, i64 idx) -> i64 -#[no_mangle] -pub extern "C" fn nyash_array_get_h(handle: i64, idx: i64) -> i64 { - use nyash_rust::{box_trait::IntegerBox, jit::rt::handles}; - if handle <= 0 || idx < 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(arr) = obj - .as_any() - .downcast_ref::() - { - let val = arr.get(Box::new(IntegerBox::new(idx))); - if let Some(ib) = val.as_any().downcast_ref::() { - return ib.value; - } - } - } - 0 -} - -// ---- ExternCall helpers for LLVM lowering ---- -// Exported as: nyash.console.log(i8* cstr) -> i64 -#[export_name = "nyash.console.log"] -pub extern "C" fn nyash_console_log_export(ptr: *const i8) -> i64 { - if ptr.is_null() { - return 0; - } - unsafe { - let c = std::ffi::CStr::from_ptr(ptr); - if let Ok(s) = c.to_str() { - println!("{}", s); - } - } - 0 -} - -// Exported as: nyash.console.log_handle(i64 handle) -> i64 -#[export_name = "nyash.console.log_handle"] -pub extern "C" fn nyash_console_log_handle(handle: i64) -> i64 { - use nyash_rust::jit::rt::handles; - eprintln!("DEBUG: handle={}", handle); - if let Some(obj) = handles::get(handle as u64) { - let s = obj.to_string_box().value; - println!("{}", s); - } else { - eprintln!("DEBUG: handle {} not found in registry", handle); - println!("{}", handle); - } - 0 -} - -// Exported as: nyash.console.warn_handle(i64 handle) -> i64 -#[export_name = "nyash.console.warn_handle"] -pub extern "C" fn nyash_console_warn_handle(handle: i64) -> i64 { - if handle <= 0 { - return 0; - } - - if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) { - let s = obj.to_string_box().value; - eprintln!("WARN: {}", s); - } else { - eprintln!("WARN: {}", handle); - } - 0 -} - -// Exported as: nyash.console.error_handle(i64 handle) -> i64 -#[export_name = "nyash.console.error_handle"] -pub extern "C" fn nyash_console_error_handle(handle: i64) -> i64 { - if handle <= 0 { - return 0; - } - - if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) { - let s = obj.to_string_box().value; - eprintln!("ERROR: {}", s); - } else { - eprintln!("ERROR: {}", handle); - } - 0 -} - -// Exported as: nyash.debug.trace_handle(i64 handle) -> i64 -#[export_name = "nyash.debug.trace_handle"] -pub extern "C" fn nyash_debug_trace_handle(handle: i64) -> i64 { - if handle <= 0 { - return 0; - } - - if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) { - let s = obj.to_string_box().value; - eprintln!("TRACE: {}", s); - } else { - eprintln!("TRACE: {}", handle); - } - 0 -} - -// Exported as: nyash.console.warn(i8* cstr) -> i64 -#[export_name = "nyash.console.warn"] -pub extern "C" fn nyash_console_warn_export(ptr: *const i8) -> i64 { - if ptr.is_null() { - return 0; - } - unsafe { - let c = std::ffi::CStr::from_ptr(ptr); - if let Ok(s) = c.to_str() { - eprintln!("[warn] {}", s); - } - } - 0 -} - -// Exported as: nyash.console.error(i8* cstr) -> i64 -#[export_name = "nyash.console.error"] -pub extern "C" fn nyash_console_error_export(ptr: *const i8) -> i64 { - if ptr.is_null() { - return 0; - } - unsafe { - let c = std::ffi::CStr::from_ptr(ptr); - if let Ok(s) = c.to_str() { - eprintln!("[error] {}", s); - } - } - 0 -} - -// Exported as: nyash.debug.trace(i8* cstr) -> i64 -#[export_name = "nyash.debug.trace"] -pub extern "C" fn nyash_debug_trace_export(ptr: *const i8) -> i64 { - if ptr.is_null() { - return 0; - } - unsafe { - let c = std::ffi::CStr::from_ptr(ptr); - if let Ok(s) = c.to_str() { - eprintln!("[trace] {}", s); - } - } - 0 -} - -// Exported as: nyash.console.readline() -> i8* -#[export_name = "nyash.console.readline"] -pub extern "C" fn nyash_console_readline_export() -> *mut i8 { - use std::io; - // Read a line from stdin; normalize to UTF-8 and strip trailing CR/LF - let mut input = String::new(); - // Use read_to_end if stdin is not a TTY? Simpler: read_line through BufRead - // For simplicity, read from stdin into buffer until newline or EOF - let mut buf = String::new(); - let mut handle = io::stdin(); - // On failure or EOF, return empty string - match io::stdin().read_line(&mut buf) { - Ok(_n) => { - input = buf; - } - Err(_) => { - input.clear(); - } - } - while input.ends_with('\n') || input.ends_with('\r') { - input.pop(); - } - // Allocate C string (null-terminated) - let mut bytes = input.into_bytes(); - bytes.push(0); - let boxed = bytes.into_boxed_slice(); - let raw = Box::into_raw(boxed) as *mut u8; - raw as *mut i8 -} - -// ---- String concat helpers for LLVM lowering ---- -// Exported as: nyash.string.concat_ss(i8* a, i8* b) -> i8* -#[export_name = "nyash.string.concat_ss"] -pub extern "C" fn nyash_string_concat_ss(a: *const i8, b: *const i8) -> *mut i8 { - let mut s = String::new(); - unsafe { - if !a.is_null() { - if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() { - s.push_str(sa); - } - } - if !b.is_null() { - if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() { - s.push_str(sb); - } - } - } - let mut bytes = s.into_bytes(); - bytes.push(0); - let boxed = bytes.into_boxed_slice(); - let raw = Box::into_raw(boxed) as *mut u8; - raw as *mut i8 -} - -// Exported as: nyash.string.concat_si(i8* a, i64 b) -> i8* -#[export_name = "nyash.string.concat_si"] -pub extern "C" fn nyash_string_concat_si(a: *const i8, b: i64) -> *mut i8 { - let mut s = String::new(); - unsafe { - if !a.is_null() { - if let Ok(sa) = std::ffi::CStr::from_ptr(a).to_str() { - s.push_str(sa); - } - } - } - s.push_str(&b.to_string()); - let mut bytes = s.into_bytes(); - bytes.push(0); - let boxed = bytes.into_boxed_slice(); - let raw = Box::into_raw(boxed) as *mut u8; - raw as *mut i8 -} - -// Exported as: nyash.string.concat_is(i64 a, i8* b) -> i8* -#[export_name = "nyash.string.concat_is"] -pub extern "C" fn nyash_string_concat_is(a: i64, b: *const i8) -> *mut i8 { - let mut s = a.to_string(); - unsafe { - if !b.is_null() { - if let Ok(sb) = std::ffi::CStr::from_ptr(b).to_str() { - s.push_str(sb); - } - } - } - let mut bytes = s.into_bytes(); - bytes.push(0); - let boxed = bytes.into_boxed_slice(); - let raw = Box::into_raw(boxed) as *mut u8; - raw as *mut i8 -} - -// ---- Instance field helpers for LLVM lowering (handle-based) ---- -// Exported as: nyash.instance.get_field_h(i64 handle, i8* name) -> i64 -#[export_name = "nyash.instance.get_field_h"] -pub extern "C" fn nyash_instance_get_field_h(handle: i64, name: *const i8) -> i64 { - if handle <= 0 || name.is_null() { - return 0; - } - let name = unsafe { std::ffi::CStr::from_ptr(name) }; - let Ok(field) = name.to_str() else { return 0 }; - if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) { - if let Some(inst) = obj - .as_any() - .downcast_ref::() - { - if let Some(shared) = inst.get_field(field) { - let arc: std::sync::Arc = - std::sync::Arc::from(shared); - let h = nyash_rust::jit::rt::handles::to_handle(arc); - return h as i64; - } - } - } - 0 -} - -// Exported as: nyash.instance.set_field_h(i64 handle, i8* name, i64 val_h) -> i64 -#[export_name = "nyash.instance.set_field_h"] -pub extern "C" fn nyash_instance_set_field_h(handle: i64, name: *const i8, val_h: i64) -> i64 { - if handle <= 0 || name.is_null() { - return 0; - } - let name = unsafe { std::ffi::CStr::from_ptr(name) }; - let Ok(field) = name.to_str() else { return 0 }; - if let Some(obj) = nyash_rust::jit::rt::handles::get(handle as u64) { - if let Some(inst) = obj - .as_any() - .downcast_ref::() - { - if val_h > 0 { - if let Some(val) = nyash_rust::jit::rt::handles::get(val_h as u64) { - let shared: nyash_rust::box_trait::SharedNyashBox = std::sync::Arc::clone(&val); - let _ = inst.set_field(field, shared); - return 0; - } - } - } - } - 0 -} - -// Exported as: nyash_array_set_h(i64 handle, i64 idx, i64 val) -> i64 -#[no_mangle] -pub extern "C" fn nyash_array_set_h(handle: i64, idx: i64, val: i64) -> i64 { - use nyash_rust::{box_trait::IntegerBox, jit::rt::handles}; - if handle <= 0 || idx < 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(arr) = obj - .as_any() - .downcast_ref::() - { - let i = idx as usize; - let len = arr.len(); - if i < len { - let _ = arr.set( - Box::new(IntegerBox::new(idx)), - Box::new(IntegerBox::new(val)), - ); - } else if i == len { - let _ = arr.push(Box::new(IntegerBox::new(val))); - } else { - // Do nothing for gaps (keep behavior conservative) - } - return 0; - } - } - 0 -} - -// Exported as: nyash_array_push_h(i64 handle, i64 val) -> i64 (returns new length) -#[no_mangle] -pub extern "C" fn nyash_array_push_h(handle: i64, val: i64) -> i64 { - use nyash_rust::{ - box_trait::{IntegerBox, NyashBox}, - jit::rt::handles, - }; - if handle <= 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(arr) = obj - .as_any() - .downcast_ref::() - { - // If val is handle, try to use it; otherwise treat as integer - let vbox: Box = if val > 0 { - if let Some(o) = handles::get(val as u64) { - o.clone_box() - } else { - Box::new(IntegerBox::new(val)) - } - } else { - Box::new(IntegerBox::new(val)) - }; - let _ = arr.push(vbox); - return arr.len() as i64; - } - } - 0 -} - -// Exported as: nyash_array_length_h(i64 handle) -> i64 -#[no_mangle] -pub extern "C" fn nyash_array_length_h(handle: i64) -> i64 { - use nyash_rust::jit::rt::handles; - if handle <= 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(arr) = obj - .as_any() - .downcast_ref::() - { - return arr.len() as i64; - } - } - 0 -} - -// --- AOT ObjectModule dotted-name aliases (Array) --- -// Provide dotted symbol names expected by ObjectBuilder lowering, forwarding to existing underscored exports. -#[export_name = "nyash.array.get_h"] -pub extern "C" fn nyash_array_get_h_alias(handle: i64, idx: i64) -> i64 { - nyash_array_get_h(handle, idx) -} - -#[export_name = "nyash.array.set_h"] -pub extern "C" fn nyash_array_set_h_alias(handle: i64, idx: i64, val: i64) -> i64 { - nyash_array_set_h(handle, idx, val) -} - -#[export_name = "nyash.array.push_h"] -pub extern "C" fn nyash_array_push_h_alias(handle: i64, val: i64) -> i64 { - nyash_array_push_h(handle, val) -} - -#[export_name = "nyash.array.len_h"] -pub extern "C" fn nyash_array_len_h_alias(handle: i64) -> i64 { - nyash_array_length_h(handle) -} - -// --- AOT ObjectModule dotted-name exports (Map) --- -// Provide dotted symbol names expected by ObjectBuilder lowering for MapBox operations. -// size: (handle) -> i64 -#[export_name = "nyash.map.size_h"] -pub extern "C" fn nyash_map_size_h(handle: i64) -> i64 { - use nyash_rust::jit::rt::handles; - if handle <= 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(map) = obj - .as_any() - .downcast_ref::() - { - if let Some(ib) = map - .size() - .as_any() - .downcast_ref::() - { - return ib.value; - } - } - } - 0 -} - -// get_h: (map_handle, key_i64) -> value_handle -#[export_name = "nyash.map.get_h"] -pub extern "C" fn nyash_map_get_h(handle: i64, key: i64) -> i64 { - use nyash_rust::{ - box_trait::{IntegerBox, NyashBox}, - jit::rt::handles, - }; - if handle <= 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(map) = obj - .as_any() - .downcast_ref::() - { - let kbox: Box = Box::new(IntegerBox::new(key)); - let v = map.get(kbox); - let arc: std::sync::Arc = std::sync::Arc::from(v); - let h = handles::to_handle(arc); - return h as i64; - } - } - 0 -} - -// get_hh: (map_handle, key_handle) -> value_handle -#[export_name = "nyash.map.get_hh"] -pub extern "C" fn nyash_map_get_hh(handle: i64, key_h: i64) -> i64 { - use nyash_rust::{box_trait::NyashBox, jit::rt::handles}; - if handle <= 0 || key_h <= 0 { - return 0; - } - if let (Some(obj), Some(key)) = (handles::get(handle as u64), handles::get(key_h as u64)) { - if let Some(map) = obj - .as_any() - .downcast_ref::() - { - let v = map.get(key.clone_box()); - let arc: std::sync::Arc = std::sync::Arc::from(v); - let h = handles::to_handle(arc); - return h as i64; - } - } - 0 -} - -// set_h: (map_handle, key_i64, val) -> i64 (ignored/0) -#[export_name = "nyash.map.set_h"] -pub extern "C" fn nyash_map_set_h(handle: i64, key: i64, val: i64) -> i64 { - use nyash_rust::{ - box_trait::{IntegerBox, NyashBox}, - jit::rt::handles, - }; - if handle <= 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(map) = obj - .as_any() - .downcast_ref::() - { - let kbox: Box = Box::new(IntegerBox::new(key)); - let vbox: Box = if val > 0 { - if let Some(o) = handles::get(val as u64) { - o.clone_box() - } else { - Box::new(IntegerBox::new(val)) - } - } else { - Box::new(IntegerBox::new(val)) - }; - let _ = map.set(kbox, vbox); - return 0; - } - } - 0 -} - -// has_h: (map_handle, key_i64) -> i64 (0/1) -#[export_name = "nyash.map.has_h"] -pub extern "C" fn nyash_map_has_h(handle: i64, key: i64) -> i64 { - use nyash_rust::{box_trait::IntegerBox, jit::rt::handles}; - if handle <= 0 { - return 0; - } - if let Some(obj) = handles::get(handle as u64) { - if let Some(map) = obj - .as_any() - .downcast_ref::() - { - let kbox = Box::new(IntegerBox::new(key)); - let v = map.get(kbox); - // Consider present if not VoidBox - let present = !v.as_any().is::(); - return if present { 1 } else { 0 }; - } - } - 0 -} diff --git a/src/backend/llvm/compiler/codegen/instructions.rs b/src/backend/llvm/compiler/codegen/instructions.rs index 8c74e24b..335d8e7b 100644 --- a/src/backend/llvm/compiler/codegen/instructions.rs +++ b/src/backend/llvm/compiler/codegen/instructions.rs @@ -508,56 +508,84 @@ pub(super) fn lower_externcall<'ctx>( || (iface_name == "env.debug" && method_name == "trace") { if args.len() != 1 { - return Err(format!("{}.{} expects 1 arg (handle)", iface_name, method_name)); + return Err(format!("{}.{} expects 1 arg", iface_name, method_name)); } let av = *vmap.get(&args[0]).ok_or("extern arg missing")?; - let arg_val = match av { - BVE::IntValue(iv) => { - if iv.get_type() == codegen.context.bool_type() { - codegen - .builder - .build_int_z_extend(iv, codegen.context.i64_type(), "bool2i64") - .map_err(|e| e.to_string())? - } else if iv.get_type() == codegen.context.i64_type() { - iv + match av { + // If argument is i8* (string), call string variant + BVE::PointerValue(pv) => { + let i8p = codegen.context.ptr_type(AddressSpace::from(0)); + let fnty = codegen.context.i64_type().fn_type(&[i8p.into()], false); + let fname = if iface_name == "env.console" { + match method_name { + "log" => "nyash.console.log", + "warn" => "nyash.console.warn", + _ => "nyash.console.error", + } } else { - codegen - .builder - .build_int_s_extend(iv, codegen.context.i64_type(), "int2i64") - .map_err(|e| e.to_string())? + "nyash.debug.trace" + }; + let callee = codegen + .module + .get_function(fname) + .unwrap_or_else(|| codegen.module.add_function(fname, fnty, None)); + let _ = codegen + .builder + .build_call(callee, &[pv.into()], "console_log_p") + .map_err(|e| e.to_string())?; + if let Some(d) = dst { + vmap.insert(*d, codegen.context.i64_type().const_zero().into()); } + return Ok(()); } - BVE::PointerValue(pv) => codegen - .builder - .build_ptr_to_int(pv, codegen.context.i64_type(), "p2i") - .map_err(|e| e.to_string())?, - _ => return Err("console.log arg conversion failed".to_string()), - }; - let fnty = codegen - .context - .i64_type() - .fn_type(&[codegen.context.i64_type().into()], false); - let fname = if iface_name == "env.console" { - match method_name { - "log" => "nyash.console.log_handle", - "warn" => "nyash.console.warn_handle", - _ => "nyash.console.error_handle", + // Otherwise, convert to i64 and call handle variant + _ => { + let arg_val = match av { + BVE::IntValue(iv) => { + if iv.get_type() == codegen.context.bool_type() { + codegen + .builder + .build_int_z_extend(iv, codegen.context.i64_type(), "bool2i64") + .map_err(|e| e.to_string())? + } else if iv.get_type() == codegen.context.i64_type() { + iv + } else { + codegen + .builder + .build_int_s_extend(iv, codegen.context.i64_type(), "int2i64") + .map_err(|e| e.to_string())? + } + } + BVE::PointerValue(_) => unreachable!(), + _ => return Err("console.log arg conversion failed".to_string()), + }; + let fnty = codegen + .context + .i64_type() + .fn_type(&[codegen.context.i64_type().into()], false); + let fname = if iface_name == "env.console" { + match method_name { + "log" => "nyash.console.log_handle", + "warn" => "nyash.console.warn_handle", + _ => "nyash.console.error_handle", + } + } else { + "nyash.debug.trace_handle" + }; + let callee = codegen + .module + .get_function(fname) + .unwrap_or_else(|| codegen.module.add_function(fname, fnty, None)); + let _ = codegen + .builder + .build_call(callee, &[arg_val.into()], "console_log_h") + .map_err(|e| e.to_string())?; + if let Some(d) = dst { + vmap.insert(*d, codegen.context.i64_type().const_zero().into()); + } + return Ok(()); } - } else { - "nyash.debug.trace_handle" - }; - let callee = codegen - .module - .get_function(fname) - .unwrap_or_else(|| codegen.module.add_function(fname, fnty, None)); - let _ = codegen - .builder - .build_call(callee, &[arg_val.into()], "console_log_h") - .map_err(|e| e.to_string())?; - if let Some(d) = dst { - vmap.insert(*d, codegen.context.i64_type().const_zero().into()); } - return Ok(()); } if iface_name == "env.console" && method_name == "readLine" { @@ -898,15 +926,11 @@ pub(super) fn lower_newbox<'ctx>( use inkwell::values::BasicValueEnum as BVE; match (box_type, args.len()) { ("StringBox", 1) => { + // Keep as i8* string pointer (AOT string fast-path) let av = *vmap.get(&args[0]).ok_or("StringBox arg missing")?; vmap.insert(dst, av); Ok(()) } - ("IntegerBox", 1) => { - let av = *vmap.get(&args[0]).ok_or("IntegerBox arg missing")?; - vmap.insert(dst, av); - Ok(()) - } (_, n) if n == 1 || n == 2 => { let type_id = *box_type_ids.get(box_type).unwrap_or(&0); let i64t = codegen.context.i64_type(); @@ -1043,6 +1067,51 @@ pub(super) fn lower_boxcall<'ctx>( 0 }; + // String concat fast-path (avoid plugin path for builtin StringBox) + if method == "concat" { + // Recognize receiver typed as String or StringBox + let is_string_recv = match func.metadata.value_types.get(box_val) { + Some(crate::mir::MirType::String) => true, + Some(crate::mir::MirType::Box(b)) if b == "StringBox" => true, + _ => false, + }; + if is_string_recv { + if args.len() != 1 { return Err("String.concat expects 1 arg".to_string()); } + // Prefer pointer-based concat to keep AOT string fast-path + let i8p = codegen.context.ptr_type(AddressSpace::from(0)); + let rhs_v = *vmap.get(&args[0]).ok_or("concat arg missing")?; + match (recv_v, rhs_v) { + (BVE::PointerValue(lp), BVE::PointerValue(rp)) => { + let fnty = i8p.fn_type(&[i8p.into(), i8p.into()], false); + let callee = codegen.module.get_function("nyash.string.concat_ss"). + unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_ss", fnty, None)); + let call = codegen.builder.build_call(callee, &[lp.into(), rp.into()], "concat_ss_call").map_err(|e| e.to_string())?; + if let Some(d) = dst { let rv = call.try_as_basic_value().left().ok_or("concat_ss returned void".to_string())?; vmap.insert(*d, rv); } + return Ok(()); + } + (BVE::PointerValue(lp), BVE::IntValue(ri)) => { + let i64t = codegen.context.i64_type(); + let fnty = i8p.fn_type(&[i8p.into(), i64t.into()], false); + let callee = codegen.module.get_function("nyash.string.concat_si"). + unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_si", fnty, None)); + let call = codegen.builder.build_call(callee, &[lp.into(), ri.into()], "concat_si_call").map_err(|e| e.to_string())?; + if let Some(d) = dst { let rv = call.try_as_basic_value().left().ok_or("concat_si returned void".to_string())?; vmap.insert(*d, rv); } + return Ok(()); + } + (BVE::IntValue(li), BVE::PointerValue(rp)) => { + let i64t = codegen.context.i64_type(); + let fnty = i8p.fn_type(&[i64t.into(), i8p.into()], false); + let callee = codegen.module.get_function("nyash.string.concat_is"). + unwrap_or_else(|| codegen.module.add_function("nyash.string.concat_is", fnty, None)); + let call = codegen.builder.build_call(callee, &[li.into(), rp.into()], "concat_is_call").map_err(|e| e.to_string())?; + if let Some(d) = dst { let rv = call.try_as_basic_value().left().ok_or("concat_is returned void".to_string())?; vmap.insert(*d, rv); } + return Ok(()); + } + _ => { /* fall through to generic path below */ } + } + } + } + // Array fast-paths if let Some(crate::mir::MirType::Box(bname)) = func.metadata.value_types.get(box_val) { if bname == "ArrayBox" && (method == "get" || method == "set" || method == "push" || method == "length") { @@ -1194,7 +1263,10 @@ pub(super) fn lower_boxcall<'ctx>( if let Some(mt) = func.metadata.value_types.get(d) { match mt { crate::mir::MirType::Integer | crate::mir::MirType::Bool => { vmap.insert(*d, rv); } - crate::mir::MirType::Box(_) | crate::mir::MirType::String | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown => { + // String: keep as i64 handle (do not cast to i8*) + crate::mir::MirType::String => { vmap.insert(*d, rv); } + // Box/Array/Future/Unknown: cast handle to opaque pointer + crate::mir::MirType::Box(_) | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown => { let h = if let BVE::IntValue(iv) = rv { iv } else { return Err("invoke ret expected i64".to_string()); }; let pty = codegen.context.ptr_type(AddressSpace::from(0)); let ptr = codegen.builder.build_int_to_ptr(h, pty, "ret_handle_to_ptr").map_err(|e| e.to_string())?; @@ -1234,7 +1306,8 @@ pub(super) fn lower_boxcall<'ctx>( if let Some(mt) = func.metadata.value_types.get(d) { match mt { crate::mir::MirType::Integer | crate::mir::MirType::Bool => { vmap.insert(*d, rv); } - crate::mir::MirType::Box(_) | crate::mir::MirType::String | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown => { + crate::mir::MirType::String => { vmap.insert(*d, rv); } + crate::mir::MirType::Box(_) | crate::mir::MirType::Array(_) | crate::mir::MirType::Future(_) | crate::mir::MirType::Unknown => { let h = if let BVE::IntValue(iv) = rv { iv } else { return Err("invoke ret expected i64".to_string()); }; let pty = codegen.context.ptr_type(AddressSpace::from(0)); let ptr = codegen.builder.build_int_to_ptr(h, pty, "ret_handle_to_ptr").map_err(|e| e.to_string())?; diff --git a/src/mir/builder.rs b/src/mir/builder.rs index cf14972f..570d8cb5 100644 --- a/src/mir/builder.rs +++ b/src/mir/builder.rs @@ -117,6 +117,13 @@ impl MirBuilder { ("StringBox", "length") | ("StringBox", "len") => Some(super::MirType::Integer), ("StringBox", "is_empty") => Some(super::MirType::Bool), ("StringBox", "charCodeAt") => Some(super::MirType::Integer), + // String-producing methods (important for LLVM ret handling) + ("StringBox", "substring") + | ("StringBox", "concat") + | ("StringBox", "replace") + | ("StringBox", "trim") + | ("StringBox", "toUpper") + | ("StringBox", "toLower") => Some(super::MirType::String), ("ArrayBox", "length") => Some(super::MirType::Integer), _ => None, }; @@ -854,7 +861,8 @@ impl MirBuilder { self.value_origin_newbox.insert(dst, class.clone()); // For plugin/builtin boxes, call birth(...). For user-defined boxes, skip (InstanceBox already constructed) - if !self.user_defined_boxes.contains(&class) { + // Special-case: StringBox is already fully constructed via from_i8_string in LLVM lowering; skip birth + if !self.user_defined_boxes.contains(&class) && class != "StringBox" { let birt_mid = resolve_slot_by_type_name(&class, "birth"); self.emit_box_or_plugin_call( None,