Phase 12: 統一TypeBox ABI実装開始 - ChatGPT5による極小コアABI基盤構築
- TypeBox ABI雛形: メソッドスロット管理システム追加 - Type Registry: Array/Map/StringBoxの基本メソッド定義 - Host API: C ABI逆呼び出しシステム実装 - Phase 12ドキュメント整理: 設計文書統合・アーカイブ化 - MIR Builder: クリーンアップと分離実装完了 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
302
src/runtime/host_api.rs
Normal file
302
src/runtime/host_api.rs
Normal file
@ -0,0 +1,302 @@
|
||||
/*!
|
||||
* Host reverse-call API for plugins (Phase 12 / A-1)
|
||||
*
|
||||
* - Provides C ABI functions that plugins can call to operate on HostHandle (user/builtin boxes) via TLV.
|
||||
* - Minimal supported methods: InstanceBox.getField/setField, ArrayBox.get/set
|
||||
* - GC correctness: setField/Array.set triggers write barrier using current VM's runtime (TLS-bound during plugin calls).
|
||||
*/
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
// ===== TLS: current VM pointer during plugin invoke =====
|
||||
thread_local! {
|
||||
static CURRENT_VM: std::cell::Cell<*mut crate::backend::vm::VM> = std::cell::Cell::new(std::ptr::null_mut());
|
||||
}
|
||||
|
||||
pub fn set_current_vm(ptr: *mut crate::backend::vm::VM) { CURRENT_VM.with(|c| c.set(ptr)); }
|
||||
pub fn clear_current_vm() { CURRENT_VM.with(|c| c.set(std::ptr::null_mut())); }
|
||||
fn with_current_vm_mut<F, R>(f: F) -> Option<R>
|
||||
where F: FnOnce(&mut crate::backend::vm::VM) -> R {
|
||||
CURRENT_VM.with(|c| {
|
||||
let p = c.get();
|
||||
if p.is_null() { None } else { Some(unsafe { f(&mut *p) }) }
|
||||
})
|
||||
}
|
||||
|
||||
// ===== Utilities: TLV encode helpers (single-value) =====
|
||||
fn tlv_encode_one(val: &crate::backend::vm::VMValue) -> Vec<u8> {
|
||||
use crate::runtime::plugin_ffi_common as tlv;
|
||||
let mut buf = tlv::encode_tlv_header(1);
|
||||
match val {
|
||||
crate::backend::vm::VMValue::Integer(i) => tlv::encode::i64(&mut buf, *i),
|
||||
crate::backend::vm::VMValue::Float(f) => tlv::encode::f64(&mut buf, *f),
|
||||
crate::backend::vm::VMValue::Bool(b) => tlv::encode::bool(&mut buf, *b),
|
||||
crate::backend::vm::VMValue::String(s) => tlv::encode::string(&mut buf, s),
|
||||
crate::backend::vm::VMValue::BoxRef(b) => {
|
||||
// Return HostHandle for arbitrary boxes
|
||||
let h = crate::runtime::host_handles::to_handle_arc(b.clone());
|
||||
tlv::encode::host_handle(&mut buf, h);
|
||||
}
|
||||
_ => tlv::encode::string(&mut buf, "void"),
|
||||
}
|
||||
buf
|
||||
}
|
||||
|
||||
fn vmvalue_from_tlv(tag: u8, payload: &[u8]) -> Option<crate::backend::vm::VMValue> {
|
||||
use crate::runtime::plugin_ffi_common as tlv;
|
||||
match tag {
|
||||
1 => Some(crate::backend::vm::VMValue::Bool(tlv::decode::bool(payload).unwrap_or(false))),
|
||||
2 => tlv::decode::i32(payload).map(|v| crate::backend::vm::VMValue::Integer(v as i64)),
|
||||
3 => {
|
||||
if payload.len()==8 { let mut b=[0u8;8]; b.copy_from_slice(payload); Some(crate::backend::vm::VMValue::Integer(i64::from_le_bytes(b))) } else { None }
|
||||
}
|
||||
5 => tlv::decode::f64(payload).map(crate::backend::vm::VMValue::Float),
|
||||
6|7 => Some(crate::backend::vm::VMValue::String(tlv::decode::string(payload))),
|
||||
8 => {
|
||||
// PluginHandle(type_id, instance_id) → reconstruct PluginBoxV2 (when plugins enabled)
|
||||
if let Some((type_id, instance_id)) = tlv::decode::plugin_handle(payload) {
|
||||
if let Some(arc) = plugin_box_from_handle(type_id, instance_id) { return Some(crate::backend::vm::VMValue::BoxRef(arc)); }
|
||||
}
|
||||
None
|
||||
}
|
||||
9 => {
|
||||
if let Some(h) = tlv::decode::u64(payload) { crate::runtime::host_handles::get(h).map(crate::backend::vm::VMValue::BoxRef) } else { None }
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn slice_from_raw<'a>(ptr: *const u8, len: usize) -> &'a [u8] { std::slice::from_raw_parts(ptr, len) }
|
||||
unsafe fn slice_from_raw_mut<'a>(ptr: *mut u8, len: usize) -> &'a mut [u8] { std::slice::from_raw_parts_mut(ptr, len) }
|
||||
|
||||
fn encode_out(out_ptr: *mut u8, out_len: *mut usize, buf: &[u8]) -> i32 {
|
||||
unsafe {
|
||||
if out_ptr.is_null() || out_len.is_null() { return -2; }
|
||||
let cap = *out_len;
|
||||
if cap < buf.len() { return -3; }
|
||||
let out = slice_from_raw_mut(out_ptr, cap);
|
||||
out[..buf.len()].copy_from_slice(buf);
|
||||
*out_len = buf.len();
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyrt_host_call_name(handle: u64, method_ptr: *const u8, method_len: usize,
|
||||
args_ptr: *const u8, args_len: usize,
|
||||
out_ptr: *mut u8, out_len: *mut usize) -> i32 {
|
||||
// Resolve receiver
|
||||
let recv_arc = match crate::runtime::host_handles::get(handle) { Some(a) => a, None => return -1 };
|
||||
let method = unsafe { std::str::from_utf8(slice_from_raw(method_ptr, method_len)).unwrap_or("") }.to_string();
|
||||
// Parse TLV args (header + entries)
|
||||
let mut argv: Vec<crate::backend::vm::VMValue> = Vec::new();
|
||||
if args_ptr.is_null() || args_len < 4 { /* no args */ } else {
|
||||
let buf = unsafe { slice_from_raw(args_ptr, args_len) };
|
||||
// Iterate entries
|
||||
let mut off = 4usize;
|
||||
while buf.len() >= off + 4 {
|
||||
let tag = buf[off]; let _rsv = buf[off+1]; let sz = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + sz { break; }
|
||||
let payload = &buf[off+4..off+4+sz];
|
||||
if let Some(v) = vmvalue_from_tlv(tag, payload) { argv.push(v); }
|
||||
off += 4 + sz;
|
||||
}
|
||||
}
|
||||
// Dispatch minimal supported methods
|
||||
// InstanceBox getField/setField
|
||||
if let Some(inst) = recv_arc.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||
match method.as_str() {
|
||||
"getField" if argv.len() >= 1 => {
|
||||
let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() };
|
||||
let out = inst.get_field_unified(&field).map(|nv| match nv {
|
||||
crate::value::NyashValue::Integer(i) => crate::backend::vm::VMValue::Integer(i),
|
||||
crate::value::NyashValue::Float(f) => crate::backend::vm::VMValue::Float(f),
|
||||
crate::value::NyashValue::Bool(b) => crate::backend::vm::VMValue::Bool(b),
|
||||
crate::value::NyashValue::String(s) => crate::backend::vm::VMValue::String(s),
|
||||
crate::value::NyashValue::Void | crate::value::NyashValue::Null => crate::backend::vm::VMValue::String("".to_string()),
|
||||
crate::value::NyashValue::Box(b) => {
|
||||
if let Ok(g) = b.lock() { crate::backend::vm::VMValue::BoxRef(std::sync::Arc::from(g.share_box())) } else { crate::backend::vm::VMValue::String("".to_string()) }
|
||||
}
|
||||
_ => crate::backend::vm::VMValue::String("".to_string()),
|
||||
}).unwrap_or(crate::backend::vm::VMValue::String("".to_string()));
|
||||
let buf = tlv_encode_one(&out);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
"setField" if argv.len() >= 2 => {
|
||||
let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() };
|
||||
// Barrier: use current VM runtime if available
|
||||
let _ = with_current_vm_mut(|vm| {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.setField");
|
||||
});
|
||||
// Accept primitives only for now
|
||||
let nv_opt = match argv[1].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Some(crate::value::NyashValue::Integer(i)),
|
||||
crate::backend::vm::VMValue::Float(f) => Some(crate::value::NyashValue::Float(f)),
|
||||
crate::backend::vm::VMValue::Bool(b) => Some(crate::value::NyashValue::Bool(b)),
|
||||
crate::backend::vm::VMValue::String(s) => Some(crate::value::NyashValue::String(s)),
|
||||
crate::backend::vm::VMValue::BoxRef(_) => None,
|
||||
_ => None,
|
||||
};
|
||||
if let Some(nv) = nv_opt { let _ = inst.set_field_unified(field, nv); }
|
||||
let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(true));
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// ArrayBox get/set
|
||||
if let Some(arr) = recv_arc.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
match method.as_str() {
|
||||
"get" if argv.len() >= 1 => {
|
||||
let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::<i64>().unwrap_or(0) };
|
||||
let out = arr.get(Box::new(crate::box_trait::IntegerBox::new(idx)));
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
let buf = tlv_encode_one(&vmv);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
"set" if argv.len() >= 2 => {
|
||||
let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::<i64>().unwrap_or(0) };
|
||||
let vb = match argv[1].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)) as Box<dyn NyashBox>,
|
||||
crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||
crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||
crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||
crate::backend::vm::VMValue::BoxRef(b) => b.share_box(),
|
||||
_ => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
let _ = with_current_vm_mut(|vm| {
|
||||
crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.Array.set");
|
||||
});
|
||||
let out = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), vb);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
let buf = tlv_encode_one(&vmv);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Unsupported
|
||||
-10
|
||||
}
|
||||
|
||||
// Helper: reconstruct PluginBoxV2 from (type_id, instance_id) when plugins are enabled
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
fn plugin_box_from_handle(type_id: u32, instance_id: u32) -> Option<std::sync::Arc<dyn NyashBox>> {
|
||||
let loader = crate::runtime::plugin_loader_v2::get_global_loader_v2();
|
||||
let loader = loader.read().ok()?;
|
||||
let bx = loader.construct_existing_instance(type_id, instance_id)?;
|
||||
Some(std::sync::Arc::from(bx))
|
||||
}
|
||||
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
|
||||
fn plugin_box_from_handle(_type_id: u32, _instance_id: u32) -> Option<std::sync::Arc<dyn NyashBox>> { None }
|
||||
|
||||
// ---- by-slot variant (selector_id: u64) ----
|
||||
// Minimal slot mapping (subject to consolidation with TypeRegistry):
|
||||
// 1: InstanceBox.getField(name: string) -> any
|
||||
// 2: InstanceBox.setField(name: string, value: any-primitive) -> bool
|
||||
// 100: ArrayBox.get(index: i64) -> any
|
||||
// 101: ArrayBox.set(index: i64, value: any) -> any
|
||||
// 102: ArrayBox.len() -> i64
|
||||
#[no_mangle]
|
||||
pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
args_ptr: *const u8, args_len: usize,
|
||||
out_ptr: *mut u8, out_len: *mut usize) -> i32 {
|
||||
let recv_arc = match crate::runtime::host_handles::get(handle) { Some(a) => a, None => return -1 };
|
||||
// Parse TLV args
|
||||
let mut argv: Vec<crate::backend::vm::VMValue> = Vec::new();
|
||||
if !args_ptr.is_null() && args_len >= 4 {
|
||||
let buf = unsafe { slice_from_raw(args_ptr, args_len) };
|
||||
let mut off = 4usize;
|
||||
while buf.len() >= off + 4 {
|
||||
let tag = buf[off]; let sz = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + sz { break; }
|
||||
let payload = &buf[off+4..off+4+sz];
|
||||
if let Some(v) = vmvalue_from_tlv(tag, payload) { argv.push(v); }
|
||||
off += 4 + sz;
|
||||
}
|
||||
}
|
||||
|
||||
match selector_id {
|
||||
1 | 2 => {
|
||||
if let Some(inst) = recv_arc.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||
if selector_id == 1 {
|
||||
// getField(name)
|
||||
if argv.len() >= 1 {
|
||||
let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() };
|
||||
let out = inst.get_field_unified(&field).map(|nv| match nv {
|
||||
crate::value::NyashValue::Integer(i) => crate::backend::vm::VMValue::Integer(i),
|
||||
crate::value::NyashValue::Float(f) => crate::backend::vm::VMValue::Float(f),
|
||||
crate::value::NyashValue::Bool(b) => crate::backend::vm::VMValue::Bool(b),
|
||||
crate::value::NyashValue::String(s) => crate::backend::vm::VMValue::String(s),
|
||||
crate::value::NyashValue::Void | crate::value::NyashValue::Null => crate::backend::vm::VMValue::String("".to_string()),
|
||||
crate::value::NyashValue::Box(b) => {
|
||||
if let Ok(g) = b.lock() { crate::backend::vm::VMValue::BoxRef(std::sync::Arc::from(g.share_box())) } else { crate::backend::vm::VMValue::String("".to_string()) }
|
||||
}
|
||||
_ => crate::backend::vm::VMValue::String("".to_string()),
|
||||
}).unwrap_or(crate::backend::vm::VMValue::String("".to_string()));
|
||||
let buf = tlv_encode_one(&out);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
} else {
|
||||
// setField(name, value)
|
||||
if argv.len() >= 2 {
|
||||
let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() };
|
||||
let _ = with_current_vm_mut(|vm| { crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.setField"); });
|
||||
let nv_opt = match argv[1].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Some(crate::value::NyashValue::Integer(i)),
|
||||
crate::backend::vm::VMValue::Float(f) => Some(crate::value::NyashValue::Float(f)),
|
||||
crate::backend::vm::VMValue::Bool(b) => Some(crate::value::NyashValue::Bool(b)),
|
||||
crate::backend::vm::VMValue::String(s) => Some(crate::value::NyashValue::String(s)),
|
||||
crate::backend::vm::VMValue::BoxRef(_) => None,
|
||||
_ => None,
|
||||
};
|
||||
if let Some(nv) = nv_opt { let _ = inst.set_field_unified(field, nv); }
|
||||
let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(true));
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
100 | 101 | 102 => {
|
||||
if let Some(arr) = recv_arc.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
match selector_id {
|
||||
100 => { // get(index)
|
||||
if argv.len() >= 1 {
|
||||
let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::<i64>().unwrap_or(0) };
|
||||
let out = arr.get(Box::new(crate::box_trait::IntegerBox::new(idx)));
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
let buf = tlv_encode_one(&vmv);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
}
|
||||
101 => { // set(index, value)
|
||||
if argv.len() >= 2 {
|
||||
let idx = match argv[0].clone() { crate::backend::vm::VMValue::Integer(i) => i, v => v.to_string().parse::<i64>().unwrap_or(0) };
|
||||
let vb = match argv[1].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)) as Box<dyn NyashBox>,
|
||||
crate::backend::vm::VMValue::Float(f) => Box::new(crate::boxes::math_box::FloatBox::new(f)),
|
||||
crate::backend::vm::VMValue::Bool(b) => Box::new(crate::box_trait::BoolBox::new(b)),
|
||||
crate::backend::vm::VMValue::String(s) => Box::new(crate::box_trait::StringBox::new(s)),
|
||||
crate::backend::vm::VMValue::BoxRef(b) => b.share_box(),
|
||||
_ => Box::new(crate::box_trait::VoidBox::new()),
|
||||
};
|
||||
let _ = with_current_vm_mut(|vm| { crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.Array.set"); });
|
||||
let out = arr.set(Box::new(crate::box_trait::IntegerBox::new(idx)), vb);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
let buf = tlv_encode_one(&vmv);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
}
|
||||
102 => { // len()
|
||||
let len = arr.len();
|
||||
let buf = tlv_encode_one(&crate::backend::vm::VMValue::Integer(len as i64));
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
-10
|
||||
}
|
||||
43
src/runtime/host_handles.rs
Normal file
43
src/runtime/host_handles.rs
Normal file
@ -0,0 +1,43 @@
|
||||
/*!
|
||||
* Host Handle Registry (global)
|
||||
*
|
||||
* 目的:
|
||||
* - C ABI(TLV)でユーザー/内蔵Boxを渡すためのホスト管理ハンドルを提供。
|
||||
* - u64ハンドルID → Arc<dyn NyashBox> をグローバルに保持し、VM/PluginHost/JITから参照可能にする。
|
||||
*/
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock, atomic::{AtomicU64, Ordering}};
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
struct Registry {
|
||||
next: AtomicU64,
|
||||
map: RwLock<HashMap<u64, Arc<dyn NyashBox>>>,
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
fn new() -> Self { Self { next: AtomicU64::new(1), map: RwLock::new(HashMap::new()) } }
|
||||
fn alloc(&self, obj: Arc<dyn NyashBox>) -> u64 {
|
||||
let h = self.next.fetch_add(1, Ordering::Relaxed);
|
||||
if let Ok(mut m) = self.map.write() { m.insert(h, obj); }
|
||||
h
|
||||
}
|
||||
fn get(&self, h: u64) -> Option<Arc<dyn NyashBox>> {
|
||||
self.map.read().ok().and_then(|m| m.get(&h).cloned())
|
||||
}
|
||||
#[allow(dead_code)]
|
||||
fn drop_handle(&self, h: u64) { if let Ok(mut m) = self.map.write() { m.remove(&h); } }
|
||||
}
|
||||
|
||||
static REG: OnceCell<Registry> = OnceCell::new();
|
||||
fn reg() -> &'static Registry { REG.get_or_init(Registry::new) }
|
||||
|
||||
/// Box<dyn NyashBox> → HostHandle (u64)
|
||||
pub fn to_handle_box(bx: Box<dyn NyashBox>) -> u64 { reg().alloc(Arc::from(bx)) }
|
||||
/// Arc<dyn NyashBox> → HostHandle (u64)
|
||||
pub fn to_handle_arc(arc: Arc<dyn NyashBox>) -> u64 { reg().alloc(arc) }
|
||||
/// HostHandle(u64) → Arc<dyn NyashBox>
|
||||
pub fn get(h: u64) -> Option<Arc<dyn NyashBox>> { reg().get(h) }
|
||||
|
||||
@ -17,6 +17,10 @@ pub mod semantics;
|
||||
// pub mod plugin_box; // legacy - 古いPluginBox
|
||||
// pub mod plugin_loader; // legacy - Host VTable使用
|
||||
pub mod type_meta;
|
||||
pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形
|
||||
pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形)
|
||||
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
||||
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@ -28,6 +28,12 @@ pub mod decode {
|
||||
if buf.len() < 8 + size { return None; }
|
||||
Some((tag, size, &buf[8..8 + size]))
|
||||
}
|
||||
/// Decode u64 payload (size must be 8)
|
||||
pub fn u64(payload: &[u8]) -> Option<u64> {
|
||||
if payload.len() != 8 { return None; }
|
||||
let mut b = [0u8;8]; b.copy_from_slice(payload);
|
||||
Some(u64::from_le_bytes(b))
|
||||
}
|
||||
/// Decode bool payload (size must be 1; nonzero => true)
|
||||
pub fn bool(payload: &[u8]) -> Option<bool> {
|
||||
if payload.len() != 1 { return None; }
|
||||
@ -50,6 +56,16 @@ pub mod decode {
|
||||
String::from_utf8_lossy(payload).to_string()
|
||||
}
|
||||
|
||||
/// Decode plugin handle payload (type_id:u32 + instance_id:u32)
|
||||
pub fn plugin_handle(payload: &[u8]) -> Option<(u32, u32)> {
|
||||
if payload.len() != 8 { return None; }
|
||||
let mut a = [0u8;4];
|
||||
let mut b = [0u8;4];
|
||||
a.copy_from_slice(&payload[0..4]);
|
||||
b.copy_from_slice(&payload[4..8]);
|
||||
Some((u32::from_le_bytes(a), u32::from_le_bytes(b)))
|
||||
}
|
||||
|
||||
/// Get nth TLV entry from a buffer with header
|
||||
pub fn tlv_nth(buf: &[u8], n: usize) -> Option<(u8, usize, &[u8])> {
|
||||
if buf.len() < 4 { return None; }
|
||||
@ -87,6 +103,8 @@ pub mod encode {
|
||||
const TAG_BYTES: u8 = 7;
|
||||
/// tag for Plugin Handle (type_id + instance_id)
|
||||
const TAG_HANDLE: u8 = 8;
|
||||
/// tag for Host Handle (host-managed handle id u64)
|
||||
const TAG_HOST_HANDLE: u8 = 9;
|
||||
|
||||
/// Append a bool TLV entry (tag=1, size=1)
|
||||
pub fn bool(buf: &mut Vec<u8>, v: bool) {
|
||||
@ -150,6 +168,13 @@ pub mod encode {
|
||||
buf.extend_from_slice(&type_id.to_le_bytes());
|
||||
buf.extend_from_slice(&instance_id.to_le_bytes());
|
||||
}
|
||||
/// Append a host handle TLV entry (tag=9, size=8, handle_id:u64)
|
||||
pub fn host_handle(buf: &mut Vec<u8>, handle_id: u64) {
|
||||
buf.push(TAG_HOST_HANDLE);
|
||||
buf.push(0u8);
|
||||
buf.extend_from_slice(&(8u16).to_le_bytes());
|
||||
buf.extend_from_slice(&handle_id.to_le_bytes());
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@ -123,7 +123,7 @@ impl PluginHost {
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
if let Some(arg0) = args.get(0) {
|
||||
if let Some(fut) = arg0.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
||||
let max_ms: u64 = std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000);
|
||||
let max_ms: u64 = crate::config::env::await_max_ms();
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
while !fut.ready() {
|
||||
|
||||
@ -395,6 +395,27 @@ impl PluginLoaderV2 {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Construct a PluginBoxV2 from an existing (type_id, instance_id) pair.
|
||||
/// Used by reverse host API to materialize PluginHandle(tag=8) as a VMValue::BoxRef.
|
||||
pub fn construct_existing_instance(&self, type_id: u32, instance_id: u32) -> Option<Box<dyn NyashBox>> {
|
||||
let config = self.config.as_ref()?;
|
||||
let cfg_path = self.config_path.as_ref()?;
|
||||
let toml_content = std::fs::read_to_string(cfg_path).ok()?;
|
||||
let toml_value: toml::Value = toml::from_str(&toml_content).ok()?;
|
||||
let (lib_name, box_type) = self.find_box_by_type_id(config, &toml_value, type_id)?;
|
||||
let plugins = self.plugins.read().ok()?;
|
||||
let plugin = plugins.get(lib_name)?.clone();
|
||||
// fini method id from spec or config
|
||||
let fini_method_id = if let Some(spec) = self.box_specs.read().ok()?.get(&(lib_name.to_string(), box_type.to_string())) {
|
||||
spec.fini_method_id
|
||||
} else {
|
||||
let box_conf = config.get_box_config(lib_name, box_type, &toml_value)?;
|
||||
box_conf.methods.get("fini").map(|m| m.method_id)
|
||||
};
|
||||
let bx = construct_plugin_box(box_type.to_string(), type_id, plugin.invoke_fn, instance_id, fini_method_id);
|
||||
Some(Box::new(bx))
|
||||
}
|
||||
|
||||
fn find_lib_name_for_box(&self, box_type: &str) -> Option<String> {
|
||||
if let Some(cfg) = &self.config {
|
||||
if let Some((name, _)) = cfg.find_library_for_box(box_type) { return Some(name.to_string()); }
|
||||
@ -488,7 +509,7 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
("env.runtime", "checkpoint") => {
|
||||
// Safepoint + scheduler poll via global hooks
|
||||
if std::env::var("NYASH_RUNTIME_CHECKPOINT_TRACE").ok().as_deref() == Some("1") {
|
||||
if crate::config::env::runtime_checkpoint_trace() {
|
||||
eprintln!("[runtime.checkpoint] reached");
|
||||
}
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
@ -515,7 +536,7 @@ impl PluginLoaderV2 {
|
||||
use crate::boxes::result::NyashResultBox;
|
||||
if let Some(arg) = args.get(0) {
|
||||
if let Some(fut) = arg.as_any().downcast_ref::<crate::boxes::future::FutureBox>() {
|
||||
let max_ms: u64 = std::env::var("NYASH_AWAIT_MAX_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(5000);
|
||||
let max_ms: u64 = crate::config::env::await_max_ms();
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
while !fut.ready() {
|
||||
@ -961,6 +982,15 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
None
|
||||
}
|
||||
9 if size == 8 => { // HostHandle -> Box (user/builtin)
|
||||
if let Some(h) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
|
||||
if dbg_on() { eprintln!("[Plugin→VM] return host_handle={} (returns_result={})", h, returns_result); }
|
||||
if let Some(arc) = crate::runtime::host_handles::get(h) {
|
||||
let val: Box<dyn NyashBox> = arc.as_ref().share_box();
|
||||
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(val)) as Box<dyn NyashBox>) } else { Some(val) }
|
||||
} else { None }
|
||||
} else { None }
|
||||
}
|
||||
2 if size == 4 => { // I32
|
||||
let n = crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap();
|
||||
let val: Box<dyn NyashBox> = Box::new(IntegerBox::new(n as i64));
|
||||
@ -983,9 +1013,14 @@ impl PluginLoaderV2 {
|
||||
let val: Box<dyn NyashBox> = Box::new(IntegerBox::new(n));
|
||||
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(val)) as Box<dyn NyashBox>) } else { Some(val) }
|
||||
}
|
||||
9 => {
|
||||
if dbg_on() { eprintln!("[Plugin→VM] return void (returns_result={})", returns_result); }
|
||||
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(Box::new(crate::box_trait::VoidBox::new()))) as Box<dyn NyashBox>) } else { None }
|
||||
9 if size == 8 => {
|
||||
if let Some(h) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
|
||||
if dbg_on() { eprintln!("[Plugin→VM] return host_handle={} (returns_result={})", h, returns_result); }
|
||||
if let Some(arc) = crate::runtime::host_handles::get(h) {
|
||||
let val: Box<dyn NyashBox> = arc.as_ref().share_box();
|
||||
if returns_result { Some(Box::new(crate::boxes::result::NyashResultBox::new_ok(val)) as Box<dyn NyashBox>) } else { Some(val) }
|
||||
} else { None }
|
||||
} else { None }
|
||||
},
|
||||
_ => None,
|
||||
}} else { None }
|
||||
@ -1238,9 +1273,9 @@ impl PluginLoaderV2 {
|
||||
crate::runtime::plugin_ffi_common::encode::f64(&mut buf, f.value);
|
||||
continue;
|
||||
}
|
||||
// Fallback: stringify
|
||||
let sv = enc_ref.to_string_box().value;
|
||||
crate::runtime::plugin_ffi_common::encode::string(&mut buf, &sv);
|
||||
// Fallback: HostHandle for user/builtin boxes
|
||||
let h = crate::runtime::host_handles::to_handle_box(enc_ref.clone_box());
|
||||
crate::runtime::plugin_ffi_common::encode::host_handle(&mut buf, h);
|
||||
}
|
||||
buf
|
||||
};
|
||||
|
||||
48
src/runtime/type_box_abi.rs
Normal file
48
src/runtime/type_box_abi.rs
Normal file
@ -0,0 +1,48 @@
|
||||
/*!
|
||||
* TypeBox ABI (Tier-0 雛形)
|
||||
*
|
||||
* 目的:
|
||||
* - Phase 12 で導入する Nyash ABI (vtable) の型定義と最小構造を先置きするための雛形。
|
||||
* - 現段階では参照型と関数ポインタの骨組みのみ提供し、実呼び出しは行わない(常に未処理)。
|
||||
*/
|
||||
|
||||
/// Nyash ABI における最小の値タグ(雛形)
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum NyrtTag {
|
||||
Void,
|
||||
I64,
|
||||
F64,
|
||||
Bool,
|
||||
String,
|
||||
Handle,
|
||||
}
|
||||
|
||||
/// Nyash ABI の値表現(雛形)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct NyrtValue {
|
||||
pub tag: NyrtTag,
|
||||
pub i64_: i64,
|
||||
}
|
||||
|
||||
impl NyrtValue {
|
||||
pub fn void() -> Self { Self { tag: NyrtTag::Void, i64_: 0 } }
|
||||
pub fn i64(v: i64) -> Self { Self { tag: NyrtTag::I64, i64_: v } }
|
||||
}
|
||||
|
||||
/// Nyash ABI のメソッド関数ポインタ(雛形)
|
||||
pub type NyrtMethodFn = fn(instance: u64, argc: usize, argv: *const NyrtValue) -> NyrtValue;
|
||||
|
||||
/// スロット定義(雛形)
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MethodEntry { pub name: &'static str, pub arity: u8, pub slot: u16 }
|
||||
|
||||
/// TypeBox(雛形): 各型の静的メタデータ(スロット一覧付き)
|
||||
pub struct TypeBox {
|
||||
pub type_name: &'static str,
|
||||
pub methods: &'static [MethodEntry],
|
||||
}
|
||||
|
||||
impl TypeBox {
|
||||
pub const fn new(type_name: &'static str) -> Self { Self { type_name, methods: &[] } }
|
||||
pub const fn new_with(type_name: &'static str, methods: &'static [MethodEntry]) -> Self { Self { type_name, methods } }
|
||||
}
|
||||
55
src/runtime/type_registry.rs
Normal file
55
src/runtime/type_registry.rs
Normal file
@ -0,0 +1,55 @@
|
||||
/*!
|
||||
* Type Registry (Tier-0 雛形)
|
||||
*
|
||||
* 目的:
|
||||
* - TypeId → TypeBox 参照の最小インターフェースを用意(現時点では未実装・常に未登録)。
|
||||
* - VM/JIT 実装が存在を前提に呼び出しても no-op/fallback できる状態にする。
|
||||
*/
|
||||
|
||||
use super::type_box_abi::{TypeBox, MethodEntry};
|
||||
|
||||
// 最小サンプル: MapBox の TypeBox を事前登録(Tier-1 PoC 用)
|
||||
// --- ArrayBox ---
|
||||
const ARRAY_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "get", arity: 1, slot: 100 },
|
||||
MethodEntry { name: "set", arity: 2, slot: 101 },
|
||||
MethodEntry { name: "len", arity: 0, slot: 102 },
|
||||
MethodEntry { name: "length", arity: 0, slot: 102 },
|
||||
];
|
||||
static ARRAYBOX_TB: TypeBox = TypeBox::new_with("ArrayBox", ARRAY_METHODS);
|
||||
|
||||
// --- MapBox ---
|
||||
const MAP_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "size", arity: 0, slot: 200 },
|
||||
MethodEntry { name: "len", arity: 0, slot: 201 },
|
||||
MethodEntry { name: "has", arity: 1, slot: 202 },
|
||||
MethodEntry { name: "get", arity: 1, slot: 203 },
|
||||
MethodEntry { name: "set", arity: 2, slot: 204 },
|
||||
];
|
||||
static MAPBOX_TB: TypeBox = TypeBox::new_with("MapBox", MAP_METHODS);
|
||||
|
||||
// --- StringBox ---
|
||||
const STRING_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "len", arity: 0, slot: 300 },
|
||||
];
|
||||
static STRINGBOX_TB: TypeBox = TypeBox::new_with("StringBox", STRING_METHODS);
|
||||
|
||||
/// 型名から TypeBox を解決(雛形)。現在は常に None。
|
||||
pub fn resolve_typebox_by_name(type_name: &str) -> Option<&'static TypeBox> {
|
||||
match type_name {
|
||||
"MapBox" => Some(&MAPBOX_TB),
|
||||
"ArrayBox" => Some(&ARRAYBOX_TB),
|
||||
"StringBox" => Some(&STRINGBOX_TB),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 型名・メソッド名・アリティからスロットを解決(雛形)
|
||||
pub fn resolve_slot_by_name(type_name: &str, method: &str, arity: usize) -> Option<u16> {
|
||||
let tb = resolve_typebox_by_name(type_name)?;
|
||||
let ar = arity as u8;
|
||||
for m in tb.methods {
|
||||
if m.name == method && m.arity == ar { return Some(m.slot); }
|
||||
}
|
||||
None
|
||||
}
|
||||
Reference in New Issue
Block a user