feat: Windows native Egui demo success + Paper C unified strategy

🎉 MAJOR MILESTONE: First successful Windows native Egui application
- Fixed stack overflow in plugin initialization
- Windows Egui demo running with JIT host-bridge
- Complete plugin system validation

📚 Paper C: Unified revolution strategy
- Three-paper approach: MIR13 + BoxCall + Unified
- ChatGPT5's "one representation, two execution" concept
- Complete experimental benchmark planning

🛠️ Technical achievements:
- nyash.exe with Cranelift JIT on Windows
- nyash_egui_plugin.dll (4.39MB) successful build
- VM + JIT + host-bridge integration working
- apps/egui-hello/main.nyash: open→uiLabel→run→close pipeline

🔬 Research foundation:
- docs/papers/active/paper-c-unified-revolution/ complete structure
- Benchmark experiments ready for Load/Store elimination validation
- AI-collaborative development methodology documented

This represents Phase 15 self-hosting technical foundation completion.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-09-04 12:15:30 +09:00
parent fb2d8e37d5
commit 02d36cc2cb
7 changed files with 961 additions and 9 deletions

View File

@ -4,6 +4,9 @@ use crate::box_trait::{NyashBox, BoxCore, StringBox, IntegerBox};
use crate::config::nyash_toml_v2::{NyashConfigV2, LibraryDefinition};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use std::path::{Path, PathBuf};
use libloading::{Library, Symbol};
fn dbg_on() -> bool { std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" }
@ -55,8 +58,51 @@ impl PluginLoaderV2 {
Ok(())
}
fn load_plugin(&self, _lib_name: &str, _lib_def: &LibraryDefinition) -> BidResult<()> {
// Keep behavior: real loading logic remains in unified loader; v2 stores minimal entries
fn load_plugin(&self, lib_name: &str, lib_def: &LibraryDefinition) -> BidResult<()> {
// Resolve platform-specific filename from configured base path
let base = Path::new(&lib_def.path);
let mut candidates: Vec<PathBuf> = Vec::new();
if cfg!(target_os = "windows") {
// Try exact + .dll, and without leading 'lib'
candidates.push(base.with_extension("dll"));
if let Some(file) = base.file_name().and_then(|s| s.to_str()) {
if file.starts_with("lib") {
let mut alt = base.to_path_buf();
let alt_file = file.trim_start_matches("lib");
alt.set_file_name(alt_file);
candidates.push(alt.with_extension("dll"));
}
}
} else if cfg!(target_os = "macos") {
candidates.push(base.with_extension("dylib"));
} else {
candidates.push(base.with_extension("so"));
}
let lib_path = candidates.into_iter().find(|p| p.exists()).unwrap_or_else(|| base.to_path_buf());
let lib = unsafe { Library::new(&lib_path) }.map_err(|_| BidError::PluginError)?;
let lib_arc = Arc::new(lib);
// Resolve required invoke symbol (TypeBox v2: nyash_plugin_invoke)
unsafe {
let invoke_sym: Symbol<unsafe extern "C" fn(u32,u32,u32,*const u8,usize,*mut u8,*mut usize) -> i32> =
lib_arc.get(b"nyash_plugin_invoke\0").map_err(|_| BidError::PluginError)?;
// Optional init (best-effort)
if let Ok(init_sym) = lib_arc.get::<Symbol<unsafe extern "C" fn() -> i32>>(b"nyash_plugin_init\0") {
let _ = init_sym();
}
let loaded = LoadedPluginV2 {
_lib: lib_arc.clone(),
box_types: lib_def.boxes.clone(),
typeboxes: HashMap::new(),
init_fn: None,
invoke_fn: *invoke_sym,
};
self.plugins.write().map_err(|_| BidError::PluginError)?.insert(lib_name.to_string(), Arc::new(loaded));
}
Ok(())
}
@ -186,9 +232,18 @@ impl PluginLoaderV2 {
// Get plugin handle
let plugins = self.plugins.read().map_err(|_| BidError::PluginError)?;
let plugin = plugins.get(lib_name).ok_or(BidError::PluginError)?;
// Encode minimal TLV args (support only 0-arity inline)
if !args.is_empty() { return Err(BidError::PluginError); }
let tlv: [u8; 0] = [];
// Encode TLV args (best-effort: i64 for integers, string otherwise)
let mut tlv = crate::runtime::plugin_ffi_common::encode_tlv_header(args.len() as u16);
for a in args {
if let Some(i) = crate::runtime::semantics::coerce_to_i64(a.as_ref()) {
crate::runtime::plugin_ffi_common::encode::i64(&mut tlv, i);
} else if let Some(s) = crate::runtime::semantics::coerce_to_string(a.as_ref()) {
crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &s);
} else {
// Fallback to toString
crate::runtime::plugin_ffi_common::encode::string(&mut tlv, &a.to_string_box().value);
}
}
let (_code, out_len, out) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, method.method_id, instance_id, &tlv);
// Minimal decoding by method name
match method_name {
@ -213,10 +268,39 @@ impl PluginLoaderV2 {
}
pub fn create_box(&self, box_type: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
// Delegate creation to unified host; preserves current behavior
let host = crate::runtime::get_global_plugin_host();
let host = host.read().map_err(|_| BidError::PluginError)?;
host.create_box(box_type, &[])
// Non-recursive: directly call plugin 'birth' and construct PluginBoxV2
let cfg = self.config.as_ref().ok_or(BidError::PluginError)?;
let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml");
let toml_value: toml::Value = toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?).map_err(|_| BidError::PluginError)?;
let (lib_name, _) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?;
// Resolve type_id and method ids
let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?;
let type_id = box_conf.type_id;
let birth_id = box_conf.methods.get("birth").map(|m| m.method_id).ok_or(BidError::InvalidMethod)?;
let fini_id = box_conf.methods.get("fini").map(|m| m.method_id);
// Get loaded plugin invoke
let plugins = self.plugins.read().map_err(|_| BidError::PluginError)?;
let plugin = plugins.get(lib_name).ok_or(BidError::PluginError)?;
// Call birth (no args TLV) and read returned instance id (little-endian u32 in bytes 0..4)
let tlv = crate::runtime::plugin_ffi_common::encode_empty_args();
let (code, out_len, out_buf) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, birth_id, 0, &tlv);
if code != 0 || out_len < 4 { return Err(BidError::PluginError); }
let instance_id = u32::from_le_bytes([out_buf[0], out_buf[1], out_buf[2], out_buf[3]]);
let bx = PluginBoxV2 {
box_type: box_type.to_string(),
inner: Arc::new(PluginHandleInner {
type_id,
invoke_fn: plugin.invoke_fn,
instance_id,
fini_method_id: fini_id,
finalized: std::sync::atomic::AtomicBool::new(false),
}),
};
Ok(Box::new(bx))
}
/// Shutdown singletons: finalize and clear all singleton handles