Phase 12: VM/JIT identical execution tests + host API slot routing
ChatGPT5による統一実行パス実装: - VM/JIT同一実行テスト追加(Array/Map/String/Instance) - host_api slot経由呼び出し(NYASH_JIT_HOST_BRIDGE=1) - extern_registry拡張(console系メソッドslot登録) - CI: vm-jit-identical.yml(STRICT/非STRICT両系テスト) - InstanceBox getField/setField slot 1,2統一 技術的改善: - JIT: ops_ext委譲による統一メソッド解決 - VM: vtable/PIC/名前ベースフォールバック階層 - host_bridge: TLV encode/decode BoxRef対応 - C ABI: nyrt_host_api.h外部公開ヘッダー テスト追加: - identical_exec_collections: Array/Map操作一致 - identical_exec_instance: ユーザー定義Box一致 - identical_exec_string: StringBox操作一致 - host_reverse_slot: 逆引きslot解決テスト 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -226,6 +226,9 @@ impl JitEngine {
|
||||
self.register_extern(hb::SYM_HOST_CONSOLE_LOG, Arc::new(|args| hb::console_log(args)));
|
||||
self.register_extern(hb::SYM_HOST_CONSOLE_WARN, Arc::new(|args| hb::console_warn(args)));
|
||||
self.register_extern(hb::SYM_HOST_CONSOLE_ERROR, Arc::new(|args| hb::console_error(args)));
|
||||
self.register_extern(hb::SYM_HOST_INSTANCE_GETFIELD, Arc::new(|args| hb::instance_getfield(args)));
|
||||
self.register_extern(hb::SYM_HOST_INSTANCE_SETFIELD, Arc::new(|args| hb::instance_setfield(args)));
|
||||
self.register_extern(hb::SYM_HOST_STRING_LEN, Arc::new(|args| hb::string_len(args)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
45
src/jit/extern/host_bridge.rs
vendored
45
src/jit/extern/host_bridge.rs
vendored
@ -13,7 +13,29 @@ fn tlv_encode_values(args: &[VMValue]) -> Vec<u8> {
|
||||
VMValue::Float(f) => enc::f64(&mut buf, *f),
|
||||
VMValue::Bool(b) => enc::bool(&mut buf, *b),
|
||||
VMValue::String(s) => enc::string(&mut buf, s),
|
||||
VMValue::BoxRef(_) | VMValue::Future(_) | VMValue::Void => enc::string(&mut buf, ""),
|
||||
VMValue::BoxRef(arc) => {
|
||||
// Try to downcast common primitives for stable TLV
|
||||
if let Some(sb) = arc.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
enc::string(&mut buf, &sb.value);
|
||||
} else if let Some(ib) = arc.as_any().downcast_ref::<crate::box_trait::IntegerBox>() {
|
||||
enc::i64(&mut buf, ib.value);
|
||||
} else if let Some(bb) = arc.as_any().downcast_ref::<crate::box_trait::BoolBox>() {
|
||||
enc::bool(&mut buf, bb.value);
|
||||
} else if let Some(fb) = arc.as_any().downcast_ref::<crate::boxes::math_box::FloatBox>() {
|
||||
enc::f64(&mut buf, fb.value);
|
||||
} else {
|
||||
// Fallback: send HostHandle so host can operate on it if needed
|
||||
let h = crate::runtime::host_handles::to_handle_arc(arc.clone());
|
||||
enc::host_handle(&mut buf, h);
|
||||
}
|
||||
}
|
||||
VMValue::Future(fu) => {
|
||||
let bx: Box<dyn crate::box_trait::NyashBox> = Box::new(fu.clone());
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::from(bx);
|
||||
let h = crate::runtime::host_handles::to_handle_arc(arc);
|
||||
enc::host_handle(&mut buf, h);
|
||||
}
|
||||
VMValue::Void => enc::string(&mut buf, "void"),
|
||||
}
|
||||
}
|
||||
buf
|
||||
@ -27,7 +49,12 @@ fn call_slot(handle: u64, slot: u64, argv: &[VMValue]) -> VMValue {
|
||||
if code != 0 { return VMValue::Void; }
|
||||
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
match tag {
|
||||
6|7 => VMValue::String(crate::runtime::plugin_ffi_common::decode::string(payload)),
|
||||
6|7 => {
|
||||
let s = crate::runtime::plugin_ffi_common::decode::string(payload);
|
||||
let sb = crate::box_trait::StringBox::new(&s);
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(sb);
|
||||
VMValue::BoxRef(arc)
|
||||
}
|
||||
1 => crate::runtime::plugin_ffi_common::decode::bool(payload).map(VMValue::Bool).unwrap_or(VMValue::Void),
|
||||
2 => crate::runtime::plugin_ffi_common::decode::i32(payload).map(|v| VMValue::Integer(v as i64)).unwrap_or(VMValue::Void),
|
||||
3 => crate::runtime::plugin_ffi_common::decode::u64(payload).map(|v| VMValue::Integer(v as i64)).unwrap_or(VMValue::Void),
|
||||
@ -61,6 +88,9 @@ pub const SYM_HOST_MAP_HAS: &str = "nyash.host.map.has"; // (MapBox, key)
|
||||
pub const SYM_HOST_CONSOLE_LOG: &str = "nyash.host.console.log"; // (value)
|
||||
pub const SYM_HOST_CONSOLE_WARN: &str = "nyash.host.console.warn"; // (value)
|
||||
pub const SYM_HOST_CONSOLE_ERROR: &str = "nyash.host.console.error"; // (value)
|
||||
pub const SYM_HOST_INSTANCE_GETFIELD: &str = "nyash.host.instance.getField"; // (InstanceBox, name)
|
||||
pub const SYM_HOST_INSTANCE_SETFIELD: &str = "nyash.host.instance.setField"; // (InstanceBox, name, value)
|
||||
pub const SYM_HOST_STRING_LEN: &str = "nyash.host.string.len"; // (StringBox)
|
||||
|
||||
pub fn array_get(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 100, &args[1..]) } else { VMValue::Void }
|
||||
@ -101,3 +131,14 @@ pub fn console_error(args: &[VMValue]) -> VMValue {
|
||||
if let Some(a0) = args.get(0) { eprintln!("[error] {}", a0.to_string()); }
|
||||
VMValue::Void
|
||||
}
|
||||
|
||||
pub fn instance_getfield(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 1, &args[1..]) } else { VMValue::Void }
|
||||
}
|
||||
pub fn instance_setfield(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 2, &args[1..]) } else { VMValue::Void }
|
||||
}
|
||||
|
||||
pub fn string_len(args: &[VMValue]) -> VMValue {
|
||||
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 300, &[]) } else { VMValue::Integer(0) }
|
||||
}
|
||||
|
||||
@ -30,6 +30,8 @@ pub trait IRBuilder {
|
||||
fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], _has_ret: bool, _ret_is_f64: bool) { }
|
||||
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, _has_ret: bool) { }
|
||||
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, _has_ret: bool) { }
|
||||
// Create a StringBox handle from a string literal and push its handle (i64) onto the stack.
|
||||
fn emit_string_handle_from_literal(&mut self, _s: &str) { }
|
||||
fn prepare_blocks(&mut self, _count: usize) { }
|
||||
fn switch_to_block(&mut self, _index: usize) { }
|
||||
fn seal_block(&mut self, _index: usize) { }
|
||||
@ -70,4 +72,3 @@ mod tls;
|
||||
pub(crate) use tls::clif_tls;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
mod rt_shims;
|
||||
|
||||
|
||||
@ -29,7 +29,7 @@ use super::super::extern_thunks::{
|
||||
nyash_box_birth_h, nyash_box_birth_i64,
|
||||
nyash_handle_of,
|
||||
nyash_rt_checkpoint, nyash_gc_barrier_write,
|
||||
nyash_console_birth_h,
|
||||
nyash_console_birth_h, nyash_string_from_ptr,
|
||||
};
|
||||
|
||||
use crate::jit::r#extern::r#async::nyash_future_await_h;
|
||||
@ -542,6 +542,33 @@ impl IRBuilder for CraneliftBuilder {
|
||||
});
|
||||
if let Some(v) = ret_val { self.value_stack.push(v); }
|
||||
}
|
||||
fn emit_string_handle_from_literal(&mut self, s: &str) {
|
||||
use cranelift_codegen::ir::{AbiParam, Signature, types};
|
||||
// Pack up to 16 bytes into two u64 words (little-endian)
|
||||
let bytes = s.as_bytes();
|
||||
let mut lo: u64 = 0; let mut hi: u64 = 0;
|
||||
let take = core::cmp::min(16, bytes.len());
|
||||
for i in 0..take.min(8) { lo |= (bytes[i] as u64) << (8 * i as u32); }
|
||||
for i in 8..take { hi |= (bytes[i] as u64) << (8 * (i - 8) as u32); }
|
||||
// Call thunk: nyash.string.from_u64x2(lo, hi, len) -> handle(i64)
|
||||
let call_conv = self.module.isa().default_call_conv();
|
||||
let mut sig = Signature::new(call_conv);
|
||||
sig.params.push(AbiParam::new(types::I64)); // lo
|
||||
sig.params.push(AbiParam::new(types::I64)); // hi
|
||||
sig.params.push(AbiParam::new(types::I64)); // len
|
||||
sig.returns.push(AbiParam::new(types::I64));
|
||||
let func_id = self.module.declare_function("nyash.string.from_u64x2", cranelift_module::Linkage::Import, &sig).expect("declare string.from_u64x2");
|
||||
let v = Self::with_fb(|fb| {
|
||||
let lo_v = fb.ins().iconst(types::I64, lo as i64);
|
||||
let hi_v = fb.ins().iconst(types::I64, hi as i64);
|
||||
let len_v = fb.ins().iconst(types::I64, bytes.len() as i64);
|
||||
let fref = self.module.declare_func_in_func(func_id, fb.func);
|
||||
let call_inst = fb.ins().call(fref, &[lo_v, hi_v, len_v]);
|
||||
fb.inst_results(call_inst).get(0).copied().expect("str.from_ptr ret")
|
||||
});
|
||||
self.value_stack.push(v);
|
||||
self.stats.0 += 1;
|
||||
}
|
||||
fn prepare_blocks(&mut self, count: usize) {
|
||||
// Allow being called before begin_function; stash desired count
|
||||
let mut need_tls = false;
|
||||
@ -724,6 +751,17 @@ impl CraneliftBuilder {
|
||||
builder.symbol("nyash_plugin_invoke3_f64", nyash_plugin_invoke3_f64 as *const u8);
|
||||
builder.symbol("nyash_plugin_invoke_name_getattr_i64", nyash_plugin_invoke_name_getattr_i64 as *const u8);
|
||||
builder.symbol("nyash_plugin_invoke_name_call_i64", nyash_plugin_invoke_name_call_i64 as *const u8);
|
||||
builder.symbol("nyash.string.from_u64x2", super::super::extern_thunks::nyash_string_from_u64x2 as *const u8);
|
||||
|
||||
// Host-bridge (by-slot) imports (opt-in)
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
use crate::jit::r#extern::host_bridge as hb;
|
||||
// Instance.getField/setField (recv_h, name_i[, val_i])
|
||||
builder.symbol(hb::SYM_HOST_INSTANCE_GETFIELD, super::super::extern_thunks::nyash_host_instance_getfield as *const u8);
|
||||
builder.symbol(hb::SYM_HOST_INSTANCE_SETFIELD, super::super::extern_thunks::nyash_host_instance_setfield as *const u8);
|
||||
// String.len (recv_h)
|
||||
builder.symbol(hb::SYM_HOST_STRING_LEN, super::super::extern_thunks::nyash_host_string_len as *const u8);
|
||||
}
|
||||
|
||||
let module = cranelift_jit::JITModule::new(builder);
|
||||
let ctx = cranelift_codegen::Context::new();
|
||||
|
||||
@ -27,8 +27,8 @@ impl IRBuilder for NoopBuilder {
|
||||
fn emit_host_call_typed(&mut self, _symbol: &str, _params: &[ParamKind], has_ret: bool, _ret_is_f64: bool) { if has_ret { self.consts += 1; } }
|
||||
fn emit_plugin_invoke(&mut self, _type_id: u32, _method_id: u32, _argc: usize, has_ret: bool) { if has_ret { self.consts += 1; } }
|
||||
fn emit_plugin_invoke_by_name(&mut self, _method: &str, _argc: usize, has_ret: bool) { if has_ret { self.consts += 1; } }
|
||||
fn emit_string_handle_from_literal(&mut self, _s: &str) { self.consts += 1; }
|
||||
fn ensure_local_i64(&mut self, _index: usize) { }
|
||||
fn store_local_i64(&mut self, _index: usize) { self.consts += 1; }
|
||||
fn load_local_i64(&mut self, _index: usize) { self.consts += 1; }
|
||||
}
|
||||
|
||||
|
||||
@ -285,6 +285,31 @@ impl LowerCore {
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2) StringBox(const string) → 文字列リテラルから直接ハンドル生成
|
||||
if box_type == "StringBox" && args.len() == 1 {
|
||||
if let Some(src) = args.get(0) {
|
||||
// 探索: 同一関数内で src を定義する Const(String)
|
||||
let mut lit: Option<String> = None;
|
||||
for (_bid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Const { dst: cdst, value } = ins {
|
||||
if cdst == src {
|
||||
if let crate::mir::ConstValue::String(s) = value { lit = Some(s.clone()); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if lit.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = lit {
|
||||
b.emit_string_handle_from_literal(&s);
|
||||
self.handle_values.insert(*dst);
|
||||
let slot = *self.local_index.entry(*dst).or_insert_with(|| { let id = self.next_local; self.next_local += 1; id });
|
||||
b.store_local_i64(slot);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2) 引数がハンドル(StringBox等)で既に存在する場合(最大2引数)
|
||||
if args.len() <= 2 && args.iter().all(|a| self.handle_values.contains(a)) {
|
||||
if let crate::jit::policy::invoke::InvokeDecision::PluginInvoke { type_id, .. } = crate::jit::policy::invoke::decide_box_method(box_type, "birth", args.len(), true) {
|
||||
|
||||
@ -63,12 +63,17 @@ impl LowerCore {
|
||||
args: &Vec<ValueId>,
|
||||
_func: &MirFunction,
|
||||
) -> Result<(), String> {
|
||||
// env.console.log/println → ConsoleBox に委譲(host-bridge有効時は直接ログ)
|
||||
if iface_name == "env.console" && (method_name == "log" || method_name == "println") {
|
||||
// env.console.log/warn/error/println → ConsoleBox に委譲(host-bridge有効時は直接ログ)
|
||||
if iface_name == "env.console" && (method_name == "log" || method_name == "println" || method_name == "warn" || method_name == "error") {
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
// a0: 先頭引数を最小限で積む
|
||||
if let Some(arg0) = args.get(0) { self.push_value_if_known_or_param(b, arg0); } else { b.emit_const_i64(0); }
|
||||
b.emit_host_call(crate::jit::r#extern::host_bridge::SYM_HOST_CONSOLE_LOG, 1, false);
|
||||
let sym = match method_name {
|
||||
"warn" => crate::jit::r#extern::host_bridge::SYM_HOST_CONSOLE_WARN,
|
||||
"error" => crate::jit::r#extern::host_bridge::SYM_HOST_CONSOLE_ERROR,
|
||||
_ => crate::jit::r#extern::host_bridge::SYM_HOST_CONSOLE_LOG,
|
||||
};
|
||||
b.emit_host_call(sym, 1, false);
|
||||
return Ok(());
|
||||
}
|
||||
// Ensure we have a Console handle (hostcall birth shim)
|
||||
@ -154,10 +159,7 @@ impl LowerCore {
|
||||
args: &Vec<ValueId>,
|
||||
dst: Option<ValueId>,
|
||||
) -> Result<bool, String> {
|
||||
// Delegate to existing helpers first
|
||||
if super::super::core_hostcall::lower_boxcall_simple_reads(b, &self.param_index, &self.known_i64, array, method, args, dst.clone()) {
|
||||
return Ok(true);
|
||||
}
|
||||
// Note: simple_reads は後段の分岐のフォールバックとして扱う(String/Instance優先)
|
||||
if matches!(method, "sin" | "cos" | "abs" | "min" | "max") {
|
||||
super::super::core_hostcall::lower_math_call(
|
||||
func,
|
||||
@ -200,6 +202,69 @@ impl LowerCore {
|
||||
}
|
||||
// Array/Map minimal handling
|
||||
match method {
|
||||
// Instance field ops via host-bridge
|
||||
"getField" | "setField" => {
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
// receiver: allow param/local/phi/known
|
||||
if let Some(v) = args.get(0) { let _ = v; } // keep args in scope
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
// name: if const string, build a StringBox handle from literal; else best-effort push
|
||||
if let Some(name_id) = args.get(0) {
|
||||
// Scan MIR for string constant defining this ValueId
|
||||
let mut found_str: Option<String> = None;
|
||||
for (_bbid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Const { dst, value } = ins {
|
||||
if dst == name_id {
|
||||
if let crate::mir::ConstValue::String(s) = value { found_str = Some(s.clone()); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if found_str.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = found_str { b.emit_string_handle_from_literal(&s); }
|
||||
else { self.push_value_if_known_or_param(b, name_id); }
|
||||
} else { b.emit_const_i64(0); }
|
||||
// value for setField
|
||||
let argc = if method == "setField" {
|
||||
if let Some(val_id) = args.get(1) {
|
||||
// If value is const string, materialize handle
|
||||
let mut found_val_str: Option<String> = None;
|
||||
for (_bbid, bb) in func.blocks.iter() {
|
||||
for ins in bb.instructions.iter() {
|
||||
if let crate::mir::MirInstruction::Const { dst, value } = ins {
|
||||
if dst == val_id {
|
||||
if let crate::mir::ConstValue::String(s) = value { found_val_str = Some(s.clone()); }
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if found_val_str.is_some() { break; }
|
||||
}
|
||||
if let Some(s) = found_val_str { b.emit_string_handle_from_literal(&s); }
|
||||
else { self.push_value_if_known_or_param(b, val_id); }
|
||||
} else { b.emit_const_i64(0); }
|
||||
3
|
||||
} else { 2 };
|
||||
let sym = if method == "setField" { crate::jit::r#extern::host_bridge::SYM_HOST_INSTANCE_SETFIELD } else { crate::jit::r#extern::host_bridge::SYM_HOST_INSTANCE_GETFIELD };
|
||||
b.emit_host_call(sym, argc, dst.is_some());
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
// String.len via host-bridge when receiver is StringBox
|
||||
"len" => {
|
||||
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||
if let Some(bt) = self.box_type_map.get(array) {
|
||||
if bt == "StringBox" {
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[LOWER]string.len via host-bridge"); }
|
||||
self.push_value_if_known_or_param(b, array);
|
||||
b.emit_host_call(crate::jit::r#extern::host_bridge::SYM_HOST_STRING_LEN, 1, dst.is_some());
|
||||
return Ok(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// Array length variants (length/len)
|
||||
"len" | "length" => {
|
||||
if let Ok(ph) = crate::runtime::plugin_loader_unified::get_global_plugin_host().read() {
|
||||
|
||||
@ -28,7 +28,7 @@ pub fn lower_array_get(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_size(
|
||||
pub fn lower_map_size_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
recv: &ValueId,
|
||||
@ -42,7 +42,7 @@ pub fn lower_map_size(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_get(
|
||||
pub fn lower_map_get_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
@ -59,7 +59,7 @@ pub fn lower_map_get(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_has(
|
||||
pub fn lower_map_has_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
@ -76,7 +76,7 @@ pub fn lower_map_has(
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_map_set(
|
||||
pub fn lower_map_set_simple(
|
||||
b: &mut dyn IRBuilder,
|
||||
param_index: &HashMap<ValueId, usize>,
|
||||
known_i64: &HashMap<ValueId, i64>,
|
||||
@ -257,10 +257,10 @@ pub fn lower_box_call(
|
||||
}
|
||||
}
|
||||
// Map
|
||||
"size" => { lower_map_size(b, param_index, recv, dst.is_some()); }
|
||||
"get" => { if let Some(k) = args.get(0) { lower_map_get(b, param_index, known_i64, recv, k, dst.is_some()); } }
|
||||
"has" => { if let Some(k) = args.get(0) { lower_map_has(b, param_index, known_i64, recv, k, dst.is_some()); } }
|
||||
"set" => { if args.len() >= 2 { lower_map_set(b, param_index, known_i64, recv, &args[0], &args[1]); } }
|
||||
"size" => { lower_map_size_simple(b, param_index, recv, dst.is_some()); }
|
||||
"get" => { if let Some(k) = args.get(0) { lower_map_get_simple(b, param_index, known_i64, recv, k, dst.is_some()); } }
|
||||
"has" => { if let Some(k) = args.get(0) { lower_map_has_simple(b, param_index, known_i64, recv, k, dst.is_some()); } }
|
||||
"set" => { if args.len() >= 2 { lower_map_set_simple(b, param_index, known_i64, recv, &args[0], &args[1]); } }
|
||||
"has" => {
|
||||
// Decide on key kind via registry and known values
|
||||
use crate::jit::hostcall_registry::{check_signature, ArgKind};
|
||||
|
||||
@ -7,6 +7,8 @@ use crate::jit::events;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::jit::r#extern::collections as c;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::jit::r#extern::host_bridge as hb;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::runtime::plugin_loader_unified;
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
use crate::runtime::plugin_loader_v2::PluginBoxV2;
|
||||
@ -656,3 +658,101 @@ pub(super) extern "C" fn nyash_semantics_add_hh(lhs_h: u64, rhs_h: u64) -> i64 {
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(StringBox::new(s));
|
||||
handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
// ==== Host-bridge (by-slot) external thunks ====
|
||||
// These thunks adapt JIT runtime handles and primitive args into VMValue vectors
|
||||
// and call the host-bridge helpers, which TLV-encode and invoke NyRT C-ABI by slot.
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
fn vmvalue_from_jit_arg_i64(v: i64) -> crate::backend::vm::VMValue {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
if v <= 0 { return V::Integer(v); }
|
||||
if let Some(obj) = crate::jit::rt::handles::get(v as u64) {
|
||||
return V::BoxRef(obj);
|
||||
}
|
||||
// Legacy fallback: allow small indices to refer into legacy VM args for string/name lookups
|
||||
if (v as u64) <= 16 {
|
||||
return crate::jit::rt::with_legacy_vm_args(|args| args.get(v as usize).cloned()).unwrap_or(V::Integer(v));
|
||||
}
|
||||
V::Integer(v)
|
||||
}
|
||||
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
fn i64_from_vmvalue(v: crate::backend::vm::VMValue) -> i64 {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
match v {
|
||||
V::Integer(i) => i,
|
||||
V::Bool(b) => if b { 1 } else { 0 },
|
||||
V::Float(f) => f as i64,
|
||||
V::String(s) => {
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(crate::box_trait::StringBox::new(&s));
|
||||
crate::jit::rt::handles::to_handle(arc) as i64
|
||||
}
|
||||
V::BoxRef(b) => crate::jit::rt::handles::to_handle(b) as i64,
|
||||
V::Future(fu) => {
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(fu);
|
||||
crate::jit::rt::handles::to_handle(arc) as i64
|
||||
}
|
||||
V::Void => 0,
|
||||
}
|
||||
}
|
||||
|
||||
// nyash.host.instance.getField(recv, name)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_host_instance_getfield(recv_h: u64, name_i: i64) -> i64 {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => return 0 };
|
||||
let name_v = vmvalue_from_jit_arg_i64(name_i);
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") {
|
||||
eprintln!("[HB|getField] name_i={} kind={}", name_i, match &name_v { V::String(_) => "String", V::BoxRef(b) => if b.as_any().downcast_ref::<crate::box_trait::StringBox>().is_some() {"StringBox"} else {"BoxRef"}, V::Integer(_) => "Integer", V::Bool(_) => "Bool", V::Float(_) => "Float", V::Void => "Void", V::Future(_) => "Future" });
|
||||
}
|
||||
let out = hb::instance_getfield(&[V::BoxRef(recv), name_v]);
|
||||
i64_from_vmvalue(out)
|
||||
}
|
||||
|
||||
// nyash.host.instance.setField(recv, name, value)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_host_instance_setfield(recv_h: u64, name_i: i64, val_i: i64) -> i64 {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => return 0 };
|
||||
let name_v = vmvalue_from_jit_arg_i64(name_i);
|
||||
let val_v = vmvalue_from_jit_arg_i64(val_i);
|
||||
let out = hb::instance_setfield(&[V::BoxRef(recv), name_v, val_v]);
|
||||
i64_from_vmvalue(out)
|
||||
}
|
||||
|
||||
// nyash.host.string.len(recv)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_host_string_len(recv_h: u64) -> i64 {
|
||||
use crate::backend::vm::VMValue as V;
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[HB|string.len] recv_h={}", recv_h); }
|
||||
let recv = match crate::jit::rt::handles::get(recv_h) { Some(a) => a, None => { if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref()==Some("1") { eprintln!("[HB|string.len] recv handle not found"); } return 0 } };
|
||||
let out = hb::string_len(&[V::BoxRef(recv)]);
|
||||
let ret = i64_from_vmvalue(out);
|
||||
if std::env::var("NYASH_JIT_TRACE_BRIDGE").ok().as_deref() == Some("1") { eprintln!("[HB|string.len] ret_i64={}", ret); }
|
||||
ret
|
||||
}
|
||||
|
||||
// Build a StringBox handle from raw bytes pointer and length
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_string_from_ptr(ptr: u64, len: u64) -> i64 {
|
||||
if ptr == 0 || len == 0 { return 0; }
|
||||
unsafe {
|
||||
let slice = std::slice::from_raw_parts(ptr as *const u8, len as usize);
|
||||
let s = match std::str::from_utf8(slice) { Ok(t) => t.to_string(), Err(_) => String::from_utf8_lossy(slice).to_string() };
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(crate::box_trait::StringBox::new(s));
|
||||
return crate::jit::rt::handles::to_handle(arc) as i64;
|
||||
}
|
||||
}
|
||||
|
||||
// Build a StringBox handle from two u64 chunks (little-endian) and length (<=16)
|
||||
#[cfg(feature = "cranelift-jit")]
|
||||
pub(super) extern "C" fn nyash_string_from_u64x2(lo: u64, hi: u64, len: i64) -> i64 {
|
||||
let n = if len <= 0 { 0usize } else { core::cmp::min(len as usize, 16usize) };
|
||||
let mut buf = [0u8; 16];
|
||||
for i in 0..core::cmp::min(8, n) { buf[i] = ((lo >> (8 * i)) & 0xFF) as u8; }
|
||||
if n > 8 { for i in 0..(n - 8) { buf[8 + i] = ((hi >> (8 * i)) & 0xFF) as u8; } }
|
||||
let s = match std::str::from_utf8(&buf[..n]) { Ok(t) => t.to_string(), Err(_) => String::from_utf8_lossy(&buf[..n]).to_string() };
|
||||
let arc: std::sync::Arc<dyn crate::box_trait::NyashBox> = std::sync::Arc::new(crate::box_trait::StringBox::new(s));
|
||||
crate::jit::rt::handles::to_handle(arc) as i64
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user