feat: Implement plugin singleton pattern with shutdown support
- Add singleton support for plugin boxes (e.g., CounterBox) - Implement shutdown_plugins_v2() for controlled plugin lifecycle - Plugin instances now shared across multiple new() calls - Shutdown properly releases and allows re-initialization - All singleton E2E tests passing ✅ ChatGPT5による高度なプラグインライフサイクル管理実装 - シングルトンパターンでプラグインインスタンス共有 - 明示的なshutdownでリソース解放と再初期化対応 - Nyashの統一ライフサイクルポリシー維持 Note: ast.rs test failures are due to rapid development pace - tests need updating for new BoxDeclaration fields (private_fields, public_fields) 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@ -575,7 +575,14 @@ impl NyashInterpreter {
|
||||
/// local変数スタックを保存・復元(関数呼び出し時)
|
||||
pub(super) fn save_local_vars(&self) -> HashMap<String, Box<dyn NyashBox>> {
|
||||
self.local_vars.iter()
|
||||
.map(|(k, v)| (k.clone(), (**v).clone_box())) // Deref Arc to get the Box
|
||||
.map(|(k, v)| {
|
||||
let b: &dyn NyashBox = &**v;
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
return (k.clone(), b.share_box());
|
||||
}
|
||||
(k.clone(), b.clone_box())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -593,12 +600,7 @@ impl NyashInterpreter {
|
||||
let _ = instance.fini();
|
||||
eprintln!("🔄 Scope exit: Called fini() on local variable '{}' (InstanceBox)", name);
|
||||
}
|
||||
// プラグインBoxの場合
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if let Some(plugin) = (**value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
plugin.call_fini();
|
||||
eprintln!("🔄 Scope exit: Called fini() on local variable '{}' (PluginBox)", name);
|
||||
}
|
||||
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない(明示呼び出しのみ)
|
||||
// ビルトインBoxは元々finiメソッドを持たないので呼ばない
|
||||
// (StringBox、IntegerBox等はリソース管理不要)
|
||||
}
|
||||
@ -612,7 +614,14 @@ impl NyashInterpreter {
|
||||
/// outbox変数スタックを保存・復元(static関数呼び出し時)
|
||||
pub(super) fn save_outbox_vars(&self) -> HashMap<String, Box<dyn NyashBox>> {
|
||||
self.outbox_vars.iter()
|
||||
.map(|(k, v)| (k.clone(), (**v).clone_box())) // Deref Arc to get the Box
|
||||
.map(|(k, v)| {
|
||||
let b: &dyn NyashBox = &**v;
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if b.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>().is_some() {
|
||||
return (k.clone(), b.share_box());
|
||||
}
|
||||
(k.clone(), b.clone_box())
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
@ -624,12 +633,7 @@ impl NyashInterpreter {
|
||||
let _ = instance.fini();
|
||||
eprintln!("🔄 Scope exit: Called fini() on outbox variable '{}' (InstanceBox)", name);
|
||||
}
|
||||
// プラグインBoxの場合
|
||||
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
||||
if let Some(plugin) = (**value).as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
plugin.call_fini();
|
||||
eprintln!("🔄 Scope exit: Called fini() on outbox variable '{}' (PluginBox)", name);
|
||||
}
|
||||
// プラグインBoxは共有ハンドルの可能性が高いため自動finiしない
|
||||
// ビルトインBoxは元々finiメソッドを持たないので呼ばない(要修正)
|
||||
}
|
||||
|
||||
|
||||
@ -692,7 +692,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
let loader = crate::runtime::get_global_loader_v2();
|
||||
let loader = loader.read().unwrap();
|
||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id, &arg_values) {
|
||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) {
|
||||
Ok(Some(result_box)) => return Ok(result_box),
|
||||
Ok(None) => return Ok(Box::new(VoidBox::new())),
|
||||
Err(_) => {}
|
||||
@ -782,7 +782,7 @@ impl NyashInterpreter {
|
||||
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||
for arg in arguments { arg_values.push(self.execute_expression(arg)?); }
|
||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id, &arg_values) {
|
||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) {
|
||||
Ok(Some(result_box)) => return Ok(result_box),
|
||||
Ok(None) => return Ok(Box::new(crate::box_trait::VoidBox::new())),
|
||||
Err(e) => {
|
||||
@ -818,23 +818,23 @@ impl NyashInterpreter {
|
||||
{
|
||||
// 親がユーザー定義に見つからない場合は、プラグインとして試行
|
||||
// 現在のインスタンスから __plugin_content を参照
|
||||
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
|
||||
// 引数を評価(ロックは既に解放済みの設計)
|
||||
let plugin_ref = &*plugin_shared;
|
||||
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||
for arg in arguments {
|
||||
arg_values.push(self.execute_expression(arg)?);
|
||||
if let Some(plugin_shared) = current_instance.get_field_legacy("__plugin_content") {
|
||||
// 引数を評価(ロックは既に解放済みの設計)
|
||||
let plugin_ref = &*plugin_shared;
|
||||
if let Some(plugin) = plugin_ref.as_any().downcast_ref::<crate::runtime::plugin_loader_v2::PluginBoxV2>() {
|
||||
let mut arg_values: Vec<Box<dyn NyashBox>> = Vec::new();
|
||||
for arg in arguments {
|
||||
arg_values.push(self.execute_expression(arg)?);
|
||||
}
|
||||
let loader = crate::runtime::get_global_loader_v2();
|
||||
let loader = loader.read().unwrap();
|
||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id(), &arg_values) {
|
||||
Ok(Some(result_box)) => return Ok(result_box),
|
||||
Ok(None) => return Ok(Box::new(VoidBox::new())),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
let loader = crate::runtime::get_global_loader_v2();
|
||||
let loader = loader.read().unwrap();
|
||||
match loader.invoke_instance_method(&plugin.box_type, method, plugin.instance_id, &arg_values) {
|
||||
Ok(Some(result_box)) => return Ok(result_box),
|
||||
Ok(None) => return Ok(Box::new(VoidBox::new())),
|
||||
Err(_) => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 親クラスのBox宣言を取得(ユーザー定義Boxの場合)
|
||||
@ -1003,7 +1003,7 @@ impl NyashInterpreter {
|
||||
}
|
||||
let loader_guard = crate::runtime::plugin_loader_v2::get_global_loader_v2();
|
||||
let loader = loader_guard.read().map_err(|_| RuntimeError::RuntimeFailure { message: "Plugin loader lock poisoned".into() })?;
|
||||
match loader.invoke_instance_method(&plugin_box.box_type, method, plugin_box.instance_id, &arg_values) {
|
||||
match loader.invoke_instance_method(&plugin_box.box_type, method, plugin_box.instance_id(), &arg_values) {
|
||||
Ok(Some(result_box)) => Ok(result_box),
|
||||
Ok(None) => Ok(Box::new(VoidBox::new())),
|
||||
Err(e) => Err(RuntimeError::RuntimeFailure { message: format!("Plugin method {} failed: {:?}", method, e) }),
|
||||
|
||||
Reference in New Issue
Block a user