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:
@ -18,10 +18,17 @@ static EXTERNS: Lazy<Vec<ExternSpec>> = Lazy::new(|| vec![
|
||||
ExternSpec { iface: "env.console", method: "log", min_arity: 1, max_arity: 255, slot: Some(10) },
|
||||
ExternSpec { iface: "env.console", method: "warn", min_arity: 1, max_arity: 255, slot: Some(10) },
|
||||
ExternSpec { iface: "env.console", method: "error", min_arity: 1, max_arity: 255, slot: Some(10) },
|
||||
ExternSpec { iface: "env.console", method: "info", min_arity: 1, max_arity: 255, slot: Some(10) },
|
||||
ExternSpec { iface: "env.console", method: "debug", min_arity: 1, max_arity: 255, slot: Some(10) },
|
||||
// debug
|
||||
ExternSpec { iface: "env.debug", method: "trace", min_arity: 1, max_arity: 255, slot: Some(11) },
|
||||
// runtime
|
||||
ExternSpec { iface: "env.runtime", method: "checkpoint", min_arity: 0, max_arity: 0, slot: Some(12) },
|
||||
// task
|
||||
ExternSpec { iface: "env.task", method: "cancelCurrent", min_arity: 0, max_arity: 0, slot: Some(30) },
|
||||
ExternSpec { iface: "env.task", method: "currentToken", min_arity: 0, max_arity: 0, slot: Some(31) },
|
||||
ExternSpec { iface: "env.task", method: "yieldNow", min_arity: 0, max_arity: 0, slot: Some(32) },
|
||||
ExternSpec { iface: "env.task", method: "sleepMs", min_arity: 1, max_arity: 1, slot: Some(33) },
|
||||
// future (scaffold)
|
||||
ExternSpec { iface: "env.future", method: "new", min_arity: 1, max_arity: 1, slot: Some(20) },
|
||||
ExternSpec { iface: "env.future", method: "birth", min_arity: 1, max_arity: 1, slot: Some(20) },
|
||||
|
||||
@ -81,7 +81,7 @@ fn encode_out(out_ptr: *mut u8, out_len: *mut usize, buf: &[u8]) -> i32 {
|
||||
}
|
||||
}
|
||||
|
||||
#[no_mangle]
|
||||
#[cfg_attr(all(not(test), feature = "c-abi-export"), 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 {
|
||||
@ -194,6 +194,8 @@ fn plugin_box_from_handle(_type_id: u32, _instance_id: u32) -> Option<std::sync:
|
||||
// Minimal slot mapping (subject to consolidation with TypeRegistry):
|
||||
// 1: InstanceBox.getField(name: string) -> any
|
||||
// 2: InstanceBox.setField(name: string, value: any-primitive) -> bool
|
||||
// 3: InstanceBox.has(name: string) -> bool
|
||||
// 4: InstanceBox.size() -> i64
|
||||
// 100: ArrayBox.get(index: i64) -> any
|
||||
// 101: ArrayBox.set(index: i64, value: any) -> any
|
||||
// 102: ArrayBox.len() -> i64
|
||||
@ -203,7 +205,7 @@ fn plugin_box_from_handle(_type_id: u32, _instance_id: u32) -> Option<std::sync:
|
||||
// 203: MapBox.get(key:any) -> any
|
||||
// 204: MapBox.set(key:any, value:any) -> any
|
||||
// 300: StringBox.len() -> i64
|
||||
#[no_mangle]
|
||||
#[cfg_attr(all(not(test), feature = "c-abi-export"), 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 {
|
||||
@ -223,7 +225,7 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
}
|
||||
|
||||
match selector_id {
|
||||
1 | 2 => {
|
||||
1 | 2 | 3 | 4 => {
|
||||
if let Some(inst) = recv_arc.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||
if selector_id == 1 {
|
||||
// getField(name)
|
||||
@ -243,7 +245,7 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
let buf = tlv_encode_one(&out);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
} else {
|
||||
} else if selector_id == 2 {
|
||||
// setField(name, value)
|
||||
if argv.len() >= 2 {
|
||||
let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() };
|
||||
@ -260,6 +262,19 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(true));
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
} else if selector_id == 3 {
|
||||
// has(name)
|
||||
if argv.len() >= 1 {
|
||||
let field = match &argv[0] { crate::backend::vm::VMValue::String(s) => s.clone(), v => v.to_string() };
|
||||
let has = inst.get_field_unified(&field).is_some();
|
||||
let buf = tlv_encode_one(&crate::backend::vm::VMValue::Bool(has));
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
} else if selector_id == 4 {
|
||||
// size()
|
||||
let sz = inst.fields_ng.lock().map(|m| m.len() as i64).unwrap_or(0);
|
||||
let buf = tlv_encode_one(&crate::backend::vm::VMValue::Integer(sz));
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -34,12 +34,27 @@ const STRING_METHODS: &[MethodEntry] = &[
|
||||
];
|
||||
static STRINGBOX_TB: TypeBox = TypeBox::new_with("StringBox", STRING_METHODS);
|
||||
|
||||
// --- InstanceBox ---
|
||||
// Representative methods exposed via unified slots for field access and diagnostics.
|
||||
// 1: getField(name)
|
||||
// 2: setField(name, value)
|
||||
// 3: has(name)
|
||||
// 4: size()
|
||||
const INSTANCE_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "getField", arity: 1, slot: 1 },
|
||||
MethodEntry { name: "setField", arity: 2, slot: 2 },
|
||||
MethodEntry { name: "has", arity: 1, slot: 3 },
|
||||
MethodEntry { name: "size", arity: 0, slot: 4 },
|
||||
];
|
||||
static INSTANCEBOX_TB: TypeBox = TypeBox::new_with("InstanceBox", INSTANCE_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),
|
||||
"InstanceBox" => Some(&INSTANCEBOX_TB),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -53,3 +68,12 @@ pub fn resolve_slot_by_name(type_name: &str, method: &str, arity: usize) -> Opti
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Return list of known methods for a type (names only) for diagnostics.
|
||||
pub fn known_methods_for(type_name: &str) -> Option<Vec<&'static str>> {
|
||||
let tb = resolve_typebox_by_name(type_name)?;
|
||||
let mut v: Vec<&'static str> = tb.methods.iter().map(|m| m.name).collect();
|
||||
v.sort();
|
||||
v.dedup();
|
||||
Some(v)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user