Phase 12: extern registry + diagnostics, JIT host-bridge PoC by-slot, vtable Map.set + VT/PIC tracing, and tests
This commit is contained in:
@ -646,12 +646,23 @@ impl VM {
|
|||||||
}
|
}
|
||||||
Err(_) => {
|
Err(_) => {
|
||||||
let strict = crate::config::env::extern_strict() || crate::config::env::abi_strict();
|
let strict = crate::config::env::extern_strict() || crate::config::env::abi_strict();
|
||||||
if strict {
|
// Build suggestion list
|
||||||
return Err(VMError::InvalidInstruction(format!("ExternCall STRICT: unregistered or unsupported call {}.{}", iface_name, method_name)));
|
let mut msg = String::new();
|
||||||
|
if strict { msg.push_str("ExternCall STRICT: unregistered or unsupported call "); } else { msg.push_str("ExternCall failed: "); }
|
||||||
|
msg.push_str(&format!("{}.{}", iface_name, method_name));
|
||||||
|
if let Some(spec) = crate::runtime::extern_registry::resolve(iface_name, method_name) {
|
||||||
|
msg.push_str(&format!(" (expected arity {}..{})", spec.min_arity, spec.max_arity));
|
||||||
} else {
|
} else {
|
||||||
return Err(VMError::InvalidInstruction(format!("ExternCall failed: {}.{}", iface_name, method_name)));
|
let known = crate::runtime::extern_registry::known_for_iface(iface_name);
|
||||||
|
if !known.is_empty() {
|
||||||
|
msg.push_str(&format!("; known methods: {}", known.join(", ")));
|
||||||
|
} else {
|
||||||
|
let ifaces = crate::runtime::extern_registry::all_ifaces();
|
||||||
|
msg.push_str(&format!("; known interfaces: {}", ifaces.join(", ")));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return Err(VMError::InvalidInstruction(msg));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Ok(ControlFlow::Continue)
|
Ok(ControlFlow::Continue)
|
||||||
}
|
}
|
||||||
@ -918,6 +929,7 @@ impl VM {
|
|||||||
if let VMValue::BoxRef(arc_box) = &recv {
|
if let VMValue::BoxRef(arc_box) = &recv {
|
||||||
if arc_box.as_any().downcast_ref::<crate::instance_v2::InstanceBox>().is_some() {
|
if arc_box.as_any().downcast_ref::<crate::instance_v2::InstanceBox>().is_some() {
|
||||||
if let Some(func_name) = self.try_poly_pic(&pic_key, &recv) {
|
if let Some(func_name) = self.try_poly_pic(&pic_key, &recv) {
|
||||||
|
if crate::config::env::vm_pic_trace() { eprintln!("[PIC] poly hit {}", pic_key); }
|
||||||
self.boxcall_hits_poly_pic = self.boxcall_hits_poly_pic.saturating_add(1);
|
self.boxcall_hits_poly_pic = self.boxcall_hits_poly_pic.saturating_add(1);
|
||||||
let mut vm_args = Vec::with_capacity(1 + args.len());
|
let mut vm_args = Vec::with_capacity(1 + args.len());
|
||||||
vm_args.push(recv.clone());
|
vm_args.push(recv.clone());
|
||||||
@ -928,6 +940,7 @@ impl VM {
|
|||||||
}
|
}
|
||||||
// Fallback to Mono-PIC (legacy) if present
|
// Fallback to Mono-PIC (legacy) if present
|
||||||
if let Some(func_name) = self.boxcall_pic_funcname.get(&pic_key).cloned() {
|
if let Some(func_name) = self.boxcall_pic_funcname.get(&pic_key).cloned() {
|
||||||
|
if crate::config::env::vm_pic_trace() { eprintln!("[PIC] mono hit {}", pic_key); }
|
||||||
self.boxcall_hits_mono_pic = self.boxcall_hits_mono_pic.saturating_add(1);
|
self.boxcall_hits_mono_pic = self.boxcall_hits_mono_pic.saturating_add(1);
|
||||||
// Build VM args: receiver first, then original args
|
// Build VM args: receiver first, then original args
|
||||||
let mut vm_args = Vec::with_capacity(1 + args.len());
|
let mut vm_args = Vec::with_capacity(1 + args.len());
|
||||||
@ -1131,10 +1144,11 @@ impl VM {
|
|||||||
if let Some(_tb) = crate::runtime::type_registry::resolve_typebox_by_name(ty_name) {
|
if let Some(_tb) = crate::runtime::type_registry::resolve_typebox_by_name(ty_name) {
|
||||||
// name+arity→slot 解決
|
// name+arity→slot 解決
|
||||||
let slot = crate::runtime::type_registry::resolve_slot_by_name(ty_name, _method, _args.len());
|
let slot = crate::runtime::type_registry::resolve_slot_by_name(ty_name, _method, _args.len());
|
||||||
// MapBox: size/len/has/get
|
// MapBox: size/len/has/get/set
|
||||||
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
if let Some(map) = b.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||||
if matches!(slot, Some(200|201)) {
|
if matches!(slot, Some(200|201)) {
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.size/len slot={:?}", slot); }
|
||||||
let out = map.size();
|
let out = map.size();
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
@ -1151,6 +1165,8 @@ impl VM {
|
|||||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||||
};
|
};
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.has"); }
|
||||||
let out = map.has(key_box);
|
let out = map.has(key_box);
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
@ -1168,16 +1184,48 @@ impl VM {
|
|||||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||||
};
|
};
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.get"); }
|
||||||
let out = map.get(key_box);
|
let out = map.get(key_box);
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if matches!(slot, Some(204)) {
|
||||||
|
if let (Ok(a0), Ok(a1)) = (self.get_value(_args[0]), self.get_value(_args[1])) {
|
||||||
|
let key_box: Box<dyn NyashBox> = match a0 {
|
||||||
|
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||||
|
VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||||
|
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||||
|
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||||
|
VMValue::BoxRef(ref bx) => bx.share_box(),
|
||||||
|
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||||
|
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||||
|
};
|
||||||
|
let val_box: Box<dyn NyashBox> = match a1 {
|
||||||
|
VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||||
|
VMValue::String(ref s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||||
|
VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||||
|
VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||||
|
VMValue::BoxRef(ref bx) => bx.share_box(),
|
||||||
|
VMValue::Future(ref fut) => Box::new(fut.clone()),
|
||||||
|
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||||
|
};
|
||||||
|
// Barrier: mutation
|
||||||
|
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Map.set");
|
||||||
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] MapBox.set"); }
|
||||||
|
let out = map.set(key_box, val_box);
|
||||||
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
|
return Some(Ok(ControlFlow::Continue));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// ArrayBox: get/set/len
|
// ArrayBox: get/set/len
|
||||||
if let Some(arr) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
if let Some(arr) = b.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||||
if matches!(slot, Some(102)) {
|
if matches!(slot, Some(102)) {
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.len"); }
|
||||||
let out = arr.length();
|
let out = arr.length();
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
@ -1192,6 +1240,8 @@ impl VM {
|
|||||||
VMValue::BoxRef(_) | VMValue::Future(_) | VMValue::Void => Box::new(crate::box_trait::IntegerBox::new(0)),
|
VMValue::BoxRef(_) | VMValue::Future(_) | VMValue::Void => Box::new(crate::box_trait::IntegerBox::new(0)),
|
||||||
};
|
};
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.get"); }
|
||||||
let out = arr.get(idx_box);
|
let out = arr.get(idx_box);
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
@ -1216,6 +1266,10 @@ impl VM {
|
|||||||
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||||
};
|
};
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
// Barrier: mutation
|
||||||
|
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Array.set");
|
||||||
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] ArrayBox.set"); }
|
||||||
let out = arr.set(idx_box, val_box);
|
let out = arr.set(idx_box, val_box);
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
@ -1226,6 +1280,7 @@ impl VM {
|
|||||||
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
if let Some(sb) = b.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||||
if matches!(slot, Some(300)) {
|
if matches!(slot, Some(300)) {
|
||||||
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
|
||||||
|
if crate::config::env::vm_vt_trace() { eprintln!("[VT] StringBox.len"); }
|
||||||
let out = crate::box_trait::IntegerBox::new(sb.value.len() as i64);
|
let out = crate::box_trait::IntegerBox::new(sb.value.len() as i64);
|
||||||
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); }
|
if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(Box::new(out))); }
|
||||||
return Some(Ok(ControlFlow::Continue));
|
return Some(Ok(ControlFlow::Continue));
|
||||||
|
|||||||
@ -99,6 +99,8 @@ pub fn gc_trace() -> bool { std::env::var("NYASH_GC_TRACE").ok().as_deref() == S
|
|||||||
pub fn gc_barrier_trace() -> bool { std::env::var("NYASH_GC_BARRIER_TRACE").ok().as_deref() == Some("1") }
|
pub fn gc_barrier_trace() -> bool { std::env::var("NYASH_GC_BARRIER_TRACE").ok().as_deref() == Some("1") }
|
||||||
pub fn runtime_checkpoint_trace() -> bool { std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") }
|
pub fn runtime_checkpoint_trace() -> bool { std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") }
|
||||||
pub fn vm_pic_stats() -> bool { std::env::var("NYASH_VM_PIC_STATS").ok().as_deref() == Some("1") }
|
pub fn vm_pic_stats() -> bool { std::env::var("NYASH_VM_PIC_STATS").ok().as_deref() == Some("1") }
|
||||||
|
pub fn vm_vt_trace() -> bool { std::env::var("NYASH_VM_VT_TRACE").ok().as_deref() == Some("1") }
|
||||||
|
pub fn vm_pic_trace() -> bool { std::env::var("NYASH_VM_PIC_TRACE").ok().as_deref() == Some("1") }
|
||||||
pub fn gc_barrier_strict() -> bool { std::env::var("NYASH_GC_BARRIER_STRICT").ok().as_deref() == Some("1") }
|
pub fn gc_barrier_strict() -> bool { std::env::var("NYASH_GC_BARRIER_STRICT").ok().as_deref() == Some("1") }
|
||||||
|
|
||||||
/// Return 0 (off) to 3 (max) for `NYASH_GC_TRACE`.
|
/// Return 0 (off) to 3 (max) for `NYASH_GC_TRACE`.
|
||||||
|
|||||||
@ -206,6 +206,7 @@ impl JitEngine {
|
|||||||
/// Register built-in externs (collections)
|
/// Register built-in externs (collections)
|
||||||
fn register_default_externs(&mut self) {
|
fn register_default_externs(&mut self) {
|
||||||
use crate::jit::r#extern::collections as c;
|
use crate::jit::r#extern::collections as c;
|
||||||
|
use crate::jit::r#extern::host_bridge as hb;
|
||||||
self.register_extern(c::SYM_ARRAY_LEN, Arc::new(|args| c::array_len(args)));
|
self.register_extern(c::SYM_ARRAY_LEN, Arc::new(|args| c::array_len(args)));
|
||||||
self.register_extern(c::SYM_ARRAY_GET, Arc::new(|args| c::array_get(args)));
|
self.register_extern(c::SYM_ARRAY_GET, Arc::new(|args| c::array_get(args)));
|
||||||
self.register_extern(c::SYM_ARRAY_SET, Arc::new(|args| c::array_set(args)));
|
self.register_extern(c::SYM_ARRAY_SET, Arc::new(|args| c::array_set(args)));
|
||||||
@ -213,6 +214,16 @@ impl JitEngine {
|
|||||||
self.register_extern(c::SYM_MAP_GET, Arc::new(|args| c::map_get(args)));
|
self.register_extern(c::SYM_MAP_GET, Arc::new(|args| c::map_get(args)));
|
||||||
self.register_extern(c::SYM_MAP_SET, Arc::new(|args| c::map_set(args)));
|
self.register_extern(c::SYM_MAP_SET, Arc::new(|args| c::map_set(args)));
|
||||||
self.register_extern(c::SYM_MAP_SIZE, Arc::new(|args| c::map_size(args)));
|
self.register_extern(c::SYM_MAP_SIZE, Arc::new(|args| c::map_size(args)));
|
||||||
|
// Host-bridge variants (by-slot via C symbol). Guarded by env opt-in for now.
|
||||||
|
if std::env::var("NYASH_JIT_HOST_BRIDGE").ok().as_deref() == Some("1") {
|
||||||
|
self.register_extern(hb::SYM_HOST_ARRAY_LEN, Arc::new(|args| hb::array_len(args)));
|
||||||
|
self.register_extern(hb::SYM_HOST_ARRAY_GET, Arc::new(|args| hb::array_get(args)));
|
||||||
|
self.register_extern(hb::SYM_HOST_ARRAY_SET, Arc::new(|args| hb::array_set(args)));
|
||||||
|
self.register_extern(hb::SYM_HOST_MAP_SIZE, Arc::new(|args| hb::map_size(args)));
|
||||||
|
self.register_extern(hb::SYM_HOST_MAP_GET, Arc::new(|args| hb::map_get(args)));
|
||||||
|
self.register_extern(hb::SYM_HOST_MAP_SET, Arc::new(|args| hb::map_set(args)));
|
||||||
|
self.register_extern(hb::SYM_HOST_MAP_HAS, Arc::new(|args| hb::map_has(args)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn register_extern(&mut self, name: &str, f: Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>) {
|
pub fn register_extern(&mut self, name: &str, f: Arc<dyn Fn(&[crate::backend::vm::VMValue]) -> crate::backend::vm::VMValue + Send + Sync>) {
|
||||||
|
|||||||
83
src/jit/extern/host_bridge.rs
vendored
Normal file
83
src/jit/extern/host_bridge.rs
vendored
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//! JIT externs bridging to NyRT host API (C symbols) via by-slot encoding.
|
||||||
|
//!
|
||||||
|
//! 目的: VM/JIT一致のため、JITからも host_api::nyrt_host_call_slot を使うPoC。
|
||||||
|
|
||||||
|
use crate::backend::vm::VMValue;
|
||||||
|
|
||||||
|
fn tlv_encode_values(args: &[VMValue]) -> Vec<u8> {
|
||||||
|
use crate::runtime::plugin_ffi_common::encode as enc;
|
||||||
|
let mut buf = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
|
||||||
|
for a in args {
|
||||||
|
match a {
|
||||||
|
VMValue::Integer(i) => enc::i64(&mut buf, *i),
|
||||||
|
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, ""),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call_slot(handle: u64, slot: u64, argv: &[VMValue]) -> VMValue {
|
||||||
|
let tlv = tlv_encode_values(argv);
|
||||||
|
let mut out = vec![0u8; 256];
|
||||||
|
let mut out_len: usize = out.len();
|
||||||
|
let code = unsafe { crate::runtime::host_api::nyrt_host_call_slot(handle, slot, tlv.as_ptr(), tlv.len(), out.as_mut_ptr(), &mut out_len) };
|
||||||
|
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)),
|
||||||
|
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),
|
||||||
|
5 => crate::runtime::plugin_ffi_common::decode::f64(payload).map(VMValue::Float).unwrap_or(VMValue::Void),
|
||||||
|
9 => {
|
||||||
|
if let Some(h) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
|
||||||
|
if let Some(arc) = crate::runtime::host_handles::get(h) { return VMValue::BoxRef(arc); }
|
||||||
|
}
|
||||||
|
VMValue::Void
|
||||||
|
}
|
||||||
|
_ => VMValue::Void,
|
||||||
|
}
|
||||||
|
} else { VMValue::Void }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_handle(recv: &VMValue) -> Option<u64> {
|
||||||
|
match recv {
|
||||||
|
VMValue::BoxRef(arc) => Some(crate::runtime::host_handles::to_handle_arc(arc.clone())),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Public bridge helpers (symbol strings align with collections for PoC)
|
||||||
|
pub const SYM_HOST_ARRAY_GET: &str = "nyash.host.array.get"; // (ArrayBox, i64)
|
||||||
|
pub const SYM_HOST_ARRAY_SET: &str = "nyash.host.array.set"; // (ArrayBox, i64, val)
|
||||||
|
pub const SYM_HOST_ARRAY_LEN: &str = "nyash.host.array.len"; // (ArrayBox)
|
||||||
|
pub const SYM_HOST_MAP_GET: &str = "nyash.host.map.get"; // (MapBox, key)
|
||||||
|
pub const SYM_HOST_MAP_SET: &str = "nyash.host.map.set"; // (MapBox, key, val)
|
||||||
|
pub const SYM_HOST_MAP_SIZE: &str = "nyash.host.map.size"; // (MapBox)
|
||||||
|
pub const SYM_HOST_MAP_HAS: &str = "nyash.host.map.has"; // (MapBox, key)
|
||||||
|
|
||||||
|
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 }
|
||||||
|
}
|
||||||
|
pub fn array_set(args: &[VMValue]) -> VMValue {
|
||||||
|
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 101, &args[1..]) } else { VMValue::Void }
|
||||||
|
}
|
||||||
|
pub fn array_len(args: &[VMValue]) -> VMValue {
|
||||||
|
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 102, &[]) } else { VMValue::Integer(0) }
|
||||||
|
}
|
||||||
|
pub fn map_get(args: &[VMValue]) -> VMValue {
|
||||||
|
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 203, &args[1..]) } else { VMValue::Void }
|
||||||
|
}
|
||||||
|
pub fn map_set(args: &[VMValue]) -> VMValue {
|
||||||
|
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 204, &args[1..]) } else { VMValue::Void }
|
||||||
|
}
|
||||||
|
pub fn map_size(args: &[VMValue]) -> VMValue {
|
||||||
|
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 200, &[]) } else { VMValue::Integer(0) }
|
||||||
|
}
|
||||||
|
pub fn map_has(args: &[VMValue]) -> VMValue {
|
||||||
|
if let Some(h) = to_handle(args.get(0).unwrap_or(&VMValue::Void)) { call_slot(h, 202, &args[1..]) } else { VMValue::Bool(false) }
|
||||||
|
}
|
||||||
|
|
||||||
1
src/jit/extern/mod.rs
vendored
1
src/jit/extern/mod.rs
vendored
@ -5,6 +5,7 @@
|
|||||||
//! these externs once call emission is added.
|
//! these externs once call emission is added.
|
||||||
|
|
||||||
pub mod collections;
|
pub mod collections;
|
||||||
|
pub mod host_bridge;
|
||||||
pub mod handles;
|
pub mod handles;
|
||||||
pub mod birth;
|
pub mod birth;
|
||||||
pub mod runtime;
|
pub mod runtime;
|
||||||
|
|||||||
37
src/runtime/extern_registry.rs
Normal file
37
src/runtime/extern_registry.rs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
//! Extern interface registry (env.*) for diagnostics and optional slotting
|
||||||
|
//!
|
||||||
|
//! 目的: ExternCallの未登録/未対応時に候補提示やSTRICT診断を改善する。
|
||||||
|
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub struct ExternSpec { pub iface: &'static str, pub method: &'static str, pub min_arity: u8, pub max_arity: u8 }
|
||||||
|
|
||||||
|
static EXTERNS: Lazy<Vec<ExternSpec>> = Lazy::new(|| vec![
|
||||||
|
// console
|
||||||
|
ExternSpec { iface: "env.console", method: "log", min_arity: 1, max_arity: 1 },
|
||||||
|
// debug
|
||||||
|
ExternSpec { iface: "env.debug", method: "trace", min_arity: 1, max_arity: 255 },
|
||||||
|
// runtime
|
||||||
|
ExternSpec { iface: "env.runtime", method: "checkpoint", min_arity: 0, max_arity: 0 },
|
||||||
|
// future (scaffold)
|
||||||
|
ExternSpec { iface: "env.future", method: "new", min_arity: 1, max_arity: 1 },
|
||||||
|
ExternSpec { iface: "env.future", method: "birth", min_arity: 1, max_arity: 1 },
|
||||||
|
ExternSpec { iface: "env.future", method: "set", min_arity: 2, max_arity: 2 },
|
||||||
|
ExternSpec { iface: "env.future", method: "await", min_arity: 1, max_arity: 1 },
|
||||||
|
]);
|
||||||
|
|
||||||
|
pub fn resolve(iface: &str, method: &str) -> Option<ExternSpec> {
|
||||||
|
EXTERNS.iter().copied().find(|e| e.iface == iface && e.method == method)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn known_for_iface(iface: &str) -> Vec<&'static str> {
|
||||||
|
let mut v: Vec<&'static str> = EXTERNS.iter().filter(|e| e.iface == iface).map(|e| e.method).collect();
|
||||||
|
v.sort(); v.dedup(); v
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn all_ifaces() -> Vec<&'static str> {
|
||||||
|
let mut v: Vec<&'static str> = EXTERNS.iter().map(|e| e.iface).collect();
|
||||||
|
v.sort(); v.dedup(); v
|
||||||
|
}
|
||||||
|
|
||||||
@ -21,6 +21,7 @@ pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形
|
|||||||
pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形)
|
pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形)
|
||||||
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
||||||
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
||||||
|
pub mod extern_registry; // ExternCall (env.*) 登録・診断用レジストリ
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|||||||
40
src/tests/vtable_strict.rs
Normal file
40
src/tests/vtable_strict.rs
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#[test]
|
||||||
|
fn vtable_map_set_and_strict_unknown() {
|
||||||
|
use crate::backend::vm::VM;
|
||||||
|
use crate::mir::{MirModule, MirFunction, FunctionSignature, MirInstruction, EffectMask, BasicBlockId, ConstValue, MirType};
|
||||||
|
std::env::set_var("NYASH_ABI_VTABLE", "1");
|
||||||
|
|
||||||
|
// Build: new MapBox; call set("k","v"); size(); return size
|
||||||
|
let sig = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Integer, effects: EffectMask::PURE };
|
||||||
|
let mut f = MirFunction::new(sig, BasicBlockId::new(0));
|
||||||
|
let bb = f.entry_block;
|
||||||
|
let mapv = f.next_value_id();
|
||||||
|
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::NewBox { dst: mapv, box_type: "MapBox".into(), args: vec![] });
|
||||||
|
let k = f.next_value_id(); let v = f.next_value_id();
|
||||||
|
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: k, value: ConstValue::String("k".into()) });
|
||||||
|
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Const { dst: v, value: ConstValue::String("v".into()) });
|
||||||
|
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: mapv, method: "set".into(), args: vec![k, v], method_id: None, effects: EffectMask::PURE });
|
||||||
|
let sz = f.next_value_id();
|
||||||
|
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::BoxCall { dst: Some(sz), box_val: mapv, method: "size".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
|
||||||
|
f.get_block_mut(bb).unwrap().add_instruction(MirInstruction::Return { value: Some(sz) });
|
||||||
|
let mut m = MirModule::new("t".into()); m.add_function(f);
|
||||||
|
let mut vm = VM::new();
|
||||||
|
let out = vm.execute_module(&m).expect("vm exec");
|
||||||
|
let s = out.to_string_box().value; assert_eq!(s, "1");
|
||||||
|
|
||||||
|
// STRICT unknown method on MapBox should error
|
||||||
|
std::env::set_var("NYASH_ABI_STRICT", "1");
|
||||||
|
let sig2 = FunctionSignature { name: "main".into(), params: vec![], return_type: MirType::Void, effects: EffectMask::PURE };
|
||||||
|
let mut f2 = MirFunction::new(sig2, BasicBlockId::new(0));
|
||||||
|
let bb2 = f2.entry_block;
|
||||||
|
let m2 = f2.next_value_id();
|
||||||
|
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::NewBox { dst: m2, box_type: "MapBox".into(), args: vec![] });
|
||||||
|
// Call unknown method
|
||||||
|
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::BoxCall { dst: None, box_val: m2, method: "unknown".into(), args: vec![], method_id: None, effects: EffectMask::PURE });
|
||||||
|
f2.get_block_mut(bb2).unwrap().add_instruction(MirInstruction::Return { value: None });
|
||||||
|
let mut mm = MirModule::new("t2".into()); mm.add_function(f2);
|
||||||
|
let mut vm2 = VM::new();
|
||||||
|
let res = vm2.execute_module(&mm);
|
||||||
|
assert!(res.is_err(), "STRICT should error on unknown vtable method");
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user