gui: add EguiBox TypeBox plugin (Windows egui stub)\n\n- plugins: add nyash-egui-plugin with TypeBox (resolve/invoke_id), Windows path for real window via eframe; stub on other OS\n- apps: add apps/egui-hello sample (open→uiLabel→run→close)\n- loader: improve Windows DLL resolution (target triples: x86_64/aarch64 msvc) and lib→dll mapping\n- tests: expand TypeBox vs TLV diff tests up to FileBox; all green\n- docs: update CURRENT_TASK checklist (diff tests completed)\n- config: nyash.toml add EguiBox (type_id=70), plugin registry and methods

This commit is contained in:
Moe Charm
2025-09-03 13:58:52 +09:00
parent f939ad0033
commit ceb22b6c18
33 changed files with 3891 additions and 84 deletions

View File

@ -772,6 +772,45 @@ impl VM {
// Debug logging if enabled
let debug_boxcall = std::env::var("NYASH_VM_DEBUG_BOXCALL").is_ok();
// Fast-path: ConsoleBox.readLine — provide safe stdin fallback with EOF→Void
if let VMValue::BoxRef(arc_box) = &recv {
if let Some(p) = arc_box.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if p.box_type == "ConsoleBox" && method == "readLine" {
use std::io::Read;
let mut s = String::new();
let mut stdin = std::io::stdin();
// Read bytes until '\n' or EOF
let mut buf = [0u8; 1];
loop {
match stdin.read(&mut buf) {
Ok(0) => { // EOF → return NullBox
if let Some(dst_id) = dst {
let nb = crate::boxes::null_box::NullBox::new();
self.set_value(dst_id, VMValue::from_nyash_box(Box::new(nb)));
}
return Ok(ControlFlow::Continue);
}
Ok(_) => {
let ch = buf[0] as char;
if ch == '\n' { break; }
s.push(ch);
if s.len() > 1_000_000 { break; }
}
Err(_) => { // On error, return NullBox
if let Some(dst_id) = dst {
let nb = crate::boxes::null_box::NullBox::new();
self.set_value(dst_id, VMValue::from_nyash_box(Box::new(nb)));
}
return Ok(ControlFlow::Continue);
}
}
}
if let Some(dst_id) = dst { self.set_value(dst_id, VMValue::String(s)); }
return Ok(ControlFlow::Continue);
}
}
}
// Phase 12 Tier-0: vtable優先経路雛形
if crate::config::env::abi_vtable() {
@ -1236,13 +1275,101 @@ impl VM {
/// Phase 12 Tier-0: vtable優先経路の雛形常に未処理
/// 目的: 将来のTypeBox ABI配線ポイントを先置きしても既存挙動を変えないこと。
fn try_boxcall_vtable_stub(&mut self, _dst: Option<ValueId>, _recv: &VMValue, _method: &str, _method_id: Option<u16>, _args: &[ValueId]) -> Option<Result<ControlFlow, VMError>> {
if crate::config::env::vm_vt_trace() {
match _recv {
VMValue::BoxRef(b) => eprintln!("[VT] probe recv_ty={} method={} argc={}", b.type_name(), _method, _args.len()),
other => eprintln!("[VT] probe recv_prim={:?} method={} argc={}", other, _method, _args.len()),
}
}
// Tier-1 PoC: Array/Map/String の get/set/len/size/has を vtable 経路で処理read-onlyまたは明示barrier不要
if let VMValue::BoxRef(b) = _recv {
// 型解決(雛形レジストリ使用)
let ty_name = b.type_name();
if let Some(_tb) = crate::runtime::type_registry::resolve_typebox_by_name(ty_name) {
// PluginBoxV2 は実型名でレジストリ解決する
let ty_name_for_reg: std::borrow::Cow<'_, str> = if let Some(p) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
std::borrow::Cow::Owned(p.box_type.clone())
} else {
std::borrow::Cow::Borrowed(ty_name)
};
if let Some(_tb) = crate::runtime::type_registry::resolve_typebox_by_name(&ty_name_for_reg) {
// 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_for_reg, _method, _args.len());
// PluginBoxV2: vtable経由で host.invoke_instance_method を使用(内蔵廃止と整合)
if let Some(p) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
if crate::config::env::vm_vt_trace() { eprintln!("[VT] plugin recv ty={} method={} arity={}", ty_name, _method, _args.len()); }
// 事前に引数を NyashBox に変換
let mut nyash_args: Vec<Box<dyn NyashBox>> = Vec::with_capacity(_args.len());
for aid in _args.iter() {
if let Ok(v) = self.get_value(*aid) { nyash_args.push(v.to_nyash_box()); } else { nyash_args.push(Box::new(crate::box_trait::VoidBox::new())); }
}
// Instance/Map/Array/String などに対して型名とスロットで分岐(最小セット)
match ty_name {
"MapBox" => {
match slot {
Some(200) | Some(201) => { // size/len
let host = crate::runtime::get_global_plugin_host();
let ro = host.read().unwrap();
if let Ok(val_opt) = ro.invoke_instance_method("MapBox", _method, p.inner.instance_id, &[]) {
if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } }
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
return Some(Ok(ControlFlow::Continue));
}
}
Some(202) | Some(203) | Some(204) => { // has/get/set
if matches!(slot, Some(204)) {
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Plugin.Map.set");
}
let host = crate::runtime::get_global_plugin_host();
let ro = host.read().unwrap();
// Route string-key variants to getS/hasS when applicable
let mut method_eff = _method;
if (matches!(slot, Some(202)) && _args.len() >= 1) || (matches!(slot, Some(203)) && _args.len() >= 1) {
if let Ok(a0v) = self.get_value(_args[0]) {
if matches!(a0v, VMValue::String(_)) { method_eff = if matches!(slot, Some(203)) { "getS" } else { "hasS" }; }
}
}
if let Ok(val_opt) = ro.invoke_instance_method("MapBox", method_eff, p.inner.instance_id, &nyash_args) {
if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } }
else if _dst.is_some() { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::Void); } }
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
return Some(Ok(ControlFlow::Continue));
}
}
_ => {}
}
}
"ArrayBox" => {
match slot {
Some(100) | Some(101) | Some(102) => {
if matches!(slot, Some(101)) {
crate::backend::gc_helpers::gc_write_barrier_site(&self.runtime, "VTable.Plugin.Array.set");
}
let host = crate::runtime::get_global_plugin_host();
let ro = host.read().unwrap();
if let Ok(val_opt) = ro.invoke_instance_method("ArrayBox", _method, p.inner.instance_id, &nyash_args) {
if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } }
else if _dst.is_some() { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::Void); } }
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
return Some(Ok(ControlFlow::Continue));
}
}
_ => {}
}
}
"StringBox" => {
if matches!(slot, Some(300)) {
let host = crate::runtime::get_global_plugin_host();
let ro = host.read().unwrap();
if let Ok(val_opt) = ro.invoke_instance_method("StringBox", _method, p.inner.instance_id, &[]) {
if let Some(out) = val_opt { if let Some(dst_id) = _dst { self.set_value(dst_id, VMValue::from_nyash_box(out)); } }
self.boxcall_hits_vtable = self.boxcall_hits_vtable.saturating_add(1);
return Some(Ok(ControlFlow::Continue));
}
}
}
_ => {}
}
}
// InstanceBox: getField/setField/has/size
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
match slot {