chore(fmt): add legacy stubs and strip trailing whitespace to unblock cargo fmt
This commit is contained in:
@ -1,5 +1,5 @@
|
||||
//! Boxファクトリレジストリ - Box生成の中央管理
|
||||
//!
|
||||
//!
|
||||
//! プラグインBoxを中心にBox生成を管理する(Plugin-First)。
|
||||
//! 旧ビルトイン経路は互換目的のAPIとして最小限に保持(テスト用途)。
|
||||
|
||||
@ -12,7 +12,7 @@ use std::sync::{Arc, RwLock};
|
||||
pub enum BoxProvider {
|
||||
/// 互換用ビルトイン実装(Rust関数、現在は原則未使用)
|
||||
Builtin(BoxConstructor),
|
||||
|
||||
|
||||
/// プラグイン実装(プラグイン名を保持)
|
||||
Plugin(String),
|
||||
}
|
||||
@ -33,36 +33,38 @@ impl BoxFactoryRegistry {
|
||||
providers: RwLock::new(HashMap::new()),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// 互換用ビルトインBoxを登録(通常は使用しない)
|
||||
pub fn register_builtin(&self, name: &str, constructor: BoxConstructor) {
|
||||
let mut providers = self.providers.write().unwrap();
|
||||
providers.insert(name.to_string(), BoxProvider::Builtin(constructor));
|
||||
}
|
||||
|
||||
|
||||
/// プラグイン設定を適用(既存のビルトインを上書き)
|
||||
pub fn apply_plugin_config(&self, config: &PluginConfig) {
|
||||
let mut providers = self.providers.write().unwrap();
|
||||
|
||||
|
||||
for (box_name, plugin_name) in &config.plugins {
|
||||
providers.insert(
|
||||
box_name.clone(),
|
||||
BoxProvider::Plugin(plugin_name.clone())
|
||||
);
|
||||
providers.insert(box_name.clone(), BoxProvider::Plugin(plugin_name.clone()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// Box名からプロバイダーを取得
|
||||
pub fn get_provider(&self, name: &str) -> Option<BoxProvider> {
|
||||
let providers = self.providers.read().unwrap();
|
||||
providers.get(name).cloned()
|
||||
}
|
||||
|
||||
|
||||
/// Boxを生成
|
||||
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String> {
|
||||
let provider = self.get_provider(name)
|
||||
pub fn create_box(
|
||||
&self,
|
||||
name: &str,
|
||||
args: &[Box<dyn NyashBox>],
|
||||
) -> Result<Box<dyn NyashBox>, String> {
|
||||
let provider = self
|
||||
.get_provider(name)
|
||||
.ok_or_else(|| format!("Unknown Box type: {}", name))?;
|
||||
|
||||
|
||||
match provider {
|
||||
BoxProvider::Builtin(constructor) => {
|
||||
// ビルトイン実装を直接呼び出し
|
||||
@ -74,17 +76,29 @@ impl BoxFactoryRegistry {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// プラグインBoxを生成(unified facade→v2)
|
||||
fn create_plugin_box(&self, plugin_name: &str, box_name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String> {
|
||||
fn create_plugin_box(
|
||||
&self,
|
||||
plugin_name: &str,
|
||||
box_name: &str,
|
||||
args: &[Box<dyn NyashBox>],
|
||||
) -> Result<Box<dyn NyashBox>, String> {
|
||||
use crate::runtime::get_global_plugin_host;
|
||||
let host = get_global_plugin_host();
|
||||
let host = host.read().unwrap();
|
||||
if std::env::var("NYASH_DEBUG_PLUGIN").ok().as_deref() == Some("1") {
|
||||
eprintln!("[BoxFactoryRegistry] create_plugin_box: plugin={} box_type={}", plugin_name, box_name);
|
||||
eprintln!(
|
||||
"[BoxFactoryRegistry] create_plugin_box: plugin={} box_type={}",
|
||||
plugin_name, box_name
|
||||
);
|
||||
}
|
||||
host.create_box(box_name, args)
|
||||
.map_err(|e| format!("Failed to create {} from plugin {}: {:?}", box_name, plugin_name, e))
|
||||
host.create_box(box_name, args).map_err(|e| {
|
||||
format!(
|
||||
"Failed to create {} from plugin {}: {:?}",
|
||||
box_name, plugin_name, e
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@ -100,7 +114,7 @@ impl Clone for BoxProvider {
|
||||
// グローバルレジストリインスタンス
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
static GLOBAL_REGISTRY: Lazy<Arc<BoxFactoryRegistry>> =
|
||||
static GLOBAL_REGISTRY: Lazy<Arc<BoxFactoryRegistry>> =
|
||||
Lazy::new(|| Arc::new(BoxFactoryRegistry::new()));
|
||||
|
||||
/// グローバルレジストリを取得
|
||||
@ -112,7 +126,7 @@ pub fn get_global_registry() -> Arc<BoxFactoryRegistry> {
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::box_trait::StringBox;
|
||||
|
||||
|
||||
fn test_string_constructor(args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String> {
|
||||
if args.is_empty() {
|
||||
Ok(Box::new(StringBox::new("")))
|
||||
@ -120,26 +134,28 @@ mod tests {
|
||||
Ok(Box::new(StringBox::new(&args[0].to_string_box().value)))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_builtin_registration() {
|
||||
let registry = BoxFactoryRegistry::new();
|
||||
registry.register_builtin("StringBox", test_string_constructor);
|
||||
|
||||
|
||||
let result = registry.create_box("StringBox", &[]).unwrap();
|
||||
assert_eq!(result.to_string_box().value, "");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_plugin_override() {
|
||||
let registry = BoxFactoryRegistry::new();
|
||||
registry.register_builtin("FileBox", test_string_constructor);
|
||||
|
||||
|
||||
// プラグイン設定で上書き
|
||||
let mut config = PluginConfig::default();
|
||||
config.plugins.insert("FileBox".to_string(), "filebox".to_string());
|
||||
config
|
||||
.plugins
|
||||
.insert("FileBox".to_string(), "filebox".to_string());
|
||||
registry.apply_plugin_config(&config);
|
||||
|
||||
|
||||
// プロバイダーがプラグインに変わっているか確認
|
||||
match registry.get_provider("FileBox").unwrap() {
|
||||
BoxProvider::Plugin(name) => assert_eq!(name, "filebox"),
|
||||
|
||||
@ -27,4 +27,3 @@ pub fn bump_many(labels: &[String]) {
|
||||
*e = e.saturating_add(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -13,45 +13,166 @@ pub struct ExternSpec {
|
||||
pub slot: Option<u16>,
|
||||
}
|
||||
|
||||
static EXTERNS: Lazy<Vec<ExternSpec>> = Lazy::new(|| vec![
|
||||
// console
|
||||
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) },
|
||||
ExternSpec { iface: "env.future", method: "set", min_arity: 2, max_arity: 2, slot: Some(21) },
|
||||
ExternSpec { iface: "env.future", method: "await", min_arity: 1, max_arity: 1, slot: Some(22) },
|
||||
// core-13 pure support shims
|
||||
ExternSpec { iface: "env.local", method: "get", min_arity: 1, max_arity: 1, slot: Some(40) },
|
||||
ExternSpec { iface: "env.local", method: "set", min_arity: 2, max_arity: 2, slot: Some(41) },
|
||||
ExternSpec { iface: "env.box", method: "new", min_arity: 1, max_arity: 255, slot: Some(50) },
|
||||
]);
|
||||
static EXTERNS: Lazy<Vec<ExternSpec>> = Lazy::new(|| {
|
||||
vec![
|
||||
// console
|
||||
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),
|
||||
},
|
||||
ExternSpec {
|
||||
iface: "env.future",
|
||||
method: "set",
|
||||
min_arity: 2,
|
||||
max_arity: 2,
|
||||
slot: Some(21),
|
||||
},
|
||||
ExternSpec {
|
||||
iface: "env.future",
|
||||
method: "await",
|
||||
min_arity: 1,
|
||||
max_arity: 1,
|
||||
slot: Some(22),
|
||||
},
|
||||
// core-13 pure support shims
|
||||
ExternSpec {
|
||||
iface: "env.local",
|
||||
method: "get",
|
||||
min_arity: 1,
|
||||
max_arity: 1,
|
||||
slot: Some(40),
|
||||
},
|
||||
ExternSpec {
|
||||
iface: "env.local",
|
||||
method: "set",
|
||||
min_arity: 2,
|
||||
max_arity: 2,
|
||||
slot: Some(41),
|
||||
},
|
||||
ExternSpec {
|
||||
iface: "env.box",
|
||||
method: "new",
|
||||
min_arity: 1,
|
||||
max_arity: 255,
|
||||
slot: Some(50),
|
||||
},
|
||||
]
|
||||
});
|
||||
|
||||
pub fn resolve(iface: &str, method: &str) -> Option<ExternSpec> {
|
||||
EXTERNS.iter().copied().find(|e| e.iface == iface && e.method == method)
|
||||
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
|
||||
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
|
||||
v.sort();
|
||||
v.dedup();
|
||||
v
|
||||
}
|
||||
|
||||
/// Resolve slot id for an extern call (if assigned)
|
||||
@ -62,7 +183,15 @@ pub fn resolve_slot(iface: &str, method: &str) -> Option<u16> {
|
||||
/// Check arity against registry; returns Ok or an explanatory error string
|
||||
pub fn check_arity(iface: &str, method: &str, argc: usize) -> Result<(), String> {
|
||||
if let Some(s) = resolve(iface, method) {
|
||||
if argc as u8 >= s.min_arity && argc as u8 <= s.max_arity { Ok(()) }
|
||||
else { Err(format!("arity {} out of range {}..{}", argc, s.min_arity, s.max_arity)) }
|
||||
} else { Err("unknown extern".to_string()) }
|
||||
if argc as u8 >= s.min_arity && argc as u8 <= s.max_arity {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(format!(
|
||||
"arity {} out of range {}..{}",
|
||||
argc, s.min_arity, s.max_arity
|
||||
))
|
||||
}
|
||||
} else {
|
||||
Err("unknown extern".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,10 @@
|
||||
//! Default implementation is a no-op. Real collectors can plug later.
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub enum BarrierKind { Read, Write }
|
||||
pub enum BarrierKind {
|
||||
Read,
|
||||
Write,
|
||||
}
|
||||
|
||||
/// GC hooks that execution engines may call at key points.
|
||||
/// Implementations must be Send + Sync for multi-thread preparation.
|
||||
@ -14,7 +17,9 @@ pub trait GcHooks: Send + Sync {
|
||||
/// Memory barrier hint for loads/stores.
|
||||
fn barrier(&self, _kind: BarrierKind) {}
|
||||
/// Optional counters snapshot for diagnostics. Default: None.
|
||||
fn snapshot_counters(&self) -> Option<(u64, u64, u64)> { None }
|
||||
fn snapshot_counters(&self) -> Option<(u64, u64, u64)> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Default no-op hooks.
|
||||
@ -55,8 +60,12 @@ impl GcHooks for CountingGc {
|
||||
}
|
||||
fn barrier(&self, kind: BarrierKind) {
|
||||
match kind {
|
||||
BarrierKind::Read => { self.barrier_reads.fetch_add(1, Ordering::Relaxed); }
|
||||
BarrierKind::Write => { self.barrier_writes.fetch_add(1, Ordering::Relaxed); }
|
||||
BarrierKind::Read => {
|
||||
self.barrier_reads.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
BarrierKind::Write => {
|
||||
self.barrier_writes.fetch_add(1, Ordering::Relaxed);
|
||||
}
|
||||
}
|
||||
}
|
||||
fn snapshot_counters(&self) -> Option<(u64, u64, u64)> {
|
||||
|
||||
@ -3,51 +3,100 @@
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use super::{gc::GcHooks, scheduler::Scheduler};
|
||||
use super::scheduler::CancellationToken;
|
||||
use super::{gc::GcHooks, scheduler::Scheduler};
|
||||
|
||||
static GLOBAL_GC: OnceCell<RwLock<Option<Arc<dyn GcHooks>>>> = OnceCell::new();
|
||||
static GLOBAL_SCHED: OnceCell<RwLock<Option<Arc<dyn Scheduler>>>> = OnceCell::new();
|
||||
// Phase 2 scaffold: current task group's cancellation token (no-op default)
|
||||
static GLOBAL_CUR_TOKEN: OnceCell<RwLock<Option<CancellationToken>>> = OnceCell::new();
|
||||
// Phase 2 scaffold: current group's child futures registry (best-effort)
|
||||
static GLOBAL_GROUP_FUTURES: OnceCell<RwLock<Vec<crate::boxes::future::FutureWeak>>> = OnceCell::new();
|
||||
static GLOBAL_GROUP_FUTURES: OnceCell<RwLock<Vec<crate::boxes::future::FutureWeak>>> =
|
||||
OnceCell::new();
|
||||
// Strong ownership list for implicit group (pre-TaskGroup actualization)
|
||||
static GLOBAL_GROUP_STRONG: OnceCell<RwLock<Vec<crate::boxes::future::FutureBox>>> = OnceCell::new();
|
||||
static GLOBAL_GROUP_STRONG: OnceCell<RwLock<Vec<crate::boxes::future::FutureBox>>> =
|
||||
OnceCell::new();
|
||||
// Simple scope depth counter for implicit group (join-at-scope-exit footing)
|
||||
static TASK_SCOPE_DEPTH: OnceCell<RwLock<usize>> = OnceCell::new();
|
||||
// TaskGroup scope stack (explicit group ownership per function scope)
|
||||
static TASK_GROUP_STACK: OnceCell<RwLock<Vec<std::sync::Arc<crate::boxes::task_group_box::TaskGroupInner>>>> = OnceCell::new();
|
||||
static TASK_GROUP_STACK: OnceCell<
|
||||
RwLock<Vec<std::sync::Arc<crate::boxes::task_group_box::TaskGroupInner>>>,
|
||||
> = OnceCell::new();
|
||||
|
||||
fn gc_cell() -> &'static RwLock<Option<Arc<dyn GcHooks>>> { GLOBAL_GC.get_or_init(|| RwLock::new(None)) }
|
||||
fn sched_cell() -> &'static RwLock<Option<Arc<dyn Scheduler>>> { GLOBAL_SCHED.get_or_init(|| RwLock::new(None)) }
|
||||
fn token_cell() -> &'static RwLock<Option<CancellationToken>> { GLOBAL_CUR_TOKEN.get_or_init(|| RwLock::new(None)) }
|
||||
fn futures_cell() -> &'static RwLock<Vec<crate::boxes::future::FutureWeak>> { GLOBAL_GROUP_FUTURES.get_or_init(|| RwLock::new(Vec::new())) }
|
||||
fn strong_cell() -> &'static RwLock<Vec<crate::boxes::future::FutureBox>> { GLOBAL_GROUP_STRONG.get_or_init(|| RwLock::new(Vec::new())) }
|
||||
fn scope_depth_cell() -> &'static RwLock<usize> { TASK_SCOPE_DEPTH.get_or_init(|| RwLock::new(0)) }
|
||||
fn group_stack_cell() -> &'static RwLock<Vec<std::sync::Arc<crate::boxes::task_group_box::TaskGroupInner>>> { TASK_GROUP_STACK.get_or_init(|| RwLock::new(Vec::new())) }
|
||||
|
||||
pub fn set_from_runtime(rt: &crate::runtime::nyash_runtime::NyashRuntime) {
|
||||
if let Ok(mut g) = gc_cell().write() { *g = Some(rt.gc.clone()); }
|
||||
if let Ok(mut s) = sched_cell().write() { *s = rt.scheduler.as_ref().cloned(); }
|
||||
// Optional: initialize a fresh token for the runtime's root group (Phase 2 wiring)
|
||||
if let Ok(mut t) = token_cell().write() { if t.is_none() { *t = Some(CancellationToken::new()); } }
|
||||
// Reset group futures registry on new runtime
|
||||
if let Ok(mut f) = futures_cell().write() { f.clear(); }
|
||||
if let Ok(mut s) = strong_cell().write() { s.clear(); }
|
||||
if let Ok(mut d) = scope_depth_cell().write() { *d = 0; }
|
||||
if let Ok(mut st) = group_stack_cell().write() { st.clear(); }
|
||||
fn gc_cell() -> &'static RwLock<Option<Arc<dyn GcHooks>>> {
|
||||
GLOBAL_GC.get_or_init(|| RwLock::new(None))
|
||||
}
|
||||
fn sched_cell() -> &'static RwLock<Option<Arc<dyn Scheduler>>> {
|
||||
GLOBAL_SCHED.get_or_init(|| RwLock::new(None))
|
||||
}
|
||||
fn token_cell() -> &'static RwLock<Option<CancellationToken>> {
|
||||
GLOBAL_CUR_TOKEN.get_or_init(|| RwLock::new(None))
|
||||
}
|
||||
fn futures_cell() -> &'static RwLock<Vec<crate::boxes::future::FutureWeak>> {
|
||||
GLOBAL_GROUP_FUTURES.get_or_init(|| RwLock::new(Vec::new()))
|
||||
}
|
||||
fn strong_cell() -> &'static RwLock<Vec<crate::boxes::future::FutureBox>> {
|
||||
GLOBAL_GROUP_STRONG.get_or_init(|| RwLock::new(Vec::new()))
|
||||
}
|
||||
fn scope_depth_cell() -> &'static RwLock<usize> {
|
||||
TASK_SCOPE_DEPTH.get_or_init(|| RwLock::new(0))
|
||||
}
|
||||
fn group_stack_cell(
|
||||
) -> &'static RwLock<Vec<std::sync::Arc<crate::boxes::task_group_box::TaskGroupInner>>> {
|
||||
TASK_GROUP_STACK.get_or_init(|| RwLock::new(Vec::new()))
|
||||
}
|
||||
|
||||
pub fn set_gc(gc: Arc<dyn GcHooks>) { if let Ok(mut g) = gc_cell().write() { *g = Some(gc); } }
|
||||
pub fn set_scheduler(s: Arc<dyn Scheduler>) { if let Ok(mut w) = sched_cell().write() { *w = Some(s); } }
|
||||
pub fn set_from_runtime(rt: &crate::runtime::nyash_runtime::NyashRuntime) {
|
||||
if let Ok(mut g) = gc_cell().write() {
|
||||
*g = Some(rt.gc.clone());
|
||||
}
|
||||
if let Ok(mut s) = sched_cell().write() {
|
||||
*s = rt.scheduler.as_ref().cloned();
|
||||
}
|
||||
// Optional: initialize a fresh token for the runtime's root group (Phase 2 wiring)
|
||||
if let Ok(mut t) = token_cell().write() {
|
||||
if t.is_none() {
|
||||
*t = Some(CancellationToken::new());
|
||||
}
|
||||
}
|
||||
// Reset group futures registry on new runtime
|
||||
if let Ok(mut f) = futures_cell().write() {
|
||||
f.clear();
|
||||
}
|
||||
if let Ok(mut s) = strong_cell().write() {
|
||||
s.clear();
|
||||
}
|
||||
if let Ok(mut d) = scope_depth_cell().write() {
|
||||
*d = 0;
|
||||
}
|
||||
if let Ok(mut st) = group_stack_cell().write() {
|
||||
st.clear();
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_gc(gc: Arc<dyn GcHooks>) {
|
||||
if let Ok(mut g) = gc_cell().write() {
|
||||
*g = Some(gc);
|
||||
}
|
||||
}
|
||||
pub fn set_scheduler(s: Arc<dyn Scheduler>) {
|
||||
if let Ok(mut w) = sched_cell().write() {
|
||||
*w = Some(s);
|
||||
}
|
||||
}
|
||||
/// Set the current task group's cancellation token (scaffold).
|
||||
pub fn set_current_group_token(tok: CancellationToken) { if let Ok(mut w) = token_cell().write() { *w = Some(tok); } }
|
||||
pub fn set_current_group_token(tok: CancellationToken) {
|
||||
if let Ok(mut w) = token_cell().write() {
|
||||
*w = Some(tok);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the current task group's cancellation token (no-op default).
|
||||
pub fn current_group_token() -> CancellationToken {
|
||||
if let Ok(r) = token_cell().read() {
|
||||
if let Some(t) = r.as_ref() { return t.clone(); }
|
||||
if let Some(t) = r.as_ref() {
|
||||
return t.clone();
|
||||
}
|
||||
}
|
||||
CancellationToken::new()
|
||||
}
|
||||
@ -57,12 +106,19 @@ pub fn register_future_to_current_group(fut: &crate::boxes::future::FutureBox) {
|
||||
// Prefer explicit current TaskGroup at top of stack
|
||||
if let Ok(st) = group_stack_cell().read() {
|
||||
if let Some(inner) = st.last() {
|
||||
if let Ok(mut v) = inner.strong.lock() { v.push(fut.clone()); return; }
|
||||
if let Ok(mut v) = inner.strong.lock() {
|
||||
v.push(fut.clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Fallback to implicit global group
|
||||
if let Ok(mut list) = futures_cell().write() { list.push(fut.downgrade()); }
|
||||
if let Ok(mut s) = strong_cell().write() { s.push(fut.clone()); }
|
||||
if let Ok(mut list) = futures_cell().write() {
|
||||
list.push(fut.downgrade());
|
||||
}
|
||||
if let Ok(mut s) = strong_cell().write() {
|
||||
s.push(fut.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// Join all currently registered futures with a coarse timeout guard.
|
||||
@ -74,36 +130,57 @@ pub fn join_all_registered_futures(timeout_ms: u64) {
|
||||
// purge list of dropped or completed futures opportunistically
|
||||
{
|
||||
// purge weak list: keep only upgradeable futures
|
||||
if let Ok(mut list) = futures_cell().write() { list.retain(|fw| fw.is_ready().is_some()); }
|
||||
if let Ok(mut list) = futures_cell().write() {
|
||||
list.retain(|fw| fw.is_ready().is_some());
|
||||
}
|
||||
// purge strong list: remove completed futures to reduce retention
|
||||
if let Ok(mut s) = strong_cell().write() { s.retain(|f| !f.ready()); }
|
||||
if let Ok(mut s) = strong_cell().write() {
|
||||
s.retain(|f| !f.ready());
|
||||
}
|
||||
}
|
||||
// check readiness
|
||||
{
|
||||
if let Ok(list) = futures_cell().read() {
|
||||
for fw in list.iter() {
|
||||
if let Some(ready) = fw.is_ready() {
|
||||
if !ready { all_ready = false; break; }
|
||||
if !ready {
|
||||
all_ready = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if all_ready { break; }
|
||||
if Instant::now() >= deadline { break; }
|
||||
if all_ready {
|
||||
break;
|
||||
}
|
||||
if Instant::now() >= deadline {
|
||||
break;
|
||||
}
|
||||
safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
}
|
||||
// Final sweep
|
||||
if let Ok(mut s) = strong_cell().write() { s.retain(|f| !f.ready()); }
|
||||
if let Ok(mut list) = futures_cell().write() { list.retain(|fw| matches!(fw.is_ready(), Some(false))); }
|
||||
if let Ok(mut s) = strong_cell().write() {
|
||||
s.retain(|f| !f.ready());
|
||||
}
|
||||
if let Ok(mut list) = futures_cell().write() {
|
||||
list.retain(|fw| matches!(fw.is_ready(), Some(false)));
|
||||
}
|
||||
}
|
||||
|
||||
/// Push a task scope (footing). On pop of the outermost scope, perform a best-effort join.
|
||||
pub fn push_task_scope() {
|
||||
if let Ok(mut d) = scope_depth_cell().write() { *d += 1; }
|
||||
if let Ok(mut d) = scope_depth_cell().write() {
|
||||
*d += 1;
|
||||
}
|
||||
// Push a new explicit TaskGroup for this scope
|
||||
if let Ok(mut st) = group_stack_cell().write() {
|
||||
st.push(std::sync::Arc::new(crate::boxes::task_group_box::TaskGroupInner { strong: std::sync::Mutex::new(Vec::new()) }));
|
||||
st.push(std::sync::Arc::new(
|
||||
crate::boxes::task_group_box::TaskGroupInner {
|
||||
strong: std::sync::Mutex::new(Vec::new()),
|
||||
},
|
||||
));
|
||||
}
|
||||
// Set a fresh cancellation token for this scope (best-effort)
|
||||
set_current_group_token(CancellationToken::new());
|
||||
@ -115,22 +192,40 @@ pub fn pop_task_scope() {
|
||||
let mut popped: Option<std::sync::Arc<crate::boxes::task_group_box::TaskGroupInner>> = None;
|
||||
{
|
||||
if let Ok(mut d) = scope_depth_cell().write() {
|
||||
if *d > 0 { *d -= 1; }
|
||||
if *d == 0 { do_join = true; }
|
||||
if *d > 0 {
|
||||
*d -= 1;
|
||||
}
|
||||
if *d == 0 {
|
||||
do_join = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Pop explicit group for this scope
|
||||
if let Ok(mut st) = group_stack_cell().write() { popped = st.pop(); }
|
||||
if let Ok(mut st) = group_stack_cell().write() {
|
||||
popped = st.pop();
|
||||
}
|
||||
if do_join {
|
||||
let ms: u64 = std::env::var("NYASH_TASK_SCOPE_JOIN_MS").ok().and_then(|s| s.parse().ok()).unwrap_or(1000);
|
||||
let ms: u64 = std::env::var("NYASH_TASK_SCOPE_JOIN_MS")
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(1000);
|
||||
if let Some(inner) = popped {
|
||||
// Join this group's outstanding futures
|
||||
let deadline = std::time::Instant::now() + std::time::Duration::from_millis(ms);
|
||||
loop {
|
||||
let mut all_ready = true;
|
||||
if let Ok(mut list) = inner.strong.lock() { list.retain(|f| !f.ready()); if !list.is_empty() { all_ready = false; } }
|
||||
if all_ready { break; }
|
||||
if std::time::Instant::now() >= deadline { break; }
|
||||
if let Ok(mut list) = inner.strong.lock() {
|
||||
list.retain(|f| !f.ready());
|
||||
if !list.is_empty() {
|
||||
all_ready = false;
|
||||
}
|
||||
}
|
||||
if all_ready {
|
||||
break;
|
||||
}
|
||||
if std::time::Instant::now() >= deadline {
|
||||
break;
|
||||
}
|
||||
safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
}
|
||||
@ -146,10 +241,14 @@ pub fn pop_task_scope() {
|
||||
/// Perform a runtime safepoint and poll the scheduler if available.
|
||||
pub fn safepoint_and_poll() {
|
||||
if let Ok(g) = gc_cell().read() {
|
||||
if let Some(gc) = g.as_ref() { gc.safepoint(); }
|
||||
if let Some(gc) = g.as_ref() {
|
||||
gc.safepoint();
|
||||
}
|
||||
}
|
||||
if let Ok(s) = sched_cell().read() {
|
||||
if let Some(sched) = s.as_ref() { sched.poll(); }
|
||||
if let Some(sched) = s.as_ref() {
|
||||
sched.poll();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -168,7 +267,11 @@ pub fn spawn_task(name: &str, f: Box<dyn FnOnce() + Send + 'static>) -> bool {
|
||||
}
|
||||
|
||||
/// Spawn a task bound to a cancellation token when available (skeleton).
|
||||
pub fn spawn_task_with_token(name: &str, token: crate::runtime::scheduler::CancellationToken, f: Box<dyn FnOnce() + Send + 'static>) -> bool {
|
||||
pub fn spawn_task_with_token(
|
||||
name: &str,
|
||||
token: crate::runtime::scheduler::CancellationToken,
|
||||
f: Box<dyn FnOnce() + Send + 'static>,
|
||||
) -> bool {
|
||||
if let Ok(s) = sched_cell().read() {
|
||||
if let Some(sched) = s.as_ref() {
|
||||
sched.spawn_with_token(name, token, f);
|
||||
|
||||
@ -13,13 +13,23 @@ 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())); }
|
||||
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 {
|
||||
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) }) }
|
||||
if p.is_null() {
|
||||
None
|
||||
} else {
|
||||
Some(unsafe { f(&mut *p) })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -45,35 +55,59 @@ fn tlv_encode_one(val: &crate::backend::vm::VMValue) -> Vec<u8> {
|
||||
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))),
|
||||
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 }
|
||||
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))),
|
||||
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)); }
|
||||
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 }
|
||||
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) }
|
||||
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; }
|
||||
if out_ptr.is_null() || out_len.is_null() {
|
||||
return -2;
|
||||
}
|
||||
let cap = *out_len;
|
||||
if cap < buf.len() { return -3; }
|
||||
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();
|
||||
@ -82,62 +116,115 @@ fn encode_out(out_ptr: *mut u8, out_len: *mut usize, buf: &[u8]) -> i32 {
|
||||
}
|
||||
|
||||
#[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 {
|
||||
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();
|
||||
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 {
|
||||
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); }
|
||||
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>() {
|
||||
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 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() };
|
||||
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");
|
||||
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::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::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); }
|
||||
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);
|
||||
}
|
||||
@ -145,27 +232,47 @@ pub extern "C" fn nyrt_host_call_name(handle: u64, method_ptr: *const u8, method
|
||||
}
|
||||
}
|
||||
// ArrayBox get/set
|
||||
if let Some(arr) = recv_arc.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
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 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 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::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");
|
||||
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);
|
||||
@ -188,7 +295,12 @@ fn plugin_box_from_handle(type_id: u32, instance_id: u32) -> Option<std::sync::A
|
||||
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 }
|
||||
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):
|
||||
@ -206,66 +318,125 @@ fn plugin_box_from_handle(_type_id: u32, _instance_id: u32) -> Option<std::sync:
|
||||
// 204: MapBox.set(key:any, value:any) -> any
|
||||
// 300: StringBox.len() -> i64
|
||||
#[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 {
|
||||
let recv_arc = match crate::runtime::host_handles::get(handle) { Some(a) => a, None => return -1 };
|
||||
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); }
|
||||
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 | 3 | 4 => {
|
||||
if let Some(inst) = recv_arc.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||
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 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 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() };
|
||||
let _ = with_current_vm_mut(|vm| { crate::backend::gc_helpers::gc_write_barrier_site(vm.runtime_ref(), "HostAPI.setField"); });
|
||||
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::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); }
|
||||
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);
|
||||
}
|
||||
} 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 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);
|
||||
@ -279,36 +450,62 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
}
|
||||
}
|
||||
100 | 101 | 102 => {
|
||||
if let Some(arr) = recv_arc.as_any().downcast_ref::<crate::boxes::array::ArrayBox>() {
|
||||
if let Some(arr) = recv_arc
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::array::ArrayBox>()
|
||||
{
|
||||
match selector_id {
|
||||
100 => { // get(index)
|
||||
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 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)
|
||||
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 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::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 _ = 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()
|
||||
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);
|
||||
@ -318,7 +515,10 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
}
|
||||
}
|
||||
200 | 201 | 202 | 203 | 204 => {
|
||||
if let Some(map) = recv_arc.as_any().downcast_ref::<crate::boxes::map_box::MapBox>() {
|
||||
if let Some(map) = recv_arc
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::map_box::MapBox>()
|
||||
{
|
||||
match selector_id {
|
||||
200 | 201 => {
|
||||
let out = map.size();
|
||||
@ -326,16 +526,27 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
let buf = tlv_encode_one(&vmv);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
202 => { // has(key)
|
||||
202 => {
|
||||
// has(key)
|
||||
if argv.len() >= 1 {
|
||||
let key_box: Box<dyn NyashBox> = match argv[0].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
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::Integer(i) => {
|
||||
Box::new(crate::box_trait::IntegerBox::new(i))
|
||||
}
|
||||
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(),
|
||||
crate::backend::vm::VMValue::Future(fu) => Box::new(fu),
|
||||
crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
};
|
||||
let out = map.has(key_box);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
@ -343,16 +554,27 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
}
|
||||
203 => { // get(key)
|
||||
203 => {
|
||||
// get(key)
|
||||
if argv.len() >= 1 {
|
||||
let key_box: Box<dyn NyashBox> = match argv[0].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
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::Integer(i) => {
|
||||
Box::new(crate::box_trait::IntegerBox::new(i))
|
||||
}
|
||||
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(),
|
||||
crate::backend::vm::VMValue::Future(fu) => Box::new(fu),
|
||||
crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
};
|
||||
let out = map.get(key_box);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
@ -360,25 +582,46 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
}
|
||||
}
|
||||
204 => { // set(key, value)
|
||||
204 => {
|
||||
// set(key, value)
|
||||
if argv.len() >= 2 {
|
||||
let key_box: Box<dyn NyashBox> = match argv[0].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
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::Integer(i) => {
|
||||
Box::new(crate::box_trait::IntegerBox::new(i))
|
||||
}
|
||||
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(),
|
||||
crate::backend::vm::VMValue::Future(fu) => Box::new(fu),
|
||||
crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
};
|
||||
let val_box: Box<dyn NyashBox> = match argv[1].clone() {
|
||||
crate::backend::vm::VMValue::Integer(i) => Box::new(crate::box_trait::IntegerBox::new(i)),
|
||||
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::Integer(i) => {
|
||||
Box::new(crate::box_trait::IntegerBox::new(i))
|
||||
}
|
||||
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(),
|
||||
crate::backend::vm::VMValue::Future(fu) => Box::new(fu),
|
||||
crate::backend::vm::VMValue::Void => Box::new(crate::box_trait::VoidBox::new()),
|
||||
crate::backend::vm::VMValue::Void => {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
};
|
||||
let out = map.set(key_box, val_box);
|
||||
let vmv = crate::backend::vm::VMValue::from_nyash_box(out);
|
||||
@ -391,7 +634,10 @@ pub extern "C" fn nyrt_host_call_slot(handle: u64, selector_id: u64,
|
||||
}
|
||||
}
|
||||
300 => {
|
||||
if let Some(sb) = recv_arc.as_any().downcast_ref::<crate::box_trait::StringBox>() {
|
||||
if let Some(sb) = recv_arc
|
||||
.as_any()
|
||||
.downcast_ref::<crate::box_trait::StringBox>()
|
||||
{
|
||||
let out = crate::backend::vm::VMValue::Integer(sb.value.len() as i64);
|
||||
let buf = tlv_encode_one(&out);
|
||||
return encode_out(out_ptr, out_len, &buf);
|
||||
|
||||
@ -8,7 +8,10 @@
|
||||
|
||||
use once_cell::sync::OnceCell;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock, atomic::{AtomicU64, Ordering}};
|
||||
use std::sync::{
|
||||
atomic::{AtomicU64, Ordering},
|
||||
Arc, RwLock,
|
||||
};
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
@ -18,26 +21,44 @@ struct Registry {
|
||||
}
|
||||
|
||||
impl Registry {
|
||||
fn new() -> Self { Self { next: AtomicU64::new(1), map: RwLock::new(HashMap::new()) } }
|
||||
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); }
|
||||
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); } }
|
||||
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) }
|
||||
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)) }
|
||||
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) }
|
||||
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) }
|
||||
|
||||
pub fn get(h: u64) -> Option<Arc<dyn NyashBox>> {
|
||||
reg().get(h)
|
||||
}
|
||||
|
||||
@ -2,33 +2,53 @@ use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
static ENABLED: Lazy<bool> = Lazy::new(|| std::env::var("NYASH_LEAK_LOG").unwrap_or_default() == "1");
|
||||
static LEAKS: Lazy<Mutex<HashMap<(String, u32), &'static str>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static ENABLED: Lazy<bool> =
|
||||
Lazy::new(|| std::env::var("NYASH_LEAK_LOG").unwrap_or_default() == "1");
|
||||
static LEAKS: Lazy<Mutex<HashMap<(String, u32), &'static str>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
pub fn init() { let _ = &*REPORTER; }
|
||||
pub fn init() {
|
||||
let _ = &*REPORTER;
|
||||
}
|
||||
|
||||
pub fn register_plugin(box_type: &str, instance_id: u32) {
|
||||
if !*ENABLED { return; }
|
||||
LEAKS.lock().unwrap().insert((box_type.to_string(), instance_id), "plugin");
|
||||
if !*ENABLED {
|
||||
return;
|
||||
}
|
||||
LEAKS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert((box_type.to_string(), instance_id), "plugin");
|
||||
}
|
||||
|
||||
pub fn finalize_plugin(box_type: &str, instance_id: u32) {
|
||||
if !*ENABLED { return; }
|
||||
LEAKS.lock().unwrap().remove(&(box_type.to_string(), instance_id));
|
||||
if !*ENABLED {
|
||||
return;
|
||||
}
|
||||
LEAKS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.remove(&(box_type.to_string(), instance_id));
|
||||
}
|
||||
|
||||
struct Reporter;
|
||||
impl Drop for Reporter {
|
||||
fn drop(&mut self) {
|
||||
if !*ENABLED { return; }
|
||||
if !*ENABLED {
|
||||
return;
|
||||
}
|
||||
let m = LEAKS.lock().unwrap();
|
||||
if m.is_empty() { return; }
|
||||
if m.is_empty() {
|
||||
return;
|
||||
}
|
||||
eprintln!("[leak] Detected {} non-finalized plugin boxes:", m.len());
|
||||
for ((ty, id), _) in m.iter() {
|
||||
eprintln!(" - {}(id={}) not finalized (missing fini or scope)", ty, id);
|
||||
eprintln!(
|
||||
" - {}(id={}) not finalized (missing fini or scope)",
|
||||
ty, id
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static REPORTER: Lazy<Reporter> = Lazy::new(|| Reporter);
|
||||
|
||||
|
||||
@ -1,41 +1,46 @@
|
||||
//! Nyashランタイムモジュール
|
||||
//!
|
||||
//!
|
||||
//! プラグインシステムとBox管理の中核
|
||||
|
||||
pub mod plugin_config;
|
||||
pub mod box_registry;
|
||||
pub mod plugin_loader_v2;
|
||||
pub mod plugin_loader_unified;
|
||||
pub mod plugin_ffi_common;
|
||||
pub mod leak_tracker;
|
||||
pub mod unified_registry;
|
||||
pub mod nyash_runtime;
|
||||
pub mod gc;
|
||||
pub mod scheduler;
|
||||
pub mod global_hooks;
|
||||
pub mod leak_tracker;
|
||||
pub mod nyash_runtime;
|
||||
pub mod plugin_config;
|
||||
pub mod plugin_ffi_common;
|
||||
pub mod plugin_loader_unified;
|
||||
pub mod plugin_loader_v2;
|
||||
pub mod scheduler;
|
||||
pub mod semantics;
|
||||
pub mod unified_registry;
|
||||
// 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に橋渡し)
|
||||
pub mod extern_registry; // ExternCall (env.*) 登録・診断用レジストリ
|
||||
pub mod modules_registry; // env.modules minimal registry
|
||||
pub mod host_api; // C ABI: plugins -> host 逆呼び出しAPI(TLSでVMに橋渡し)
|
||||
pub mod host_handles; // C ABI(TLV) 向け HostHandle レジストリ(ユーザー/内蔵Box受け渡し)
|
||||
pub mod modules_registry;
|
||||
pub mod type_box_abi; // Phase 12: Nyash ABI (vtable) 雛形
|
||||
pub mod type_meta;
|
||||
pub mod type_registry; // Phase 12: TypeId→TypeBox 解決(雛形) // env.modules minimal registry
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
pub use box_registry::{get_global_registry, BoxFactoryRegistry, BoxProvider};
|
||||
pub use plugin_config::PluginConfig;
|
||||
pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry};
|
||||
pub use plugin_loader_v2::{PluginLoaderV2, get_global_loader_v2, init_global_loader_v2};
|
||||
pub use plugin_loader_unified::{PluginHost, get_global_plugin_host, init_global_plugin_host, PluginLibraryHandle, PluginBoxType, MethodHandle};
|
||||
pub use plugin_loader_unified::{
|
||||
get_global_plugin_host, init_global_plugin_host, MethodHandle, PluginBoxType, PluginHost,
|
||||
PluginLibraryHandle,
|
||||
};
|
||||
pub use plugin_loader_v2::{get_global_loader_v2, init_global_loader_v2, PluginLoaderV2};
|
||||
pub mod cache_versions;
|
||||
pub use unified_registry::{get_global_unified_registry, init_global_unified_registry, register_user_defined_factory};
|
||||
pub use gc::{BarrierKind, GcHooks};
|
||||
pub use nyash_runtime::{NyashRuntime, NyashRuntimeBuilder};
|
||||
pub use gc::{GcHooks, BarrierKind};
|
||||
pub use scheduler::{Scheduler, SingleThreadScheduler};
|
||||
pub use unified_registry::{
|
||||
get_global_unified_registry, init_global_unified_registry, register_user_defined_factory,
|
||||
};
|
||||
// pub use plugin_box::PluginBox; // legacy
|
||||
// Use unified plugin loader (formerly v2)
|
||||
// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
//! Minimal global registry for env.modules (Phase 15 P0b)
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use once_cell::sync::Lazy;
|
||||
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
static REGISTRY: Lazy<Mutex<HashMap<String, Box<dyn NyashBox>>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static REGISTRY: Lazy<Mutex<HashMap<String, Box<dyn NyashBox>>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
pub fn set(name: String, value: Box<dyn NyashBox>) {
|
||||
if let Ok(mut map) = REGISTRY.lock() {
|
||||
@ -23,4 +24,3 @@ pub fn get(name: &str) -> Option<Box<dyn NyashBox>> {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
|
||||
@ -6,11 +6,11 @@
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use crate::core::model::BoxDeclaration;
|
||||
use crate::box_factory::{UnifiedBoxRegistry, BoxFactory};
|
||||
use crate::box_factory::builtin::BuiltinBoxFactory;
|
||||
#[cfg(feature = "plugins")]
|
||||
use crate::box_factory::plugin::PluginBoxFactory;
|
||||
use crate::box_factory::{BoxFactory, UnifiedBoxRegistry};
|
||||
use crate::core::model::BoxDeclaration;
|
||||
|
||||
/// Core runtime container for executing Nyash programs
|
||||
pub struct NyashRuntime {
|
||||
@ -21,7 +21,7 @@ pub struct NyashRuntime {
|
||||
/// GC hooks (switchable runtime). Default is no-op.
|
||||
pub gc: Arc<dyn crate::runtime::gc::GcHooks>,
|
||||
/// Optional scheduler (single-thread by default is fine)
|
||||
pub scheduler: Option<Arc<dyn crate::runtime::scheduler::Scheduler>>,
|
||||
pub scheduler: Option<Arc<dyn crate::runtime::scheduler::Scheduler>>,
|
||||
}
|
||||
|
||||
impl NyashRuntime {
|
||||
@ -31,7 +31,9 @@ impl NyashRuntime {
|
||||
box_registry: create_default_registry(),
|
||||
box_declarations: Arc::new(RwLock::new(HashMap::new())),
|
||||
gc: Arc::new(crate::runtime::gc::NullGc),
|
||||
scheduler: Some(Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new())),
|
||||
scheduler: Some(Arc::new(
|
||||
crate::runtime::scheduler::SingleThreadScheduler::new(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -40,18 +42,26 @@ impl NyashRuntime {
|
||||
pub struct NyashRuntimeBuilder {
|
||||
box_registry: Option<Arc<Mutex<UnifiedBoxRegistry>>>,
|
||||
box_declarations: Option<Arc<RwLock<HashMap<String, BoxDeclaration>>>>,
|
||||
gc: Option<Arc<dyn crate::runtime::gc::GcHooks>>,
|
||||
gc: Option<Arc<dyn crate::runtime::gc::GcHooks>>,
|
||||
scheduler: Option<Arc<dyn crate::runtime::scheduler::Scheduler>>,
|
||||
}
|
||||
|
||||
impl NyashRuntimeBuilder {
|
||||
pub fn new() -> Self {
|
||||
Self { box_registry: None, box_declarations: None, gc: None, scheduler: None }
|
||||
Self {
|
||||
box_registry: None,
|
||||
box_declarations: None,
|
||||
gc: None,
|
||||
scheduler: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Inject a BoxFactory implementation directly into a private registry
|
||||
pub fn with_factory(mut self, factory: Arc<dyn BoxFactory>) -> Self {
|
||||
let registry = self.box_registry.take().unwrap_or_else(|| create_default_registry());
|
||||
let registry = self
|
||||
.box_registry
|
||||
.take()
|
||||
.unwrap_or_else(|| create_default_registry());
|
||||
if let Ok(mut reg) = registry.lock() {
|
||||
reg.register(factory);
|
||||
}
|
||||
@ -68,13 +78,21 @@ impl NyashRuntimeBuilder {
|
||||
}
|
||||
|
||||
pub fn build(self) -> NyashRuntime {
|
||||
let registry = self.box_registry.unwrap_or_else(|| create_default_registry());
|
||||
let registry = self
|
||||
.box_registry
|
||||
.unwrap_or_else(|| create_default_registry());
|
||||
|
||||
NyashRuntime {
|
||||
box_registry: registry,
|
||||
box_declarations: self.box_declarations.unwrap_or_else(|| Arc::new(RwLock::new(HashMap::new()))),
|
||||
gc: self.gc.unwrap_or_else(|| Arc::new(crate::runtime::gc::NullGc)),
|
||||
scheduler: Some(self.scheduler.unwrap_or_else(|| Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new()))),
|
||||
box_declarations: self
|
||||
.box_declarations
|
||||
.unwrap_or_else(|| Arc::new(RwLock::new(HashMap::new()))),
|
||||
gc: self
|
||||
.gc
|
||||
.unwrap_or_else(|| Arc::new(crate::runtime::gc::NullGc)),
|
||||
scheduler: Some(self.scheduler.unwrap_or_else(|| {
|
||||
Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new())
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -115,7 +133,9 @@ impl NyashRuntimeBuilder {
|
||||
|
||||
/// Convenience: use SingleThreadScheduler
|
||||
pub fn with_single_thread_scheduler(mut self) -> Self {
|
||||
self.scheduler = Some(Arc::new(crate::runtime::scheduler::SingleThreadScheduler::new()));
|
||||
self.scheduler = Some(Arc::new(
|
||||
crate::runtime::scheduler::SingleThreadScheduler::new(),
|
||||
));
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
//! プラグイン設定(nyash.toml)の読み込み
|
||||
//!
|
||||
//!
|
||||
//! シンプルな実装から始める - 必要最小限の機能のみ
|
||||
|
||||
use std::collections::HashMap;
|
||||
@ -17,14 +17,14 @@ pub struct PluginConfig {
|
||||
impl PluginConfig {
|
||||
/// nyash.tomlを読み込む
|
||||
pub fn load_from_file(path: impl AsRef<Path>) -> Result<Self, String> {
|
||||
let content = fs::read_to_string(path)
|
||||
.map_err(|e| format!("Failed to read nyash.toml: {}", e))?;
|
||||
|
||||
let content =
|
||||
fs::read_to_string(path).map_err(|e| format!("Failed to read nyash.toml: {}", e))?;
|
||||
|
||||
Self::parse(&content)
|
||||
}
|
||||
|
||||
|
||||
/// 設定文字列をパース(シンプル版)
|
||||
///
|
||||
///
|
||||
/// 対応フォーマット:
|
||||
/// ```toml
|
||||
/// [plugins]
|
||||
@ -34,15 +34,15 @@ impl PluginConfig {
|
||||
pub fn parse(content: &str) -> Result<Self, String> {
|
||||
let mut config = PluginConfig::default();
|
||||
let mut in_plugins_section = false;
|
||||
|
||||
|
||||
for line in content.lines() {
|
||||
let line = line.trim();
|
||||
|
||||
|
||||
// 空行やコメントはスキップ
|
||||
if line.is_empty() || line.starts_with('#') {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// セクション検出
|
||||
if line == "[plugins]" {
|
||||
in_plugins_section = true;
|
||||
@ -51,7 +51,7 @@ impl PluginConfig {
|
||||
in_plugins_section = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// plugins セクション内の設定を読む
|
||||
if in_plugins_section {
|
||||
if let Some((key, value)) = line.split_once('=') {
|
||||
@ -61,7 +61,7 @@ impl PluginConfig {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
@ -69,7 +69,7 @@ impl PluginConfig {
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_simple_config() {
|
||||
let toml = r#"
|
||||
@ -80,20 +80,23 @@ StringBox = "mystring"
|
||||
[other]
|
||||
something = "else"
|
||||
"#;
|
||||
|
||||
|
||||
let config = PluginConfig::parse(toml).unwrap();
|
||||
assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string()));
|
||||
assert_eq!(config.plugins.get("StringBox"), Some(&"mystring".to_string()));
|
||||
assert_eq!(
|
||||
config.plugins.get("StringBox"),
|
||||
Some(&"mystring".to_string())
|
||||
);
|
||||
assert_eq!(config.plugins.len(), 2);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_empty_config() {
|
||||
let toml = "";
|
||||
let config = PluginConfig::parse(toml).unwrap();
|
||||
assert!(config.plugins.is_empty());
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_parse_with_comments() {
|
||||
let toml = r#"
|
||||
@ -103,10 +106,10 @@ something = "else"
|
||||
FileBox = "filebox"
|
||||
# StringBox = "disabled" # This is commented out
|
||||
"#;
|
||||
|
||||
|
||||
let config = PluginConfig::parse(toml).unwrap();
|
||||
assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string()));
|
||||
assert_eq!(config.plugins.get("StringBox"), None);
|
||||
assert_eq!(config.plugins.len(), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -4,7 +4,9 @@
|
||||
use crate::box_trait::NyashBox;
|
||||
|
||||
/// Encode empty TLV arguments: version=1, argc=0
|
||||
pub fn encode_empty_args() -> Vec<u8> { vec![1u8, 0, 0, 0] }
|
||||
pub fn encode_empty_args() -> Vec<u8> {
|
||||
vec![1u8, 0, 0, 0]
|
||||
}
|
||||
|
||||
/// Encode TLV header with argc (no payload entries encoded here)
|
||||
pub fn encode_tlv_header(argc: u16) -> Vec<u8> {
|
||||
@ -34,39 +36,56 @@ pub fn encode_args(args: &[Box<dyn NyashBox>]) -> Vec<u8> {
|
||||
pub mod decode {
|
||||
/// Try to parse a u32 instance id from an output buffer (little-endian).
|
||||
pub fn instance_id(buf: &[u8]) -> Option<u32> {
|
||||
if buf.len() < 4 { return None; }
|
||||
if buf.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
Some(u32::from_le_bytes([buf[0], buf[1], buf[2], buf[3]]))
|
||||
}
|
||||
|
||||
/// Parse TLV header from buffer; returns (tag, size, payload_slice)
|
||||
pub fn tlv_first(buf: &[u8]) -> Option<(u8, usize, &[u8])> {
|
||||
if buf.len() < 8 { return None; }
|
||||
if buf.len() < 8 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[4];
|
||||
let size = u16::from_le_bytes([buf[6], buf[7]]) as usize;
|
||||
if buf.len() < 8 + size { return None; }
|
||||
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);
|
||||
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; }
|
||||
if payload.len() != 1 {
|
||||
return None;
|
||||
}
|
||||
Some(payload[0] != 0)
|
||||
}
|
||||
/// Decode i32 payload (size must be 4)
|
||||
pub fn i32(payload: &[u8]) -> Option<i32> {
|
||||
if payload.len() != 4 { return None; }
|
||||
let mut b = [0u8;4]; b.copy_from_slice(payload);
|
||||
if payload.len() != 4 {
|
||||
return None;
|
||||
}
|
||||
let mut b = [0u8; 4];
|
||||
b.copy_from_slice(payload);
|
||||
Some(i32::from_le_bytes(b))
|
||||
}
|
||||
/// Decode f64 payload (size must be 8)
|
||||
pub fn f64(payload: &[u8]) -> Option<f64> {
|
||||
if payload.len() != 8 { return None; }
|
||||
let mut b = [0u8;8]; b.copy_from_slice(payload);
|
||||
if payload.len() != 8 {
|
||||
return None;
|
||||
}
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
Some(f64::from_le_bytes(b))
|
||||
}
|
||||
/// Decode UTF-8 string/bytes
|
||||
@ -76,9 +95,11 @@ pub mod decode {
|
||||
|
||||
/// 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];
|
||||
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)))
|
||||
@ -86,16 +107,22 @@ pub mod decode {
|
||||
|
||||
/// 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; }
|
||||
if buf.len() < 4 {
|
||||
return None;
|
||||
}
|
||||
let mut off = 4usize;
|
||||
for i in 0..=n {
|
||||
if buf.len() < off + 4 { return None; }
|
||||
if buf.len() < off + 4 {
|
||||
return None;
|
||||
}
|
||||
let tag = buf[off];
|
||||
let _rsv = buf[off+1];
|
||||
let size = u16::from_le_bytes([buf[off+2], buf[off+3]]) as usize;
|
||||
if buf.len() < off + 4 + size { return None; }
|
||||
let _rsv = buf[off + 1];
|
||||
let size = u16::from_le_bytes([buf[off + 2], buf[off + 3]]) as usize;
|
||||
if buf.len() < off + 4 + size {
|
||||
return None;
|
||||
}
|
||||
if i == n {
|
||||
return Some((tag, size, &buf[off+4..off+4+size]));
|
||||
return Some((tag, size, &buf[off + 4..off + 4 + size]));
|
||||
}
|
||||
off += 4 + size;
|
||||
}
|
||||
@ -197,7 +224,7 @@ pub mod encode {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{encode, decode};
|
||||
use super::{decode, encode};
|
||||
|
||||
#[test]
|
||||
fn test_encode_decode_bool() {
|
||||
@ -234,7 +261,7 @@ mod tests {
|
||||
fn test_encode_decode_string_and_bytes() {
|
||||
let mut buf = super::encode_tlv_header(2);
|
||||
encode::string(&mut buf, "hello");
|
||||
encode::bytes(&mut buf, &[1,2,3,4]);
|
||||
encode::bytes(&mut buf, &[1, 2, 3, 4]);
|
||||
// First entry string
|
||||
let (tag, _sz, payload) = decode::tlv_nth(&buf, 0).unwrap();
|
||||
assert_eq!(tag, 6);
|
||||
@ -243,6 +270,6 @@ mod tests {
|
||||
let (tag2, sz2, payload2) = decode::tlv_nth(&buf, 1).unwrap();
|
||||
assert_eq!(tag2, 7);
|
||||
assert_eq!(sz2, 4);
|
||||
assert_eq!(payload2, &[1,2,3,4]);
|
||||
assert_eq!(payload2, &[1, 2, 3, 4]);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,9 +3,9 @@
|
||||
//! Thin wrapper over v2 loader to provide a stable facade
|
||||
//! with minimal, friendly API for runtime/runner and future transports.
|
||||
|
||||
use std::sync::{Arc, RwLock};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::cell::Cell;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::config::nyash_toml_v2::NyashConfigV2;
|
||||
@ -44,7 +44,11 @@ pub struct PluginHost {
|
||||
|
||||
impl PluginHost {
|
||||
pub fn new(loader: Arc<RwLock<PluginLoaderV2>>) -> Self {
|
||||
Self { loader, config: None, config_path: None }
|
||||
Self {
|
||||
loader,
|
||||
config: None,
|
||||
config_path: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Load config and dynamic libraries, keeping a local config cache.
|
||||
@ -58,7 +62,8 @@ impl PluginHost {
|
||||
let canonical = std::fs::canonicalize(config_path)
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|_| config_path.to_string());
|
||||
self.config = Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?);
|
||||
self.config =
|
||||
Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?);
|
||||
self.config_path = Some(canonical);
|
||||
|
||||
// Delegate actual library loads + pre-birth singletons to v2
|
||||
@ -67,20 +72,32 @@ impl PluginHost {
|
||||
}
|
||||
|
||||
/// Register built-ins or user-defined boxes if needed (no-op for now).
|
||||
pub fn register_boxes(&self) -> BidResult<()> { Ok(()) }
|
||||
pub fn register_boxes(&self) -> BidResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Expose read-only view of loaded config for callers migrating from v2 paths.
|
||||
pub fn config_ref(&self) -> Option<&NyashConfigV2> { self.config.as_ref() }
|
||||
pub fn config_ref(&self) -> Option<&NyashConfigV2> {
|
||||
self.config.as_ref()
|
||||
}
|
||||
|
||||
/// Resolve a method handle for a given plugin box type and method name.
|
||||
pub fn resolve_method(&self, box_type: &str, method_name: &str) -> BidResult<MethodHandle> {
|
||||
let cfg = self.config.as_ref().ok_or(BidError::PluginError)?;
|
||||
let (lib_name, _lib_def) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?;
|
||||
let (lib_name, _lib_def) = cfg
|
||||
.find_library_for_box(box_type)
|
||||
.ok_or(BidError::InvalidType)?;
|
||||
let cfg_path = self.config_path.as_deref().unwrap_or("nyash.toml");
|
||||
let toml_content = std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?;
|
||||
let toml_value: toml::Value = toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?;
|
||||
let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?;
|
||||
let m = box_conf.methods.get(method_name).ok_or(BidError::InvalidMethod)?;
|
||||
let toml_value: toml::Value =
|
||||
toml::from_str(&toml_content).map_err(|_| BidError::PluginError)?;
|
||||
let box_conf = cfg
|
||||
.get_box_config(lib_name, box_type, &toml_value)
|
||||
.ok_or(BidError::InvalidType)?;
|
||||
let m = box_conf
|
||||
.methods
|
||||
.get(method_name)
|
||||
.ok_or(BidError::InvalidMethod)?;
|
||||
Ok(MethodHandle {
|
||||
lib: lib_name.to_string(),
|
||||
box_type: box_type.to_string(),
|
||||
@ -91,7 +108,11 @@ impl PluginHost {
|
||||
}
|
||||
|
||||
// --- v2 adapter layer: allow gradual migration of callers ---
|
||||
pub fn create_box(&self, box_type: &str, args: &[Box<dyn crate::box_trait::NyashBox>]) -> BidResult<Box<dyn crate::box_trait::NyashBox>> {
|
||||
pub fn create_box(
|
||||
&self,
|
||||
box_type: &str,
|
||||
args: &[Box<dyn crate::box_trait::NyashBox>],
|
||||
) -> BidResult<Box<dyn crate::box_trait::NyashBox>> {
|
||||
let l = self.loader.read().unwrap();
|
||||
l.create_box(box_type, args)
|
||||
}
|
||||
@ -137,7 +158,10 @@ impl PluginHost {
|
||||
if iface_name == "env.future" && method_name == "await" {
|
||||
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>() {
|
||||
if let Some(fut) = arg0
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::FutureBox>()
|
||||
{
|
||||
let max_ms: u64 = crate::config::env::await_max_ms();
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
@ -145,18 +169,24 @@ impl PluginHost {
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
spins += 1;
|
||||
if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); }
|
||||
if spins % 1024 == 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
}
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
let err = crate::box_trait::StringBox::new("Timeout");
|
||||
return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err)))));
|
||||
}
|
||||
}
|
||||
return Ok(fut.wait_and_get().ok().map(|v| Box::new(NyashResultBox::new_ok(v)) as Box<dyn crate::box_trait::NyashBox>));
|
||||
return Ok(fut.wait_and_get().ok().map(|v| {
|
||||
Box::new(NyashResultBox::new_ok(v)) as Box<dyn crate::box_trait::NyashBox>
|
||||
}));
|
||||
} else {
|
||||
return Ok(Some(Box::new(NyashResultBox::new_ok(arg0.clone_box()))));
|
||||
}
|
||||
}
|
||||
return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(crate::box_trait::StringBox::new("InvalidArgs"))))));
|
||||
return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(
|
||||
crate::box_trait::StringBox::new("InvalidArgs"),
|
||||
)))));
|
||||
}
|
||||
let l = self.loader.read().unwrap();
|
||||
l.extern_call(iface_name, method_name, args)
|
||||
@ -169,7 +199,9 @@ static GLOBAL_HOST: Lazy<Arc<RwLock<PluginHost>>> = Lazy::new(|| {
|
||||
Arc::new(RwLock::new(PluginHost::new(loader)))
|
||||
});
|
||||
|
||||
pub fn get_global_plugin_host() -> Arc<RwLock<PluginHost>> { GLOBAL_HOST.clone() }
|
||||
pub fn get_global_plugin_host() -> Arc<RwLock<PluginHost>> {
|
||||
GLOBAL_HOST.clone()
|
||||
}
|
||||
|
||||
pub fn init_global_plugin_host(config_path: &str) -> BidResult<()> {
|
||||
let host = get_global_plugin_host();
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
use crate::bid::{BidResult, BidError};
|
||||
use crate::bid::{BidError, BidResult};
|
||||
|
||||
// Minimal helpers to keep loader.rs lean and consistent
|
||||
#[inline]
|
||||
@ -12,5 +12,6 @@ pub fn from_toml<T>(_r: Result<T, toml::de::Error>) -> BidResult<T> {
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn or_plugin_err<T>(opt: Option<T>) -> BidResult<T> { opt.ok_or(BidError::PluginError) }
|
||||
|
||||
pub fn or_plugin_err<T>(opt: Option<T>) -> BidResult<T> {
|
||||
opt.ok_or(BidError::PluginError)
|
||||
}
|
||||
|
||||
@ -1,12 +1,14 @@
|
||||
use super::loader::PluginLoaderV2;
|
||||
use crate::bid::{BidResult};
|
||||
use crate::bid::BidResult;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
static GLOBAL_LOADER_V2: Lazy<Arc<RwLock<PluginLoaderV2>>> =
|
||||
Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new())));
|
||||
|
||||
pub fn get_global_loader_v2() -> Arc<RwLock<PluginLoaderV2>> { GLOBAL_LOADER_V2.clone() }
|
||||
pub fn get_global_loader_v2() -> Arc<RwLock<PluginLoaderV2>> {
|
||||
GLOBAL_LOADER_V2.clone()
|
||||
}
|
||||
|
||||
pub fn init_global_loader_v2(config_path: &str) -> BidResult<()> {
|
||||
let loader = get_global_loader_v2();
|
||||
@ -24,4 +26,3 @@ pub fn shutdown_plugins_v2() -> BidResult<()> {
|
||||
loader.shutdown_singletons();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
// Host bridge helpers for TypeBox invoke (minimal, v2)
|
||||
|
||||
pub type InvokeFn = unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32;
|
||||
pub type InvokeFn =
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32;
|
||||
|
||||
// Call invoke_id with a temporary output buffer; returns (code, bytes_written, buffer)
|
||||
pub fn invoke_alloc(
|
||||
@ -25,4 +26,3 @@ pub fn invoke_alloc(
|
||||
};
|
||||
(code, out_len, out)
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,16 @@
|
||||
use super::types::{PluginBoxMetadata, PluginBoxV2, PluginHandleInner, LoadedPluginV2};
|
||||
use crate::bid::{BidResult, BidError};
|
||||
use super::types::{LoadedPluginV2, PluginBoxMetadata, PluginBoxV2, PluginHandleInner};
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::box_trait::NyashBox;
|
||||
use crate::config::nyash_toml_v2::{NyashConfigV2, LibraryDefinition};
|
||||
use crate::config::nyash_toml_v2::{LibraryDefinition, NyashConfigV2};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::{Arc, RwLock};
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
use libloading::{Library, Symbol};
|
||||
|
||||
fn dbg_on() -> bool { std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" }
|
||||
fn dbg_on() -> bool {
|
||||
std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1"
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
struct LoadedBoxSpec {
|
||||
@ -17,14 +19,17 @@ struct LoadedBoxSpec {
|
||||
fini_method_id: Option<u32>,
|
||||
}
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct MethodSpec { method_id: u32, returns_result: bool }
|
||||
struct MethodSpec {
|
||||
method_id: u32,
|
||||
returns_result: bool,
|
||||
}
|
||||
|
||||
pub struct PluginLoaderV2 {
|
||||
pub(super) plugins: RwLock<HashMap<String, Arc<LoadedPluginV2>>>,
|
||||
pub config: Option<NyashConfigV2>,
|
||||
pub(super) config_path: Option<String>,
|
||||
pub(super) singletons: RwLock<HashMap<(String,String), Arc<PluginHandleInner>>>,
|
||||
pub(super) box_specs: RwLock<HashMap<(String,String), LoadedBoxSpec>>,
|
||||
pub(super) singletons: RwLock<HashMap<(String, String), Arc<PluginHandleInner>>>,
|
||||
pub(super) box_specs: RwLock<HashMap<(String, String), LoadedBoxSpec>>,
|
||||
}
|
||||
|
||||
impl PluginLoaderV2 {
|
||||
@ -39,12 +44,19 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
|
||||
pub fn load_config(&mut self, config_path: &str) -> BidResult<()> {
|
||||
let canonical = std::fs::canonicalize(config_path).map(|p| p.to_string_lossy().to_string()).unwrap_or_else(|_| config_path.to_string());
|
||||
let canonical = std::fs::canonicalize(config_path)
|
||||
.map(|p| p.to_string_lossy().to_string())
|
||||
.unwrap_or_else(|_| config_path.to_string());
|
||||
self.config_path = Some(canonical.clone());
|
||||
self.config = Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?);
|
||||
self.config =
|
||||
Some(NyashConfigV2::from_file(&canonical).map_err(|_| BidError::PluginError)?);
|
||||
if let Some(cfg) = self.config.as_ref() {
|
||||
let mut labels: Vec<String> = Vec::new();
|
||||
for (_lib, def) in &cfg.libraries { for bt in &def.boxes { labels.push(format!("BoxRef:{}", bt)); } }
|
||||
for (_lib, def) in &cfg.libraries {
|
||||
for bt in &def.boxes {
|
||||
labels.push(format!("BoxRef:{}", bt));
|
||||
}
|
||||
}
|
||||
crate::runtime::cache_versions::bump_many(&labels);
|
||||
}
|
||||
Ok(())
|
||||
@ -52,8 +64,12 @@ impl PluginLoaderV2 {
|
||||
|
||||
pub fn load_all_plugins(&self) -> BidResult<()> {
|
||||
let config = self.config.as_ref().ok_or(BidError::PluginError)?;
|
||||
for (lib_name, lib_def) in &config.libraries { let _ = self.load_plugin(lib_name, lib_def); }
|
||||
for (plugin_name, root) in &config.plugins { let _ = self.load_plugin_from_root(plugin_name, root); }
|
||||
for (lib_name, lib_def) in &config.libraries {
|
||||
let _ = self.load_plugin(lib_name, lib_def);
|
||||
}
|
||||
for (plugin_name, root) in &config.plugins {
|
||||
let _ = self.load_plugin_from_root(plugin_name, root);
|
||||
}
|
||||
self.prebirth_singletons()?;
|
||||
Ok(())
|
||||
}
|
||||
@ -88,24 +104,38 @@ impl PluginLoaderV2 {
|
||||
if let Some(fname) = c.file_name().and_then(|s| s.to_str()) {
|
||||
if let Some(resolved) = cfg.resolve_plugin_path(fname) {
|
||||
let pb = PathBuf::from(resolved);
|
||||
if pb.exists() { lib_path = Some(pb); break; }
|
||||
if pb.exists() {
|
||||
lib_path = Some(pb);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let lib_path = lib_path.unwrap_or_else(|| base.to_path_buf());
|
||||
if dbg_on() { eprintln!("[PluginLoaderV2] load_plugin: lib='{}' path='{}'", lib_name, lib_path.display()); }
|
||||
if dbg_on() {
|
||||
eprintln!(
|
||||
"[PluginLoaderV2] load_plugin: lib='{}' path='{}'",
|
||||
lib_name,
|
||||
lib_path.display()
|
||||
);
|
||||
}
|
||||
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)?;
|
||||
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") {
|
||||
if let Ok(init_sym) =
|
||||
lib_arc.get::<Symbol<unsafe extern "C" fn() -> i32>>(b"nyash_plugin_init\0")
|
||||
{
|
||||
let _ = init_sym();
|
||||
}
|
||||
|
||||
@ -116,13 +146,18 @@ impl PluginLoaderV2 {
|
||||
init_fn: None,
|
||||
invoke_fn: *invoke_sym,
|
||||
};
|
||||
self.plugins.write().map_err(|_| BidError::PluginError)?.insert(lib_name.to_string(), Arc::new(loaded));
|
||||
self.plugins
|
||||
.write()
|
||||
.map_err(|_| BidError::PluginError)?
|
||||
.insert(lib_name.to_string(), Arc::new(loaded));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_plugin_from_root(&self, _plugin_name: &str, _root: &str) -> BidResult<()> { Ok(()) }
|
||||
fn load_plugin_from_root(&self, _plugin_name: &str, _root: &str) -> BidResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn prebirth_singletons(&self) -> BidResult<()> {
|
||||
let config = self.config.as_ref().ok_or(BidError::PluginError)?;
|
||||
@ -131,16 +166,29 @@ impl PluginLoaderV2 {
|
||||
let toml_value: toml::Value = super::errors::from_toml(toml::from_str(&toml_content))?;
|
||||
for (lib_name, lib_def) in &config.libraries {
|
||||
for box_name in &lib_def.boxes {
|
||||
if let Some(bc) = config.get_box_config(lib_name, box_name, &toml_value) { if bc.singleton { let _ = self.ensure_singleton_handle(lib_name, box_name); } }
|
||||
if let Some(bc) = config.get_box_config(lib_name, box_name, &toml_value) {
|
||||
if bc.singleton {
|
||||
let _ = self.ensure_singleton_handle(lib_name, box_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn find_box_by_type_id<'a>(&'a self, config: &'a NyashConfigV2, toml_value: &'a toml::Value, type_id: u32) -> Option<(&'a str, &'a str)> {
|
||||
fn find_box_by_type_id<'a>(
|
||||
&'a self,
|
||||
config: &'a NyashConfigV2,
|
||||
toml_value: &'a toml::Value,
|
||||
type_id: u32,
|
||||
) -> Option<(&'a str, &'a str)> {
|
||||
for (lib_name, lib_def) in &config.libraries {
|
||||
for box_name in &lib_def.boxes {
|
||||
if let Some(box_conf) = config.get_box_config(lib_name, box_name, toml_value) { if box_conf.type_id == type_id { return Some((lib_name.as_str(), box_name.as_str())); } }
|
||||
if let Some(box_conf) = config.get_box_config(lib_name, box_name, toml_value) {
|
||||
if box_conf.type_id == type_id {
|
||||
return Some((lib_name.as_str(), box_name.as_str()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
@ -160,12 +208,18 @@ impl PluginLoaderV2 {
|
||||
let mut resolved_type = type_id;
|
||||
let mut fini_method = None;
|
||||
if let Some(spec) = self.box_specs.read().ok()?.get(&spec_key).cloned() {
|
||||
if let Some(tid) = spec.type_id { resolved_type = tid; }
|
||||
if let Some(fini) = spec.fini_method_id { fini_method = Some(fini); }
|
||||
if let Some(tid) = spec.type_id {
|
||||
resolved_type = tid;
|
||||
}
|
||||
if let Some(fini) = spec.fini_method_id {
|
||||
fini_method = Some(fini);
|
||||
}
|
||||
}
|
||||
if resolved_type == type_id || fini_method.is_none() {
|
||||
if let Some(cfg) = config.get_box_config(lib_name, box_type, &toml_value) {
|
||||
if resolved_type == type_id { resolved_type = cfg.type_id; }
|
||||
if resolved_type == type_id {
|
||||
resolved_type = cfg.type_id;
|
||||
}
|
||||
if fini_method.is_none() {
|
||||
fini_method = cfg.methods.get("fini").map(|m| m.method_id);
|
||||
}
|
||||
@ -180,7 +234,11 @@ impl PluginLoaderV2 {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn construct_existing_instance(&self, type_id: u32, instance_id: u32) -> Option<Box<dyn NyashBox>> {
|
||||
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_str = std::fs::read_to_string(cfg_path).ok()?;
|
||||
@ -188,42 +246,122 @@ impl PluginLoaderV2 {
|
||||
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();
|
||||
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 = super::types::construct_plugin_box(box_type.to_string(), type_id, plugin.invoke_fn, instance_id, fini_method_id);
|
||||
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 = super::types::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()); } }
|
||||
for ((lib, b), _) in self.box_specs.read().unwrap().iter() { if b == box_type { return Some(lib.clone()); } }
|
||||
if let Some(cfg) = &self.config {
|
||||
if let Some((name, _)) = cfg.find_library_for_box(box_type) {
|
||||
return Some(name.to_string());
|
||||
}
|
||||
}
|
||||
for ((lib, b), _) in self.box_specs.read().unwrap().iter() {
|
||||
if b == box_type {
|
||||
return Some(lib.clone());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn ensure_singleton_handle(&self, lib_name: &str, box_type: &str) -> BidResult<()> {
|
||||
if self.singletons.read().unwrap().contains_key(&(lib_name.to_string(), box_type.to_string())) { return Ok(()); }
|
||||
if self
|
||||
.singletons
|
||||
.read()
|
||||
.unwrap()
|
||||
.contains_key(&(lib_name.to_string(), box_type.to_string()))
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
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 toml_value: toml::Value =
|
||||
toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?)
|
||||
.map_err(|_| BidError::PluginError)?;
|
||||
let config = self.config.as_ref().ok_or(BidError::PluginError)?;
|
||||
let plugins = self.plugins.read().unwrap();
|
||||
let plugin = plugins.get(lib_name).ok_or(BidError::PluginError)?;
|
||||
let type_id = if let Some(spec) = self.box_specs.read().unwrap().get(&(lib_name.to_string(), box_type.to_string())) { spec.type_id.unwrap_or_else(|| config.box_types.get(box_type).copied().unwrap_or(0)) } else { let box_conf = config.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?; box_conf.type_id };
|
||||
let type_id = if let Some(spec) = self
|
||||
.box_specs
|
||||
.read()
|
||||
.unwrap()
|
||||
.get(&(lib_name.to_string(), box_type.to_string()))
|
||||
{
|
||||
spec.type_id
|
||||
.unwrap_or_else(|| config.box_types.get(box_type).copied().unwrap_or(0))
|
||||
} else {
|
||||
let box_conf = config
|
||||
.get_box_config(lib_name, box_type, &toml_value)
|
||||
.ok_or(BidError::InvalidType)?;
|
||||
box_conf.type_id
|
||||
};
|
||||
let out = vec![0u8; 1024];
|
||||
let out_len = out.len();
|
||||
let tlv_args = crate::runtime::plugin_ffi_common::encode_empty_args();
|
||||
let (birth_result, _len, out_vec) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, 0, 0, &tlv_args);
|
||||
let (birth_result, _len, out_vec) =
|
||||
super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, 0, 0, &tlv_args);
|
||||
let out = out_vec;
|
||||
if birth_result != 0 || out_len < 4 { return Err(BidError::PluginError); }
|
||||
if birth_result != 0 || out_len < 4 {
|
||||
return Err(BidError::PluginError);
|
||||
}
|
||||
let instance_id = u32::from_le_bytes([out[0], out[1], out[2], out[3]]);
|
||||
let fini_id = if let Some(spec) = self.box_specs.read().unwrap().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).ok_or(BidError::InvalidType)?; box_conf.methods.get("fini").map(|m| m.method_id) };
|
||||
let handle = Arc::new(PluginHandleInner { type_id, invoke_fn: plugin.invoke_fn, instance_id, fini_method_id: fini_id, finalized: std::sync::atomic::AtomicBool::new(false) });
|
||||
self.singletons.write().unwrap().insert((lib_name.to_string(), box_type.to_string()), handle);
|
||||
let fini_id = if let Some(spec) = self
|
||||
.box_specs
|
||||
.read()
|
||||
.unwrap()
|
||||
.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)
|
||||
.ok_or(BidError::InvalidType)?;
|
||||
box_conf.methods.get("fini").map(|m| m.method_id)
|
||||
};
|
||||
let handle = Arc::new(PluginHandleInner {
|
||||
type_id,
|
||||
invoke_fn: plugin.invoke_fn,
|
||||
instance_id,
|
||||
fini_method_id: fini_id,
|
||||
finalized: std::sync::atomic::AtomicBool::new(false),
|
||||
});
|
||||
self.singletons
|
||||
.write()
|
||||
.unwrap()
|
||||
.insert((lib_name.to_string(), box_type.to_string()), handle);
|
||||
crate::runtime::cache_versions::bump_version(&format!("BoxRef:{}", box_type));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn extern_call(&self, iface_name: &str, method_name: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
pub fn extern_call(
|
||||
&self,
|
||||
iface_name: &str,
|
||||
method_name: &str,
|
||||
args: &[Box<dyn NyashBox>],
|
||||
) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
match (iface_name, method_name) {
|
||||
("env.console", "log") => { for a in args { println!("{}", a.to_string_box().value); } Ok(None) }
|
||||
("env.console", "log") => {
|
||||
for a in args {
|
||||
println!("{}", a.to_string_box().value);
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
("env.modules", "set") => {
|
||||
if args.len() >= 2 {
|
||||
let key = args[0].to_string_box().value;
|
||||
@ -235,27 +373,110 @@ impl PluginLoaderV2 {
|
||||
("env.modules", "get") => {
|
||||
if let Some(k) = args.get(0) {
|
||||
let key = k.to_string_box().value;
|
||||
if let Some(v) = crate::runtime::modules_registry::get(&key) { return Ok(Some(v)); }
|
||||
if let Some(v) = crate::runtime::modules_registry::get(&key) {
|
||||
return Ok(Some(v));
|
||||
}
|
||||
}
|
||||
Ok(Some(Box::new(crate::box_trait::VoidBox::new())))
|
||||
}
|
||||
("env.task", "cancelCurrent") => { let tok = crate::runtime::global_hooks::current_group_token(); tok.cancel(); Ok(None) }
|
||||
("env.task", "currentToken") => { let tok = crate::runtime::global_hooks::current_group_token(); let tb = crate::boxes::token_box::TokenBox::from_token(tok); Ok(Some(Box::new(tb))) }
|
||||
("env.debug", "trace") => { if std::env::var("NYASH_DEBUG_TRACE").ok().as_deref() == Some("1") { for a in args { eprintln!("[debug.trace] {}", a.to_string_box().value); } } Ok(None) }
|
||||
("env.runtime", "checkpoint") => { if crate::config::env::runtime_checkpoint_trace() { eprintln!("[runtime.checkpoint] reached"); } crate::runtime::global_hooks::safepoint_and_poll(); Ok(None) }
|
||||
("env.future", "new") | ("env.future", "birth") => { let fut = crate::boxes::future::FutureBox::new(); if let Some(v) = args.get(0) { fut.set_result(v.clone_box()); } Ok(Some(Box::new(fut))) }
|
||||
("env.future", "set") => { if args.len() >= 2 { if let Some(fut) = args[0].as_any().downcast_ref::<crate::boxes::future::FutureBox>() { fut.set_result(args[1].clone_box()); } } Ok(None) }
|
||||
("env.future", "await") => { 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 = crate::config::env::await_max_ms(); let start = std::time::Instant::now(); let mut spins = 0usize; while !fut.ready() { crate::runtime::global_hooks::safepoint_and_poll(); std::thread::yield_now(); spins += 1; if spins % 1024 == 0 { std::thread::sleep(std::time::Duration::from_millis(1)); } if start.elapsed() >= std::time::Duration::from_millis(max_ms) { let err = crate::box_trait::StringBox::new("Timeout"); return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))); } } return match fut.wait_and_get() { Ok(v) => Ok(Some(Box::new(NyashResultBox::new_ok(v)))), Err(e) => { let err = crate::box_trait::StringBox::new(format!("Error: {}", e)); Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err))))) } }; } else { return Ok(Some(Box::new(NyashResultBox::new_ok(arg.clone_box())))); } } Ok(Some(Box::new(crate::boxes::result::NyashResultBox::new_err(Box::new(crate::box_trait::StringBox::new("InvalidArgs")))))) }
|
||||
_ => Err(BidError::PluginError)
|
||||
("env.task", "cancelCurrent") => {
|
||||
let tok = crate::runtime::global_hooks::current_group_token();
|
||||
tok.cancel();
|
||||
Ok(None)
|
||||
}
|
||||
("env.task", "currentToken") => {
|
||||
let tok = crate::runtime::global_hooks::current_group_token();
|
||||
let tb = crate::boxes::token_box::TokenBox::from_token(tok);
|
||||
Ok(Some(Box::new(tb)))
|
||||
}
|
||||
("env.debug", "trace") => {
|
||||
if std::env::var("NYASH_DEBUG_TRACE").ok().as_deref() == Some("1") {
|
||||
for a in args {
|
||||
eprintln!("[debug.trace] {}", a.to_string_box().value);
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
("env.runtime", "checkpoint") => {
|
||||
if crate::config::env::runtime_checkpoint_trace() {
|
||||
eprintln!("[runtime.checkpoint] reached");
|
||||
}
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
Ok(None)
|
||||
}
|
||||
("env.future", "new") | ("env.future", "birth") => {
|
||||
let fut = crate::boxes::future::FutureBox::new();
|
||||
if let Some(v) = args.get(0) {
|
||||
fut.set_result(v.clone_box());
|
||||
}
|
||||
Ok(Some(Box::new(fut)))
|
||||
}
|
||||
("env.future", "set") => {
|
||||
if args.len() >= 2 {
|
||||
if let Some(fut) = args[0]
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::future::FutureBox>()
|
||||
{
|
||||
fut.set_result(args[1].clone_box());
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
("env.future", "await") => {
|
||||
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 = crate::config::env::await_max_ms();
|
||||
let start = std::time::Instant::now();
|
||||
let mut spins = 0usize;
|
||||
while !fut.ready() {
|
||||
crate::runtime::global_hooks::safepoint_and_poll();
|
||||
std::thread::yield_now();
|
||||
spins += 1;
|
||||
if spins % 1024 == 0 {
|
||||
std::thread::sleep(std::time::Duration::from_millis(1));
|
||||
}
|
||||
if start.elapsed() >= std::time::Duration::from_millis(max_ms) {
|
||||
let err = crate::box_trait::StringBox::new("Timeout");
|
||||
return Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err)))));
|
||||
}
|
||||
}
|
||||
return match fut.wait_and_get() {
|
||||
Ok(v) => Ok(Some(Box::new(NyashResultBox::new_ok(v)))),
|
||||
Err(e) => {
|
||||
let err = crate::box_trait::StringBox::new(format!("Error: {}", e));
|
||||
Ok(Some(Box::new(NyashResultBox::new_err(Box::new(err)))))
|
||||
}
|
||||
};
|
||||
} else {
|
||||
return Ok(Some(Box::new(NyashResultBox::new_ok(arg.clone_box()))));
|
||||
}
|
||||
}
|
||||
Ok(Some(Box::new(
|
||||
crate::boxes::result::NyashResultBox::new_err(Box::new(
|
||||
crate::box_trait::StringBox::new("InvalidArgs"),
|
||||
)),
|
||||
)))
|
||||
}
|
||||
_ => Err(BidError::PluginError),
|
||||
}
|
||||
}
|
||||
|
||||
fn resolve_method_id_from_file(&self, box_type: &str, method_name: &str) -> BidResult<u32> {
|
||||
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 = super::errors::from_toml(toml::from_str(&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?))?;
|
||||
let toml_value: toml::Value = super::errors::from_toml(toml::from_str(
|
||||
&std::fs::read_to_string(cfg_path).map_err(|_| BidError::PluginError)?,
|
||||
))?;
|
||||
if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) {
|
||||
if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) { if let Some(m) = bc.methods.get(method_name) { return Ok(m.method_id); } }
|
||||
if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) {
|
||||
if let Some(m) = bc.methods.get(method_name) {
|
||||
return Ok(m.method_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
Err(BidError::InvalidMethod)
|
||||
}
|
||||
@ -264,8 +485,16 @@ impl PluginLoaderV2 {
|
||||
if let Some(cfg) = &self.config {
|
||||
if let Some((lib_name, _)) = cfg.find_library_for_box(box_type) {
|
||||
if let Some(cfg_path) = self.config_path.as_deref() {
|
||||
if let Ok(toml_value) = toml::from_str::<toml::Value>(&std::fs::read_to_string(cfg_path).unwrap_or_default()) {
|
||||
if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) { return bc.methods.get(method_name).map(|m| m.returns_result).unwrap_or(false); }
|
||||
if let Ok(toml_value) = toml::from_str::<toml::Value>(
|
||||
&std::fs::read_to_string(cfg_path).unwrap_or_default(),
|
||||
) {
|
||||
if let Some(bc) = cfg.get_box_config(&lib_name, box_type, &toml_value) {
|
||||
return bc
|
||||
.methods
|
||||
.get(method_name)
|
||||
.map(|m| m.returns_result)
|
||||
.unwrap_or(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,41 +503,83 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
|
||||
/// Resolve (type_id, method_id, returns_result) for a box_type.method
|
||||
pub fn resolve_method_handle(&self, box_type: &str, method_name: &str) -> BidResult<(u32, u32, bool)> {
|
||||
pub fn resolve_method_handle(
|
||||
&self,
|
||||
box_type: &str,
|
||||
method_name: &str,
|
||||
) -> BidResult<(u32, u32, bool)> {
|
||||
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)?;
|
||||
let bc = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?;
|
||||
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)?;
|
||||
let bc = cfg
|
||||
.get_box_config(lib_name, box_type, &toml_value)
|
||||
.ok_or(BidError::InvalidType)?;
|
||||
let m = bc.methods.get(method_name).ok_or(BidError::InvalidMethod)?;
|
||||
Ok((bc.type_id, m.method_id, m.returns_result))
|
||||
}
|
||||
|
||||
pub fn invoke_instance_method(&self, box_type: &str, method_name: &str, instance_id: u32, args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
pub fn invoke_instance_method(
|
||||
&self,
|
||||
box_type: &str,
|
||||
method_name: &str,
|
||||
instance_id: u32,
|
||||
args: &[Box<dyn NyashBox>],
|
||||
) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
// Non-recursive direct bridge for minimal methods used by semantics and basic VM paths
|
||||
// Resolve library/type/method ids from cached config
|
||||
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, _lib_def) = cfg.find_library_for_box(box_type).ok_or(BidError::InvalidType)?;
|
||||
let box_conf = cfg.get_box_config(lib_name, box_type, &toml_value).ok_or(BidError::InvalidType)?;
|
||||
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, _lib_def) = cfg
|
||||
.find_library_for_box(box_type)
|
||||
.ok_or(BidError::InvalidType)?;
|
||||
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 method = box_conf.methods.get(method_name).ok_or(BidError::InvalidMethod)?;
|
||||
let method = box_conf
|
||||
.methods
|
||||
.get(method_name)
|
||||
.ok_or(BidError::InvalidMethod)?;
|
||||
// Get plugin handle
|
||||
let plugins = self.plugins.read().map_err(|_| BidError::PluginError)?;
|
||||
let plugin = plugins.get(lib_name).ok_or(BidError::PluginError)?;
|
||||
// Encode TLV args via shared helper (numeric→string→toString)
|
||||
let tlv = crate::runtime::plugin_ffi_common::encode_args(args);
|
||||
let (_code, out_len, out) = super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, method.method_id, instance_id, &tlv);
|
||||
let (_code, out_len, out) = super::host_bridge::invoke_alloc(
|
||||
plugin.invoke_fn,
|
||||
type_id,
|
||||
method.method_id,
|
||||
instance_id,
|
||||
&tlv,
|
||||
);
|
||||
// Decode TLV (first entry) generically
|
||||
if let Some((tag, _sz, payload)) = crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len]) {
|
||||
if let Some((tag, _sz, payload)) =
|
||||
crate::runtime::plugin_ffi_common::decode::tlv_first(&out[..out_len])
|
||||
{
|
||||
let bx: Box<dyn NyashBox> = match tag {
|
||||
1 => Box::new(crate::box_trait::BoolBox::new(crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false))),
|
||||
2 => Box::new(crate::box_trait::IntegerBox::new(crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or(0) as i64)),
|
||||
1 => Box::new(crate::box_trait::BoolBox::new(
|
||||
crate::runtime::plugin_ffi_common::decode::bool(payload).unwrap_or(false),
|
||||
)),
|
||||
2 => Box::new(crate::box_trait::IntegerBox::new(
|
||||
crate::runtime::plugin_ffi_common::decode::i32(payload).unwrap_or(0) as i64,
|
||||
)),
|
||||
3 => {
|
||||
// i64 payload
|
||||
if payload.len() == 8 { let mut b=[0u8;8]; b.copy_from_slice(payload); Box::new(crate::box_trait::IntegerBox::new(i64::from_le_bytes(b))) }
|
||||
else { Box::new(crate::box_trait::IntegerBox::new(0)) }
|
||||
if payload.len() == 8 {
|
||||
let mut b = [0u8; 8];
|
||||
b.copy_from_slice(payload);
|
||||
Box::new(crate::box_trait::IntegerBox::new(i64::from_le_bytes(b)))
|
||||
} else {
|
||||
Box::new(crate::box_trait::IntegerBox::new(0))
|
||||
}
|
||||
}
|
||||
5 => {
|
||||
let x = crate::runtime::plugin_ffi_common::decode::f64(payload).unwrap_or(0.0);
|
||||
@ -320,7 +591,9 @@ impl PluginLoaderV2 {
|
||||
}
|
||||
8 => {
|
||||
// Plugin handle (type_id, instance_id) → wrap into PluginBoxV2
|
||||
if let Some((ret_type, inst)) = crate::runtime::plugin_ffi_common::decode::plugin_handle(payload) {
|
||||
if let Some((ret_type, inst)) =
|
||||
crate::runtime::plugin_ffi_common::decode::plugin_handle(payload)
|
||||
{
|
||||
let handle = Arc::new(super::types::PluginHandleInner {
|
||||
type_id: ret_type,
|
||||
invoke_fn: plugin.invoke_fn,
|
||||
@ -328,13 +601,20 @@ impl PluginLoaderV2 {
|
||||
fini_method_id: None,
|
||||
finalized: std::sync::atomic::AtomicBool::new(false),
|
||||
});
|
||||
Box::new(super::types::PluginBoxV2 { box_type: box_type.to_string(), inner: handle })
|
||||
} else { Box::new(crate::box_trait::VoidBox::new()) }
|
||||
Box::new(super::types::PluginBoxV2 {
|
||||
box_type: box_type.to_string(),
|
||||
inner: handle,
|
||||
})
|
||||
} else {
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
}
|
||||
9 => {
|
||||
// Host handle (u64) → try to map back to BoxRef, else void
|
||||
if let Some(u) = crate::runtime::plugin_ffi_common::decode::u64(payload) {
|
||||
if let Some(arc) = crate::runtime::host_handles::get(u) { return Ok(Some(arc.share_box())); }
|
||||
if let Some(arc) = crate::runtime::host_handles::get(u) {
|
||||
return Ok(Some(arc.share_box()));
|
||||
}
|
||||
}
|
||||
Box::new(crate::box_trait::VoidBox::new())
|
||||
}
|
||||
@ -345,17 +625,31 @@ impl PluginLoaderV2 {
|
||||
Ok(Some(Box::new(crate::box_trait::VoidBox::new())))
|
||||
}
|
||||
|
||||
pub fn create_box(&self, box_type: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
||||
pub fn create_box(
|
||||
&self,
|
||||
box_type: &str,
|
||||
_args: &[Box<dyn NyashBox>],
|
||||
) -> BidResult<Box<dyn NyashBox>> {
|
||||
// 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)?;
|
||||
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 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 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
|
||||
@ -364,15 +658,26 @@ impl PluginLoaderV2 {
|
||||
|
||||
// Call birth (no args TLV) and read returned instance id (little-endian u32 in bytes 0..4)
|
||||
if dbg_on() {
|
||||
eprintln!("[PluginLoaderV2] invoking birth: box_type={} type_id={} birth_id={}", box_type, type_id, birth_id);
|
||||
eprintln!(
|
||||
"[PluginLoaderV2] invoking birth: box_type={} type_id={} birth_id={}",
|
||||
box_type, type_id, birth_id
|
||||
);
|
||||
}
|
||||
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);
|
||||
let (code, out_len, out_buf) =
|
||||
super::host_bridge::invoke_alloc(plugin.invoke_fn, type_id, birth_id, 0, &tlv);
|
||||
if dbg_on() {
|
||||
eprintln!("[PluginLoaderV2] create_box: box_type={} type_id={} birth_id={} code={} out_len={}", box_type, type_id, birth_id, code, out_len);
|
||||
if out_len > 0 { eprintln!("[PluginLoaderV2] create_box: out[0..min(8)]={:02x?}", &out_buf[..out_len.min(8)]); }
|
||||
if out_len > 0 {
|
||||
eprintln!(
|
||||
"[PluginLoaderV2] create_box: out[0..min(8)]={:02x?}",
|
||||
&out_buf[..out_len.min(8)]
|
||||
);
|
||||
}
|
||||
}
|
||||
if code != 0 || out_len < 4 {
|
||||
return Err(BidError::PluginError);
|
||||
}
|
||||
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 {
|
||||
@ -391,6 +696,8 @@ impl PluginLoaderV2 {
|
||||
/// Shutdown singletons: finalize and clear all singleton handles
|
||||
pub fn shutdown_singletons(&self) {
|
||||
let mut map = self.singletons.write().unwrap();
|
||||
for (_, handle) in map.drain() { handle.finalize_now(); }
|
||||
for (_, handle) in map.drain() {
|
||||
handle.finalize_now();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,12 +1,15 @@
|
||||
mod types;
|
||||
mod loader;
|
||||
mod globals;
|
||||
mod errors;
|
||||
mod globals;
|
||||
mod host_bridge;
|
||||
mod loader;
|
||||
mod types;
|
||||
|
||||
pub use types::{PluginBoxMetadata, PluginBoxV2, PluginHandleInner, NyashTypeBoxFfi, make_plugin_box_v2, construct_plugin_box};
|
||||
pub use loader::PluginLoaderV2;
|
||||
pub use globals::{get_global_loader_v2, init_global_loader_v2, shutdown_plugins_v2};
|
||||
pub use loader::PluginLoaderV2;
|
||||
pub use types::{
|
||||
construct_plugin_box, make_plugin_box_v2, NyashTypeBoxFfi, PluginBoxMetadata, PluginBoxV2,
|
||||
PluginHandleInner,
|
||||
};
|
||||
|
||||
pub fn metadata_for_type_id(type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
let loader = get_global_loader_v2();
|
||||
@ -14,4 +17,6 @@ pub fn metadata_for_type_id(type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
guard.metadata_for_type_id(type_id)
|
||||
}
|
||||
|
||||
pub fn backend_kind() -> &'static str { "enabled" }
|
||||
pub fn backend_kind() -> &'static str {
|
||||
"enabled"
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
use crate::box_trait::{BoxCore, NyashBox, StringBox};
|
||||
use super::host_bridge::InvokeFn;
|
||||
use crate::box_trait::{BoxCore, NyashBox, StringBox};
|
||||
use std::any::Any;
|
||||
use std::sync::Arc;
|
||||
|
||||
fn dbg_on() -> bool { std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1" }
|
||||
fn dbg_on() -> bool {
|
||||
std::env::var("NYASH_DEBUG_PLUGIN").unwrap_or_default() == "1"
|
||||
}
|
||||
|
||||
/// Loaded plugin information (library handle + exported addresses)
|
||||
pub struct LoadedPluginV2 {
|
||||
@ -36,9 +38,18 @@ pub struct PluginHandleInner {
|
||||
impl Drop for PluginHandleInner {
|
||||
fn drop(&mut self) {
|
||||
if let Some(fini_id) = self.fini_method_id {
|
||||
if !self.finalized.swap(true, std::sync::atomic::Ordering::SeqCst) {
|
||||
if !self
|
||||
.finalized
|
||||
.swap(true, std::sync::atomic::Ordering::SeqCst)
|
||||
{
|
||||
let tlv_args: [u8; 4] = [1, 0, 0, 0];
|
||||
let _ = super::host_bridge::invoke_alloc(self.invoke_fn, self.type_id, fini_id, self.instance_id, &tlv_args);
|
||||
let _ = super::host_bridge::invoke_alloc(
|
||||
self.invoke_fn,
|
||||
self.type_id,
|
||||
fini_id,
|
||||
self.instance_id,
|
||||
&tlv_args,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -47,10 +58,19 @@ impl Drop for PluginHandleInner {
|
||||
impl PluginHandleInner {
|
||||
pub fn finalize_now(&self) {
|
||||
if let Some(fini_id) = self.fini_method_id {
|
||||
if !self.finalized.swap(true, std::sync::atomic::Ordering::SeqCst) {
|
||||
if !self
|
||||
.finalized
|
||||
.swap(true, std::sync::atomic::Ordering::SeqCst)
|
||||
{
|
||||
crate::runtime::leak_tracker::finalize_plugin("PluginBox", self.instance_id);
|
||||
let tlv_args: [u8; 4] = [1, 0, 0, 0];
|
||||
let _ = super::host_bridge::invoke_alloc(self.invoke_fn, self.type_id, fini_id, self.instance_id, &tlv_args);
|
||||
let _ = super::host_bridge::invoke_alloc(
|
||||
self.invoke_fn,
|
||||
self.type_id,
|
||||
fini_id,
|
||||
self.instance_id,
|
||||
&tlv_args,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -76,46 +96,113 @@ pub struct PluginBoxV2 {
|
||||
}
|
||||
|
||||
impl BoxCore for PluginBoxV2 {
|
||||
fn box_id(&self) -> u64 { self.inner.instance_id as u64 }
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> { None }
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { write!(f, "{}({})", self.box_type, self.inner.instance_id) }
|
||||
fn as_any(&self) -> &dyn Any { self }
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any { self }
|
||||
fn box_id(&self) -> u64 {
|
||||
self.inner.instance_id as u64
|
||||
}
|
||||
fn parent_type_id(&self) -> Option<std::any::TypeId> {
|
||||
None
|
||||
}
|
||||
fn fmt_box(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
write!(f, "{}({})", self.box_type, self.inner.instance_id)
|
||||
}
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl NyashBox for PluginBoxV2 {
|
||||
fn is_identity(&self) -> bool { true }
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self.box_type.as_str() { "FileBox" => "FileBox", _ => "PluginBoxV2" }
|
||||
fn is_identity(&self) -> bool {
|
||||
true
|
||||
}
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
if dbg_on() { eprintln!("[PluginBoxV2] clone_box {}({})", self.box_type, self.inner.instance_id); }
|
||||
let tlv_args = [1u8, 0, 0, 0];
|
||||
let (result, out_len, out_buf) = super::host_bridge::invoke_alloc(self.inner.invoke_fn, self.inner.type_id, 0, 0, &tlv_args);
|
||||
if result == 0 && out_len >= 4 {
|
||||
let new_instance_id = u32::from_le_bytes([out_buf[0], out_buf[1], out_buf[2], out_buf[3]]);
|
||||
Box::new(PluginBoxV2 {
|
||||
box_type: self.box_type.clone(),
|
||||
inner: Arc::new(PluginHandleInner { type_id: self.inner.type_id, invoke_fn: self.inner.invoke_fn, instance_id: new_instance_id, fini_method_id: self.inner.fini_method_id, finalized: std::sync::atomic::AtomicBool::new(false) }),
|
||||
})
|
||||
} else {
|
||||
Box::new(StringBox::new(format!("Clone failed for {}", self.box_type)))
|
||||
fn type_name(&self) -> &'static str {
|
||||
match self.box_type.as_str() {
|
||||
"FileBox" => "FileBox",
|
||||
_ => "PluginBoxV2",
|
||||
}
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox { StringBox::new(format!("{}({})", self.box_type, self.inner.instance_id)) }
|
||||
fn equals(&self, _other: &dyn NyashBox) -> crate::box_trait::BoolBox { crate::box_trait::BoolBox::new(false) }
|
||||
fn share_box(&self) -> Box<dyn NyashBox> { Box::new(PluginBoxV2 { box_type: self.box_type.clone(), inner: self.inner.clone() }) }
|
||||
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||
if dbg_on() {
|
||||
eprintln!(
|
||||
"[PluginBoxV2] clone_box {}({})",
|
||||
self.box_type, self.inner.instance_id
|
||||
);
|
||||
}
|
||||
let tlv_args = [1u8, 0, 0, 0];
|
||||
let (result, out_len, out_buf) = super::host_bridge::invoke_alloc(
|
||||
self.inner.invoke_fn,
|
||||
self.inner.type_id,
|
||||
0,
|
||||
0,
|
||||
&tlv_args,
|
||||
);
|
||||
if result == 0 && out_len >= 4 {
|
||||
let new_instance_id =
|
||||
u32::from_le_bytes([out_buf[0], out_buf[1], out_buf[2], out_buf[3]]);
|
||||
Box::new(PluginBoxV2 {
|
||||
box_type: self.box_type.clone(),
|
||||
inner: Arc::new(PluginHandleInner {
|
||||
type_id: self.inner.type_id,
|
||||
invoke_fn: self.inner.invoke_fn,
|
||||
instance_id: new_instance_id,
|
||||
fini_method_id: self.inner.fini_method_id,
|
||||
finalized: std::sync::atomic::AtomicBool::new(false),
|
||||
}),
|
||||
})
|
||||
} else {
|
||||
Box::new(StringBox::new(format!(
|
||||
"Clone failed for {}",
|
||||
self.box_type
|
||||
)))
|
||||
}
|
||||
}
|
||||
fn to_string_box(&self) -> StringBox {
|
||||
StringBox::new(format!("{}({})", self.box_type, self.inner.instance_id))
|
||||
}
|
||||
fn equals(&self, _other: &dyn NyashBox) -> crate::box_trait::BoolBox {
|
||||
crate::box_trait::BoolBox::new(false)
|
||||
}
|
||||
fn share_box(&self) -> Box<dyn NyashBox> {
|
||||
Box::new(PluginBoxV2 {
|
||||
box_type: self.box_type.clone(),
|
||||
inner: self.inner.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginBoxV2 {
|
||||
pub fn instance_id(&self) -> u32 { self.inner.instance_id }
|
||||
pub fn finalize_now(&self) { self.inner.finalize_now() }
|
||||
pub fn is_finalized(&self) -> bool { self.inner.finalized.load(std::sync::atomic::Ordering::SeqCst) }
|
||||
pub fn instance_id(&self) -> u32 {
|
||||
self.inner.instance_id
|
||||
}
|
||||
pub fn finalize_now(&self) {
|
||||
self.inner.finalize_now()
|
||||
}
|
||||
pub fn is_finalized(&self) -> bool {
|
||||
self.inner
|
||||
.finalized
|
||||
.load(std::sync::atomic::Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper to construct a PluginBoxV2 from raw ids and invoke pointer safely
|
||||
pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: InvokeFn) -> PluginBoxV2 {
|
||||
PluginBoxV2 { box_type, inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None, finalized: std::sync::atomic::AtomicBool::new(false) }) }
|
||||
pub fn make_plugin_box_v2(
|
||||
box_type: String,
|
||||
type_id: u32,
|
||||
instance_id: u32,
|
||||
invoke_fn: InvokeFn,
|
||||
) -> PluginBoxV2 {
|
||||
PluginBoxV2 {
|
||||
box_type,
|
||||
inner: Arc::new(PluginHandleInner {
|
||||
type_id,
|
||||
invoke_fn,
|
||||
instance_id,
|
||||
fini_method_id: None,
|
||||
finalized: std::sync::atomic::AtomicBool::new(false),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Public helper to construct a PluginBoxV2 from raw parts (for VM/JIT integration)
|
||||
@ -126,5 +213,14 @@ pub fn construct_plugin_box(
|
||||
instance_id: u32,
|
||||
fini_method_id: Option<u32>,
|
||||
) -> PluginBoxV2 {
|
||||
PluginBoxV2 { box_type, inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id, finalized: std::sync::atomic::AtomicBool::new(false) }) }
|
||||
PluginBoxV2 {
|
||||
box_type,
|
||||
inner: Arc::new(PluginHandleInner {
|
||||
type_id,
|
||||
invoke_fn,
|
||||
instance_id,
|
||||
fini_method_id,
|
||||
finalized: std::sync::atomic::AtomicBool::new(false),
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,4 +9,3 @@ mod stub;
|
||||
pub use enabled::*;
|
||||
#[cfg(any(not(feature = "plugins"), target_arch = "wasm32"))]
|
||||
pub use stub::*;
|
||||
|
||||
|
||||
@ -1,9 +1,10 @@
|
||||
use crate::bid::{BidResult, BidError};
|
||||
use crate::bid::{BidError, BidResult};
|
||||
use crate::box_trait::NyashBox;
|
||||
use once_cell::sync::Lazy;
|
||||
use std::sync::{Arc, RwLock};
|
||||
|
||||
pub type InvokeFn = unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32;
|
||||
pub type InvokeFn =
|
||||
unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32;
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct PluginBoxV2 {
|
||||
@ -28,32 +29,82 @@ pub struct PluginHandleInner {
|
||||
pub fini_method_id: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct PluginLoaderV2 { pub config: Option<()> }
|
||||
impl PluginLoaderV2 { pub fn new() -> Self { Self { config: None } } }
|
||||
pub struct PluginLoaderV2 {
|
||||
pub config: Option<()>,
|
||||
}
|
||||
impl PluginLoaderV2 {
|
||||
pub fn new() -> Self {
|
||||
Self { config: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl PluginLoaderV2 {
|
||||
pub fn load_config(&mut self, _p: &str) -> BidResult<()> { Ok(()) }
|
||||
pub fn load_all_plugins(&self) -> BidResult<()> { Ok(()) }
|
||||
pub fn create_box(&self, _t: &str, _a: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> { Err(BidError::PluginError) }
|
||||
pub fn extern_call(&self, _iface_name: &str, _method_name: &str, _args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> { Err(BidError::PluginError) }
|
||||
pub fn invoke_instance_method(&self, _box_type: &str, _method_name: &str, _instance_id: u32, _args: &[Box<dyn NyashBox>]) -> BidResult<Option<Box<dyn NyashBox>>> { Err(BidError::PluginError) }
|
||||
pub fn metadata_for_type_id(&self, _type_id: u32) -> Option<PluginBoxMetadata> { None }
|
||||
pub fn load_config(&mut self, _p: &str) -> BidResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
pub fn load_all_plugins(&self) -> BidResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
pub fn create_box(&self, _t: &str, _a: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
||||
Err(BidError::PluginError)
|
||||
}
|
||||
pub fn extern_call(
|
||||
&self,
|
||||
_iface_name: &str,
|
||||
_method_name: &str,
|
||||
_args: &[Box<dyn NyashBox>],
|
||||
) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
Err(BidError::PluginError)
|
||||
}
|
||||
pub fn invoke_instance_method(
|
||||
&self,
|
||||
_box_type: &str,
|
||||
_method_name: &str,
|
||||
_instance_id: u32,
|
||||
_args: &[Box<dyn NyashBox>],
|
||||
) -> BidResult<Option<Box<dyn NyashBox>>> {
|
||||
Err(BidError::PluginError)
|
||||
}
|
||||
pub fn metadata_for_type_id(&self, _type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
None
|
||||
}
|
||||
pub fn shutdown_singletons(&self) {}
|
||||
}
|
||||
|
||||
static GLOBAL_LOADER_V2: Lazy<Arc<RwLock<PluginLoaderV2>>> = Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new())));
|
||||
pub fn get_global_loader_v2() -> Arc<RwLock<PluginLoaderV2>> { GLOBAL_LOADER_V2.clone() }
|
||||
pub fn init_global_loader_v2(_config_path: &str) -> BidResult<()> { Ok(()) }
|
||||
pub fn shutdown_plugins_v2() -> BidResult<()> { Ok(()) }
|
||||
static GLOBAL_LOADER_V2: Lazy<Arc<RwLock<PluginLoaderV2>>> =
|
||||
Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new())));
|
||||
pub fn get_global_loader_v2() -> Arc<RwLock<PluginLoaderV2>> {
|
||||
GLOBAL_LOADER_V2.clone()
|
||||
}
|
||||
pub fn init_global_loader_v2(_config_path: &str) -> BidResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
pub fn shutdown_plugins_v2() -> BidResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn backend_kind() -> &'static str { "stub" }
|
||||
pub fn backend_kind() -> &'static str {
|
||||
"stub"
|
||||
}
|
||||
|
||||
pub fn metadata_for_type_id(_type_id: u32) -> Option<PluginBoxMetadata> { None }
|
||||
pub fn metadata_for_type_id(_type_id: u32) -> Option<PluginBoxMetadata> {
|
||||
None
|
||||
}
|
||||
|
||||
pub fn make_plugin_box_v2(box_type: String, type_id: u32, instance_id: u32, invoke_fn: InvokeFn) -> PluginBoxV2 {
|
||||
pub fn make_plugin_box_v2(
|
||||
box_type: String,
|
||||
type_id: u32,
|
||||
instance_id: u32,
|
||||
invoke_fn: InvokeFn,
|
||||
) -> PluginBoxV2 {
|
||||
PluginBoxV2 {
|
||||
box_type,
|
||||
inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id: None }),
|
||||
inner: Arc::new(PluginHandleInner {
|
||||
type_id,
|
||||
invoke_fn,
|
||||
instance_id,
|
||||
fini_method_id: None,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -66,6 +117,11 @@ pub fn construct_plugin_box(
|
||||
) -> PluginBoxV2 {
|
||||
PluginBoxV2 {
|
||||
box_type,
|
||||
inner: Arc::new(PluginHandleInner { type_id, invoke_fn, instance_id, fini_method_id }),
|
||||
inner: Arc::new(PluginHandleInner {
|
||||
type_id,
|
||||
invoke_fn,
|
||||
instance_id,
|
||||
fini_method_id,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
@ -10,10 +10,15 @@ pub trait Scheduler: Send + Sync {
|
||||
/// Poll scheduler: run due tasks and a limited number of queued tasks.
|
||||
fn poll(&self) {}
|
||||
/// Cooperative yield point (no-op for single-thread).
|
||||
fn yield_now(&self) { }
|
||||
fn yield_now(&self) {}
|
||||
|
||||
/// Optional: spawn with a cancellation token. Default delegates to spawn.
|
||||
fn spawn_with_token(&self, name: &str, _token: CancellationToken, f: Box<dyn FnOnce() + Send + 'static>) {
|
||||
fn spawn_with_token(
|
||||
&self,
|
||||
name: &str,
|
||||
_token: CancellationToken,
|
||||
f: Box<dyn FnOnce() + Send + 'static>,
|
||||
) {
|
||||
self.spawn(name, f)
|
||||
}
|
||||
}
|
||||
@ -30,17 +35,24 @@ pub struct SingleThreadScheduler {
|
||||
|
||||
impl SingleThreadScheduler {
|
||||
pub fn new() -> Self {
|
||||
Self { queue: Arc::new(Mutex::new(VecDeque::new())), delayed: Arc::new(Mutex::new(Vec::new())) }
|
||||
Self {
|
||||
queue: Arc::new(Mutex::new(VecDeque::new())),
|
||||
delayed: Arc::new(Mutex::new(Vec::new())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Scheduler for SingleThreadScheduler {
|
||||
fn spawn(&self, _name: &str, f: Box<dyn FnOnce() + Send + 'static>) {
|
||||
if let Ok(mut q) = self.queue.lock() { q.push_back(f); }
|
||||
if let Ok(mut q) = self.queue.lock() {
|
||||
q.push_back(f);
|
||||
}
|
||||
}
|
||||
fn spawn_after(&self, delay_ms: u64, _name: &str, f: Box<dyn FnOnce() + Send + 'static>) {
|
||||
let when = Instant::now() + Duration::from_millis(delay_ms);
|
||||
if let Ok(mut d) = self.delayed.lock() { d.push((when, f)); }
|
||||
if let Ok(mut d) = self.delayed.lock() {
|
||||
d.push((when, f));
|
||||
}
|
||||
}
|
||||
fn poll(&self) {
|
||||
// Move due delayed tasks to queue
|
||||
@ -52,20 +64,36 @@ impl Scheduler for SingleThreadScheduler {
|
||||
while i < d.len() {
|
||||
if d[i].0 <= now {
|
||||
let (_when, task) = d.remove(i);
|
||||
if let Ok(mut q) = self.queue.lock() { q.push_back(task); }
|
||||
if let Ok(mut q) = self.queue.lock() {
|
||||
q.push_back(task);
|
||||
}
|
||||
moved += 1;
|
||||
} else { i += 1; }
|
||||
} else {
|
||||
i += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Run up to budget queued tasks
|
||||
let budget: usize = std::env::var("NYASH_SCHED_POLL_BUDGET")
|
||||
.ok().and_then(|s| s.parse().ok()).filter(|&n: &usize| n > 0).unwrap_or(1);
|
||||
.ok()
|
||||
.and_then(|s| s.parse().ok())
|
||||
.filter(|&n: &usize| n > 0)
|
||||
.unwrap_or(1);
|
||||
let mut ran = 0usize;
|
||||
while ran < budget {
|
||||
let task_opt = {
|
||||
if let Ok(mut q) = self.queue.lock() { q.pop_front() } else { None }
|
||||
if let Ok(mut q) = self.queue.lock() {
|
||||
q.pop_front()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
};
|
||||
if let Some(task) = task_opt { task(); ran += 1; } else { break; }
|
||||
if let Some(task) = task_opt {
|
||||
task();
|
||||
ran += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if trace {
|
||||
eprintln!("[SCHED] poll moved={} ran={} budget={}", moved, ran, budget);
|
||||
@ -80,7 +108,13 @@ use std::sync::atomic::{AtomicBool, Ordering};
|
||||
pub struct CancellationToken(Arc<AtomicBool>);
|
||||
|
||||
impl CancellationToken {
|
||||
pub fn new() -> Self { Self(Arc::new(AtomicBool::new(false))) }
|
||||
pub fn cancel(&self) { self.0.store(true, Ordering::SeqCst); }
|
||||
pub fn is_cancelled(&self) -> bool { self.0.load(Ordering::SeqCst) }
|
||||
pub fn new() -> Self {
|
||||
Self(Arc::new(AtomicBool::new(false)))
|
||||
}
|
||||
pub fn cancel(&self) {
|
||||
self.0.store(true, Ordering::SeqCst);
|
||||
}
|
||||
pub fn is_cancelled(&self) -> bool {
|
||||
self.0.load(Ordering::SeqCst)
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,20 +5,27 @@
|
||||
* string/number coercions and common operation ordering.
|
||||
*/
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox, IntegerBox};
|
||||
use crate::box_trait::{IntegerBox, NyashBox, StringBox};
|
||||
|
||||
/// Try to unwrap InstanceBox and return inner if present
|
||||
fn maybe_unwrap_instance(b: &dyn NyashBox) -> &dyn NyashBox {
|
||||
if let Some(inst) = b.as_any().downcast_ref::<crate::instance_v2::InstanceBox>() {
|
||||
if let Some(ref inner) = inst.inner_content { return inner.as_ref(); }
|
||||
if let Some(ref inner) = inst.inner_content {
|
||||
return inner.as_ref();
|
||||
}
|
||||
}
|
||||
b
|
||||
}
|
||||
|
||||
/// Result.Ok(inner) → recurse helper
|
||||
fn maybe_unwrap_result_ok(b: &dyn NyashBox) -> &dyn NyashBox {
|
||||
if let Some(res) = b.as_any().downcast_ref::<crate::boxes::result::NyashResultBox>() {
|
||||
if let crate::boxes::result::NyashResultBox::Ok(inner) = res { return inner.as_ref(); }
|
||||
if let Some(res) = b
|
||||
.as_any()
|
||||
.downcast_ref::<crate::boxes::result::NyashResultBox>()
|
||||
{
|
||||
if let crate::boxes::result::NyashResultBox::Ok(inner) = res {
|
||||
return inner.as_ref();
|
||||
}
|
||||
}
|
||||
b
|
||||
}
|
||||
@ -27,23 +34,34 @@ fn maybe_unwrap_result_ok(b: &dyn NyashBox) -> &dyn NyashBox {
|
||||
pub fn coerce_to_string(b: &dyn NyashBox) -> Option<String> {
|
||||
let b = maybe_unwrap_instance(b);
|
||||
// Internal StringBox
|
||||
if let Some(s) = b.as_any().downcast_ref::<StringBox>() { return Some(s.value.clone()); }
|
||||
if let Some(s) = b.as_any().downcast_ref::<StringBox>() {
|
||||
return Some(s.value.clone());
|
||||
}
|
||||
// Result.Ok recursion
|
||||
let b2 = maybe_unwrap_result_ok(b);
|
||||
if !std::ptr::eq(b2 as *const _, b as *const _) {
|
||||
if let Some(s) = coerce_to_string(b2) { return Some(s); }
|
||||
if let Some(s) = coerce_to_string(b2) {
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
|
||||
// Plugin StringBox: prefer toUtf8; fallback to toString
|
||||
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(pb) = b
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
{
|
||||
// StringBox.toUtf8
|
||||
if pb.box_type == "StringBox" {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[]) {
|
||||
if let Ok(ret) =
|
||||
ro.invoke_instance_method("StringBox", "toUtf8", pb.instance_id(), &[])
|
||||
{
|
||||
if let Some(vb) = ret {
|
||||
if let Some(sb2) = vb.as_any().downcast_ref::<StringBox>() { return Some(sb2.value.clone()); }
|
||||
if let Some(sb2) = vb.as_any().downcast_ref::<StringBox>() {
|
||||
return Some(sb2.value.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -52,8 +70,14 @@ pub fn coerce_to_string(b: &dyn NyashBox) -> Option<String> {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method(&pb.box_type, "toString", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret { if let Some(s) = coerce_to_string(vb.as_ref()) { return Some(s); } }
|
||||
if let Ok(ret) =
|
||||
ro.invoke_instance_method(&pb.box_type, "toString", pb.instance_id(), &[])
|
||||
{
|
||||
if let Some(vb) = ret {
|
||||
if let Some(s) = coerce_to_string(vb.as_ref()) {
|
||||
return Some(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -63,17 +87,28 @@ pub fn coerce_to_string(b: &dyn NyashBox) -> Option<String> {
|
||||
/// Best-effort integer coercion used by all backends.
|
||||
pub fn coerce_to_i64(b: &dyn NyashBox) -> Option<i64> {
|
||||
let b = maybe_unwrap_instance(b);
|
||||
if let Some(i) = b.as_any().downcast_ref::<IntegerBox>() { return Some(i.value); }
|
||||
if let Some(i) = b.as_any().downcast_ref::<IntegerBox>() {
|
||||
return Some(i.value);
|
||||
}
|
||||
|
||||
// Plugin numeric getters
|
||||
if let Some(pb) = b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
if let Some(pb) = b
|
||||
.as_any()
|
||||
.downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>()
|
||||
{
|
||||
// IntegerBox.get -> IntegerBox
|
||||
if pb.box_type == "IntegerBox" {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method("IntegerBox", "get", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret { if let Some(ii) = vb.as_any().downcast_ref::<IntegerBox>() { return Some(ii.value); } }
|
||||
if let Ok(ret) =
|
||||
ro.invoke_instance_method("IntegerBox", "get", pb.instance_id(), &[])
|
||||
{
|
||||
if let Some(vb) = ret {
|
||||
if let Some(ii) = vb.as_any().downcast_ref::<IntegerBox>() {
|
||||
return Some(ii.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -82,8 +117,14 @@ pub fn coerce_to_i64(b: &dyn NyashBox) -> Option<i64> {
|
||||
let host = crate::runtime::get_global_plugin_host();
|
||||
let read_res = host.read();
|
||||
if let Ok(ro) = read_res {
|
||||
if let Ok(ret) = ro.invoke_instance_method("FloatBox", "toDouble", pb.instance_id(), &[]) {
|
||||
if let Some(vb) = ret { if let Some(fb) = vb.as_any().downcast_ref::<crate::boxes::FloatBox>() { return Some(fb.value as i64); } }
|
||||
if let Ok(ret) =
|
||||
ro.invoke_instance_method("FloatBox", "toDouble", pb.instance_id(), &[])
|
||||
{
|
||||
if let Some(vb) = ret {
|
||||
if let Some(fb) = vb.as_any().downcast_ref::<crate::boxes::FloatBox>() {
|
||||
return Some(fb.value as i64);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,23 +1,26 @@
|
||||
//! プラグインシステム統合テスト
|
||||
//!
|
||||
//!
|
||||
//! プラグインBoxの透過的切り替えをテスト
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::super::{PluginConfig, BoxFactoryRegistry};
|
||||
use crate::runtime::box_registry::BoxProvider;
|
||||
use crate::box_trait::{NyashBox, StringBox};
|
||||
use super::super::{BoxFactoryRegistry, PluginConfig};
|
||||
use crate::bid::{BidHandle, BoxTypeId};
|
||||
|
||||
use crate::box_trait::{NyashBox, StringBox};
|
||||
use crate::runtime::box_registry::BoxProvider;
|
||||
|
||||
fn dummy_filebox_constructor(args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String> {
|
||||
// ダミーFileBox作成(ビルトイン版シミュレーション)
|
||||
if args.is_empty() {
|
||||
Ok(Box::new(StringBox::new("DummyFileBox")))
|
||||
} else {
|
||||
Ok(Box::new(StringBox::new(&format!("DummyFileBox({})", args[0].to_string_box().value))))
|
||||
Ok(Box::new(StringBox::new(&format!(
|
||||
"DummyFileBox({})",
|
||||
args[0].to_string_box().value
|
||||
))))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_plugin_config_parsing() {
|
||||
let toml = r#"
|
||||
@ -25,29 +28,34 @@ mod tests {
|
||||
FileBox = "filebox"
|
||||
StringBox = "custom_string"
|
||||
"#;
|
||||
|
||||
|
||||
let config = PluginConfig::parse(toml).unwrap();
|
||||
assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string()));
|
||||
assert_eq!(config.plugins.get("StringBox"), Some(&"custom_string".to_string()));
|
||||
assert_eq!(
|
||||
config.plugins.get("StringBox"),
|
||||
Some(&"custom_string".to_string())
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_box_registry_builtin() {
|
||||
let registry = BoxFactoryRegistry::new();
|
||||
registry.register_builtin("FileBox", dummy_filebox_constructor);
|
||||
|
||||
|
||||
let result = registry.create_box("FileBox", &[]).unwrap();
|
||||
assert_eq!(result.to_string_box().value, "DummyFileBox");
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_box_registry_plugin_override() {
|
||||
let registry = BoxFactoryRegistry::new();
|
||||
registry.register_builtin("FileBox", dummy_filebox_constructor);
|
||||
|
||||
|
||||
// プラグイン設定でビルトインを上書き
|
||||
let mut config = PluginConfig::default();
|
||||
config.plugins.insert("FileBox".to_string(), "filebox".to_string());
|
||||
config
|
||||
.plugins
|
||||
.insert("FileBox".to_string(), "filebox".to_string());
|
||||
registry.apply_plugin_config(&config);
|
||||
|
||||
// 生成までは行わず、プロバイダーがプラグインに切り替わったことを確認
|
||||
@ -56,55 +64,69 @@ StringBox = "custom_string"
|
||||
_ => panic!("Expected plugin provider for FileBox"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// TODO: PluginBox型が削除されたためこのテストはコメントアウト
|
||||
// #[test]
|
||||
// fn test_plugin_box_creation() {
|
||||
// let handle = BidHandle::new(BoxTypeId::FileBox as u32, 123);
|
||||
// let plugin_box = PluginBox::new("filebox".to_string(), handle);
|
||||
//
|
||||
//
|
||||
// assert_eq!(plugin_box.plugin_name(), "filebox");
|
||||
// assert_eq!(plugin_box.handle().type_id, BoxTypeId::FileBox as u32);
|
||||
// assert_eq!(plugin_box.handle().instance_id, 123);
|
||||
// }
|
||||
|
||||
|
||||
// 旧PluginBox直接生成テストは削除(v2統合により不要)
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_transparent_box_switching() {
|
||||
let registry = BoxFactoryRegistry::new();
|
||||
|
||||
|
||||
// 1. ビルトイン版を登録
|
||||
registry.register_builtin("FileBox", dummy_filebox_constructor);
|
||||
|
||||
|
||||
// 2. 現在のプロバイダーはビルトイン
|
||||
match registry.get_provider("FileBox").unwrap() {
|
||||
BoxProvider::Builtin(_) => {}
|
||||
_ => panic!("Expected builtin provider before plugin override"),
|
||||
}
|
||||
|
||||
|
||||
// 3. プラグイン設定を適用
|
||||
let mut config = PluginConfig::default();
|
||||
config.plugins.insert("FileBox".to_string(), "filebox".to_string());
|
||||
config
|
||||
.plugins
|
||||
.insert("FileBox".to_string(), "filebox".to_string());
|
||||
registry.apply_plugin_config(&config);
|
||||
|
||||
|
||||
// 4. プロバイダーがプラグインに切り替わっている
|
||||
match registry.get_provider("FileBox").unwrap() {
|
||||
BoxProvider::Plugin(name) => assert_eq!(name, "filebox"),
|
||||
_ => panic!("Expected plugin provider after override"),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[test]
|
||||
fn test_multiple_plugin_types() {
|
||||
let mut config = PluginConfig::default();
|
||||
config.plugins.insert("FileBox".to_string(), "filebox".to_string());
|
||||
config.plugins.insert("StringBox".to_string(), "custom_string".to_string());
|
||||
config.plugins.insert("MathBox".to_string(), "advanced_math".to_string());
|
||||
|
||||
config
|
||||
.plugins
|
||||
.insert("FileBox".to_string(), "filebox".to_string());
|
||||
config
|
||||
.plugins
|
||||
.insert("StringBox".to_string(), "custom_string".to_string());
|
||||
config
|
||||
.plugins
|
||||
.insert("MathBox".to_string(), "advanced_math".to_string());
|
||||
|
||||
assert_eq!(config.plugins.len(), 3);
|
||||
assert_eq!(config.plugins.get("FileBox"), Some(&"filebox".to_string()));
|
||||
assert_eq!(config.plugins.get("StringBox"), Some(&"custom_string".to_string()));
|
||||
assert_eq!(config.plugins.get("MathBox"), Some(&"advanced_math".to_string()));
|
||||
assert_eq!(
|
||||
config.plugins.get("StringBox"),
|
||||
Some(&"custom_string".to_string())
|
||||
);
|
||||
assert_eq!(
|
||||
config.plugins.get("MathBox"),
|
||||
Some(&"advanced_math".to_string())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -25,8 +25,18 @@ pub struct NyrtValue {
|
||||
}
|
||||
|
||||
impl NyrtValue {
|
||||
pub fn void() -> Self { Self { tag: NyrtTag::Void, i64_: 0 } }
|
||||
pub fn i64(v: i64) -> Self { Self { tag: NyrtTag::I64, i64_: v } }
|
||||
pub fn void() -> Self {
|
||||
Self {
|
||||
tag: NyrtTag::Void,
|
||||
i64_: 0,
|
||||
}
|
||||
}
|
||||
pub fn i64(v: i64) -> Self {
|
||||
Self {
|
||||
tag: NyrtTag::I64,
|
||||
i64_: v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Nyash ABI のメソッド関数ポインタ(雛形)
|
||||
@ -34,7 +44,11 @@ pub type NyrtMethodFn = fn(instance: u64, argc: usize, argv: *const NyrtValue) -
|
||||
|
||||
/// スロット定義(雛形)
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct MethodEntry { pub name: &'static str, pub arity: u8, pub slot: u16 }
|
||||
pub struct MethodEntry {
|
||||
pub name: &'static str,
|
||||
pub arity: u8,
|
||||
pub slot: u16,
|
||||
}
|
||||
|
||||
/// TypeBox(雛形): 各型の静的メタデータ(スロット一覧付き)
|
||||
pub struct TypeBox {
|
||||
@ -43,6 +57,13 @@ pub struct TypeBox {
|
||||
}
|
||||
|
||||
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 } }
|
||||
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 }
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,10 +31,25 @@ pub struct MethodThunk {
|
||||
}
|
||||
|
||||
impl MethodThunk {
|
||||
pub fn new() -> Self { Self { target: RwLock::new(None), _flags: RwLock::new(0) } }
|
||||
pub fn get_target(&self) -> Option<ThunkTarget> { self.target.read().ok().and_then(|g| g.clone()) }
|
||||
pub fn set_mir_target(&self, name: String) { if let Ok(mut g) = self.target.write() { *g = Some(ThunkTarget::MirFunction(name)); } }
|
||||
pub fn set_plugin_invoke(&self, method_id: u16) { if let Ok(mut g) = self.target.write() { *g = Some(ThunkTarget::PluginInvoke { method_id }); } }
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
target: RwLock::new(None),
|
||||
_flags: RwLock::new(0),
|
||||
}
|
||||
}
|
||||
pub fn get_target(&self) -> Option<ThunkTarget> {
|
||||
self.target.read().ok().and_then(|g| g.clone())
|
||||
}
|
||||
pub fn set_mir_target(&self, name: String) {
|
||||
if let Ok(mut g) = self.target.write() {
|
||||
*g = Some(ThunkTarget::MirFunction(name));
|
||||
}
|
||||
}
|
||||
pub fn set_plugin_invoke(&self, method_id: u16) {
|
||||
if let Ok(mut g) = self.target.write() {
|
||||
*g = Some(ThunkTarget::PluginInvoke { method_id });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Per-type metadata including thunk table
|
||||
@ -46,7 +61,10 @@ pub struct TypeMeta {
|
||||
|
||||
impl TypeMeta {
|
||||
fn new(class_name: String) -> Self {
|
||||
Self { class_name, thunks: RwLock::new(Vec::new()) }
|
||||
Self {
|
||||
class_name,
|
||||
thunks: RwLock::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the thunk table length is at least `len`.
|
||||
@ -54,7 +72,9 @@ impl TypeMeta {
|
||||
if let Ok(mut tbl) = self.thunks.write() {
|
||||
if tbl.len() < len {
|
||||
let to_add = len - tbl.len();
|
||||
for _ in 0..to_add { tbl.push(Arc::new(MethodThunk::new())); }
|
||||
for _ in 0..to_add {
|
||||
tbl.push(Arc::new(MethodThunk::new()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -68,12 +88,16 @@ impl TypeMeta {
|
||||
/// Set thunk target name for slot
|
||||
pub fn set_thunk_mir_target(&self, slot: usize, target_name: String) {
|
||||
self.ensure_len(slot + 1);
|
||||
if let Some(th) = self.get_thunk(slot) { th.set_mir_target(target_name); }
|
||||
if let Some(th) = self.get_thunk(slot) {
|
||||
th.set_mir_target(target_name);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_thunk_plugin_invoke(&self, slot: usize, method_id: u16) {
|
||||
self.ensure_len(slot + 1);
|
||||
if let Some(th) = self.get_thunk(slot) { th.set_plugin_invoke(method_id); }
|
||||
if let Some(th) = self.get_thunk(slot) {
|
||||
th.set_plugin_invoke(method_id);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_thunk_builtin(&self, slot: usize, method: String) {
|
||||
@ -91,12 +115,15 @@ impl TypeMeta {
|
||||
}
|
||||
}
|
||||
|
||||
static TYPE_META_REGISTRY: Lazy<Mutex<HashMap<String, Arc<TypeMeta>>>> = Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
static TYPE_META_REGISTRY: Lazy<Mutex<HashMap<String, Arc<TypeMeta>>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
/// Get or create TypeMeta for a given class name
|
||||
pub fn get_or_create_type_meta(class_name: &str) -> Arc<TypeMeta> {
|
||||
let mut map = TYPE_META_REGISTRY.lock().unwrap();
|
||||
if let Some(m) = map.get(class_name) { return m.clone(); }
|
||||
if let Some(m) = map.get(class_name) {
|
||||
return m.clone();
|
||||
}
|
||||
let m = Arc::new(TypeMeta::new(class_name.to_string()));
|
||||
map.insert(class_name.to_string(), m.clone());
|
||||
m
|
||||
|
||||
@ -19,67 +19,207 @@
|
||||
* - 400..: Console 系(log/warn/error/clear)
|
||||
*/
|
||||
|
||||
use super::type_box_abi::{TypeBox, MethodEntry};
|
||||
use super::type_box_abi::{MethodEntry, TypeBox};
|
||||
|
||||
// 最小サンプル: 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 },
|
||||
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,
|
||||
},
|
||||
// P0: vtable coverage extension
|
||||
MethodEntry { name: "push", arity: 1, slot: 103 },
|
||||
MethodEntry { name: "pop", arity: 0, slot: 104 },
|
||||
MethodEntry { name: "clear", arity: 0, slot: 105 },
|
||||
MethodEntry {
|
||||
name: "push",
|
||||
arity: 1,
|
||||
slot: 103,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "pop",
|
||||
arity: 0,
|
||||
slot: 104,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "clear",
|
||||
arity: 0,
|
||||
slot: 105,
|
||||
},
|
||||
// P1: contains/indexOf/join
|
||||
MethodEntry { name: "contains", arity: 1, slot: 106 },
|
||||
MethodEntry { name: "indexOf", arity: 1, slot: 107 },
|
||||
MethodEntry { name: "join", arity: 1, slot: 108 },
|
||||
MethodEntry {
|
||||
name: "contains",
|
||||
arity: 1,
|
||||
slot: 106,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "indexOf",
|
||||
arity: 1,
|
||||
slot: 107,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "join",
|
||||
arity: 1,
|
||||
slot: 108,
|
||||
},
|
||||
// P2: sort/reverse/slice
|
||||
MethodEntry { name: "sort", arity: 0, slot: 109 },
|
||||
MethodEntry { name: "reverse", arity: 0, slot: 110 },
|
||||
MethodEntry { name: "slice", arity: 2, slot: 111 },
|
||||
MethodEntry {
|
||||
name: "sort",
|
||||
arity: 0,
|
||||
slot: 109,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "reverse",
|
||||
arity: 0,
|
||||
slot: 110,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "slice",
|
||||
arity: 2,
|
||||
slot: 111,
|
||||
},
|
||||
];
|
||||
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 },
|
||||
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,
|
||||
},
|
||||
// Extended
|
||||
MethodEntry { name: "delete", arity: 1, slot: 205 }, // alias: remove (同一スロット)
|
||||
MethodEntry { name: "remove", arity: 1, slot: 205 },
|
||||
MethodEntry { name: "keys", arity: 0, slot: 206 },
|
||||
MethodEntry { name: "values", arity: 0, slot: 207 },
|
||||
MethodEntry { name: "clear", arity: 0, slot: 208 },
|
||||
MethodEntry {
|
||||
name: "delete",
|
||||
arity: 1,
|
||||
slot: 205,
|
||||
}, // alias: remove (同一スロット)
|
||||
MethodEntry {
|
||||
name: "remove",
|
||||
arity: 1,
|
||||
slot: 205,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "keys",
|
||||
arity: 0,
|
||||
slot: 206,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "values",
|
||||
arity: 0,
|
||||
slot: 207,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "clear",
|
||||
arity: 0,
|
||||
slot: 208,
|
||||
},
|
||||
];
|
||||
static MAPBOX_TB: TypeBox = TypeBox::new_with("MapBox", MAP_METHODS);
|
||||
|
||||
// --- StringBox ---
|
||||
const STRING_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "len", arity: 0, slot: 300 },
|
||||
MethodEntry {
|
||||
name: "len",
|
||||
arity: 0,
|
||||
slot: 300,
|
||||
},
|
||||
// P1: extend String vtable
|
||||
MethodEntry { name: "substring", arity: 2, slot: 301 },
|
||||
MethodEntry { name: "concat", arity: 1, slot: 302 },
|
||||
MethodEntry { name: "indexOf", arity: 1, slot: 303 },
|
||||
MethodEntry { name: "replace", arity: 2, slot: 304 },
|
||||
MethodEntry { name: "trim", arity: 0, slot: 305 },
|
||||
MethodEntry { name: "toUpper", arity: 0, slot: 306 },
|
||||
MethodEntry { name: "toLower", arity: 0, slot: 307 },
|
||||
MethodEntry {
|
||||
name: "substring",
|
||||
arity: 2,
|
||||
slot: 301,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "concat",
|
||||
arity: 1,
|
||||
slot: 302,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "indexOf",
|
||||
arity: 1,
|
||||
slot: 303,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "replace",
|
||||
arity: 2,
|
||||
slot: 304,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "trim",
|
||||
arity: 0,
|
||||
slot: 305,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "toUpper",
|
||||
arity: 0,
|
||||
slot: 306,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "toLower",
|
||||
arity: 0,
|
||||
slot: 307,
|
||||
},
|
||||
];
|
||||
static STRINGBOX_TB: TypeBox = TypeBox::new_with("StringBox", STRING_METHODS);
|
||||
|
||||
// --- ConsoleBox --- (WASM v2 unified dispatch 用の雛形)
|
||||
// 400: log(..), 401: warn(..), 402: error(..), 403: clear()
|
||||
const CONSOLE_METHODS: &[MethodEntry] = &[
|
||||
MethodEntry { name: "log", arity: 1, slot: 400 },
|
||||
MethodEntry { name: "warn", arity: 1, slot: 401 },
|
||||
MethodEntry { name: "error", arity: 1, slot: 402 },
|
||||
MethodEntry { name: "clear", arity: 0, slot: 403 },
|
||||
MethodEntry {
|
||||
name: "log",
|
||||
arity: 1,
|
||||
slot: 400,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "warn",
|
||||
arity: 1,
|
||||
slot: 401,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "error",
|
||||
arity: 1,
|
||||
slot: 402,
|
||||
},
|
||||
MethodEntry {
|
||||
name: "clear",
|
||||
arity: 0,
|
||||
slot: 403,
|
||||
},
|
||||
];
|
||||
static CONSOLEBOX_TB: TypeBox = TypeBox::new_with("ConsoleBox", CONSOLE_METHODS);
|
||||
|
||||
@ -90,10 +230,26 @@ static CONSOLEBOX_TB: TypeBox = TypeBox::new_with("ConsoleBox", CONSOLE_METHODS)
|
||||
// 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 },
|
||||
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);
|
||||
|
||||
@ -114,7 +270,9 @@ pub fn resolve_slot_by_name(type_name: &str, method: &str, arity: usize) -> Opti
|
||||
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); }
|
||||
if m.name == method && m.arity == ar {
|
||||
return Some(m.slot);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
/*!
|
||||
* Global Unified Box Registry
|
||||
*
|
||||
*
|
||||
* Manages the global instance of UnifiedBoxRegistry
|
||||
* Integrates all Box creation sources (builtin, user-defined, plugin)
|
||||
*/
|
||||
|
||||
use crate::box_factory::UnifiedBoxRegistry;
|
||||
use crate::box_factory::builtin::BuiltinBoxFactory;
|
||||
#[cfg(feature = "plugins")]
|
||||
use crate::box_factory::plugin::PluginBoxFactory;
|
||||
use crate::box_factory::UnifiedBoxRegistry;
|
||||
use std::sync::{Arc, Mutex, OnceLock};
|
||||
|
||||
/// Global registry instance
|
||||
@ -23,15 +23,15 @@ pub fn init_global_unified_registry() {
|
||||
{
|
||||
registry.register(std::sync::Arc::new(BuiltinBoxFactory::new()));
|
||||
}
|
||||
|
||||
|
||||
// Register plugin Box factory (primary)
|
||||
#[cfg(feature = "plugins")]
|
||||
{
|
||||
registry.register(Arc::new(PluginBoxFactory::new()));
|
||||
}
|
||||
|
||||
|
||||
// TODO: User-defined Box factory will be registered by interpreter
|
||||
|
||||
|
||||
Arc::new(Mutex::new(registry))
|
||||
});
|
||||
}
|
||||
@ -46,7 +46,7 @@ pub fn get_global_unified_registry() -> Arc<Mutex<UnifiedBoxRegistry>> {
|
||||
pub fn register_user_defined_factory(factory: Arc<dyn crate::box_factory::BoxFactory>) {
|
||||
let registry = get_global_unified_registry();
|
||||
let mut registry_lock = registry.lock().unwrap();
|
||||
|
||||
|
||||
// Insert at position 1 (after builtin, before plugin)
|
||||
// This maintains priority: builtin > user > plugin
|
||||
if registry_lock.factories.len() >= 2 {
|
||||
|
||||
Reference in New Issue
Block a user