feat: Implement nyash.toml v2 plugin system with PluginLoaderV2
- Add plugin_loader_v2.rs with nyash.toml v2 support - Create PluginBoxV2 as temporary wrapper for v2 plugins - Update runner.rs to use v2 loader instead of legacy BID registry - Update box_registry.rs to work with v2 plugin system - Support single FFI entry point (nyash_plugin_invoke) + optional init - Integrate with existing BoxFactoryRegistry for seamless Box creation - All compilation errors resolved, ready for testing
This commit is contained in:
@ -20,8 +20,8 @@ use crate::{
|
|||||||
use crate::backend::{llvm_compile_and_execute};
|
use crate::backend::{llvm_compile_and_execute};
|
||||||
use std::{fs, process};
|
use std::{fs, process};
|
||||||
|
|
||||||
// BID prototype imports
|
// v2 plugin system imports
|
||||||
use crate::bid::{PluginRegistry, PluginBoxInstance};
|
use crate::runtime::init_global_loader_v2;
|
||||||
|
|
||||||
/// Main execution coordinator
|
/// Main execution coordinator
|
||||||
pub struct NyashRunner {
|
pub struct NyashRunner {
|
||||||
@ -58,18 +58,34 @@ impl NyashRunner {
|
|||||||
|
|
||||||
fn init_bid_plugins(&self) {
|
fn init_bid_plugins(&self) {
|
||||||
// Best-effort init; do not fail the program if missing
|
// Best-effort init; do not fail the program if missing
|
||||||
eprintln!("🔍 DEBUG: init_bid_plugins called");
|
eprintln!("🔍 DEBUG: Initializing v2 plugin system");
|
||||||
if let Ok(()) = crate::bid::registry::init_global_from_config("nyash.toml") {
|
|
||||||
let reg = crate::bid::registry::global().unwrap();
|
// Try to load nyash.toml configuration
|
||||||
// If FileBox plugin is present, try a birth/fini cycle as a smoke test
|
if let Ok(()) = init_global_loader_v2("nyash.toml") {
|
||||||
if let Some(plugin) = reg.get_by_name("FileBox") {
|
println!("🔌 v2 plugin system initialized from nyash.toml");
|
||||||
if let Ok(inst) = PluginBoxInstance::birth(plugin) {
|
|
||||||
println!("🔌 BID plugin loaded: FileBox (instance_id={})", inst.instance_id);
|
// Apply plugin configuration to the box registry
|
||||||
// Drop will call fini
|
use crate::runtime::{get_global_registry, get_global_loader_v2};
|
||||||
return;
|
|
||||||
|
let loader = get_global_loader_v2();
|
||||||
|
let loader = loader.read().unwrap();
|
||||||
|
|
||||||
|
if let Some(config) = &loader.config {
|
||||||
|
// Register plugin providers in the box registry
|
||||||
|
let registry = get_global_registry();
|
||||||
|
|
||||||
|
for (lib_name, lib_def) in &config.libraries {
|
||||||
|
for box_name in &lib_def.boxes {
|
||||||
|
eprintln!(" 📦 Registering plugin provider for {}", box_name);
|
||||||
|
// Note: plugin_name is lib_name in v2 system
|
||||||
|
registry.apply_plugin_config(&crate::runtime::PluginConfig {
|
||||||
|
plugins: [(box_name.clone(), lib_name.clone())].into(),
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
println!("🔌 BID registry initialized");
|
}
|
||||||
|
} else {
|
||||||
|
eprintln!("⚠️ Failed to load nyash.toml - plugins disabled");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -75,11 +75,17 @@ impl BoxFactoryRegistry {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// プラグインBoxを生成(v2への移行中の一時的スタブ)
|
/// プラグインBoxを生成(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> {
|
||||||
// TODO: v2プラグインシステムへの実装
|
use crate::runtime::get_global_loader_v2;
|
||||||
// 現在は一時的にエラーを返す
|
|
||||||
Err(format!("Plugin system v2 migration in progress. Cannot create {} from plugin {}", box_name, plugin_name))
|
// v2ローダーを取得
|
||||||
|
let loader = get_global_loader_v2();
|
||||||
|
let loader = loader.read().unwrap();
|
||||||
|
|
||||||
|
// プラグインからBoxを生成
|
||||||
|
loader.create_box(box_name, args)
|
||||||
|
.map_err(|e| format!("Failed to create {} from plugin {}: {:?}", box_name, plugin_name, e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
pub mod plugin_config;
|
pub mod plugin_config;
|
||||||
pub mod box_registry;
|
pub mod box_registry;
|
||||||
|
pub mod plugin_loader_v2;
|
||||||
// pub mod plugin_box; // legacy - 古いPluginBox
|
// pub mod plugin_box; // legacy - 古いPluginBox
|
||||||
// pub mod plugin_loader; // legacy - Host VTable使用
|
// pub mod plugin_loader; // legacy - Host VTable使用
|
||||||
|
|
||||||
@ -12,6 +13,7 @@ mod tests;
|
|||||||
|
|
||||||
pub use plugin_config::PluginConfig;
|
pub use plugin_config::PluginConfig;
|
||||||
pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry};
|
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_box::PluginBox; // legacy
|
// pub use plugin_box::PluginBox; // legacy
|
||||||
// Use unified plugin loader (formerly v2)
|
// Use unified plugin loader (formerly v2)
|
||||||
// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy
|
// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy
|
||||||
238
src/runtime/plugin_loader_v2.rs
Normal file
238
src/runtime/plugin_loader_v2.rs
Normal file
@ -0,0 +1,238 @@
|
|||||||
|
//! Nyash v2 Plugin Loader
|
||||||
|
//!
|
||||||
|
//! nyash.toml v2ベースの新しいプラグインローダー
|
||||||
|
//! Single FFI entry point (nyash_plugin_invoke) + optional init
|
||||||
|
|
||||||
|
use crate::bid::{BidResult, BidError};
|
||||||
|
use crate::box_trait::{NyashBox, BoxCore, BoxBase, StringBox};
|
||||||
|
use crate::config::nyash_toml_v2::{NyashConfigV2, LibraryDefinition};
|
||||||
|
use std::collections::HashMap;
|
||||||
|
use std::sync::{Arc, RwLock};
|
||||||
|
use std::ffi::c_void;
|
||||||
|
use std::any::Any;
|
||||||
|
|
||||||
|
/// Loaded plugin information
|
||||||
|
pub struct LoadedPluginV2 {
|
||||||
|
/// Library handle
|
||||||
|
_lib: Arc<libloading::Library>,
|
||||||
|
|
||||||
|
/// Box types provided by this plugin
|
||||||
|
box_types: Vec<String>,
|
||||||
|
|
||||||
|
/// Optional init function
|
||||||
|
init_fn: Option<unsafe extern "C" fn() -> i32>,
|
||||||
|
|
||||||
|
/// Required invoke function
|
||||||
|
invoke_fn: unsafe extern "C" fn(u32, u32, *const u8, u32, *mut u8, u32) -> i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// v2 Plugin Box wrapper - temporary implementation
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct PluginBoxV2 {
|
||||||
|
box_type: String,
|
||||||
|
invoke_fn: unsafe extern "C" fn(u32, u32, *const u8, u32, *mut u8, u32) -> i32,
|
||||||
|
instance_id: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BoxCore for PluginBoxV2 {
|
||||||
|
fn box_id(&self) -> u64 {
|
||||||
|
self.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.instance_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any(&self) -> &dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NyashBox for PluginBoxV2 {
|
||||||
|
fn type_name(&self) -> &'static str {
|
||||||
|
"PluginBoxV2"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone_box(&self) -> Box<dyn NyashBox> {
|
||||||
|
Box::new(StringBox::new(format!("Cannot clone plugin box {}", self.box_type)))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_string_box(&self) -> crate::box_trait::StringBox {
|
||||||
|
StringBox::new(format!("{}({})", self.box_type, self.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(StringBox::new(format!("Cannot share plugin box {}", self.box_type)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Plugin loader v2
|
||||||
|
pub struct PluginLoaderV2 {
|
||||||
|
/// Loaded plugins (library name -> plugin info)
|
||||||
|
plugins: RwLock<HashMap<String, Arc<LoadedPluginV2>>>,
|
||||||
|
|
||||||
|
/// Configuration
|
||||||
|
pub config: Option<NyashConfigV2>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PluginLoaderV2 {
|
||||||
|
/// Create new loader
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
plugins: RwLock::new(HashMap::new()),
|
||||||
|
config: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load configuration from nyash.toml
|
||||||
|
pub fn load_config(&mut self, config_path: &str) -> BidResult<()> {
|
||||||
|
self.config = Some(NyashConfigV2::from_file(config_path)
|
||||||
|
.map_err(|e| {
|
||||||
|
eprintln!("Failed to load config: {}", e);
|
||||||
|
BidError::PluginError
|
||||||
|
})?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load all plugins from config
|
||||||
|
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 {
|
||||||
|
if let Err(e) = self.load_plugin(lib_name, lib_def) {
|
||||||
|
eprintln!("Warning: Failed to load plugin {}: {:?}", lib_name, e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Load single plugin
|
||||||
|
pub fn load_plugin(&self, lib_name: &str, lib_def: &LibraryDefinition) -> BidResult<()> {
|
||||||
|
// Check if already loaded
|
||||||
|
{
|
||||||
|
let plugins = self.plugins.read().unwrap();
|
||||||
|
if plugins.contains_key(lib_name) {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load library
|
||||||
|
let lib = unsafe {
|
||||||
|
libloading::Library::new(&lib_def.path)
|
||||||
|
.map_err(|e| {
|
||||||
|
eprintln!("Failed to load library: {}", e);
|
||||||
|
BidError::PluginError
|
||||||
|
})?
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get required invoke function and dereference it
|
||||||
|
let invoke_fn = unsafe {
|
||||||
|
let symbol: libloading::Symbol<unsafe extern "C" fn(u32, u32, *const u8, u32, *mut u8, u32) -> i32> =
|
||||||
|
lib.get(b"nyash_plugin_invoke")
|
||||||
|
.map_err(|e| {
|
||||||
|
eprintln!("Missing nyash_plugin_invoke: {}", e);
|
||||||
|
BidError::InvalidMethod
|
||||||
|
})?;
|
||||||
|
*symbol // Dereference to get the actual function pointer
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get optional init function and dereference it
|
||||||
|
let init_fn = unsafe {
|
||||||
|
lib.get::<unsafe extern "C" fn() -> i32>(b"nyash_plugin_init").ok()
|
||||||
|
.map(|f| *f)
|
||||||
|
};
|
||||||
|
|
||||||
|
// Call init if available
|
||||||
|
if let Some(init) = init_fn {
|
||||||
|
let result = unsafe { init() };
|
||||||
|
if result != 0 {
|
||||||
|
eprintln!("Plugin init failed with code: {}", result);
|
||||||
|
return Err(BidError::PluginError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store plugin with Arc-wrapped library
|
||||||
|
let lib_arc = Arc::new(lib);
|
||||||
|
let plugin = Arc::new(LoadedPluginV2 {
|
||||||
|
_lib: lib_arc,
|
||||||
|
box_types: lib_def.boxes.clone(),
|
||||||
|
init_fn,
|
||||||
|
invoke_fn,
|
||||||
|
});
|
||||||
|
|
||||||
|
let mut plugins = self.plugins.write().unwrap();
|
||||||
|
plugins.insert(lib_name.to_string(), plugin);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a Box instance
|
||||||
|
pub fn create_box(&self, box_type: &str, args: &[Box<dyn NyashBox>]) -> BidResult<Box<dyn NyashBox>> {
|
||||||
|
let config = self.config.as_ref()
|
||||||
|
.ok_or(BidError::PluginError)?;
|
||||||
|
|
||||||
|
// Find library that provides this box type
|
||||||
|
let (lib_name, _lib_def) = config.find_library_for_box(box_type)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
eprintln!("No plugin provides box type: {}", box_type);
|
||||||
|
BidError::InvalidType
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Get loaded plugin
|
||||||
|
let plugins = self.plugins.read().unwrap();
|
||||||
|
let plugin = plugins.get(lib_name)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
eprintln!("Plugin not loaded: {}", lib_name);
|
||||||
|
BidError::PluginError
|
||||||
|
})?;
|
||||||
|
|
||||||
|
// Create v2 plugin box wrapper
|
||||||
|
let plugin_box = PluginBoxV2 {
|
||||||
|
box_type: box_type.to_string(),
|
||||||
|
invoke_fn: plugin.invoke_fn,
|
||||||
|
instance_id: 0, // Will be set after birth call
|
||||||
|
};
|
||||||
|
|
||||||
|
// TODO: Call birth constructor with args via TLV encoding
|
||||||
|
|
||||||
|
Ok(Box::new(plugin_box))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global loader instance
|
||||||
|
use once_cell::sync::Lazy;
|
||||||
|
|
||||||
|
static GLOBAL_LOADER_V2: Lazy<Arc<RwLock<PluginLoaderV2>>> =
|
||||||
|
Lazy::new(|| Arc::new(RwLock::new(PluginLoaderV2::new())));
|
||||||
|
|
||||||
|
/// Get global v2 loader
|
||||||
|
pub fn get_global_loader_v2() -> Arc<RwLock<PluginLoaderV2>> {
|
||||||
|
GLOBAL_LOADER_V2.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Initialize global loader with config
|
||||||
|
pub fn init_global_loader_v2(config_path: &str) -> BidResult<()> {
|
||||||
|
let loader = get_global_loader_v2();
|
||||||
|
let mut loader = loader.write().unwrap();
|
||||||
|
loader.load_config(config_path)?;
|
||||||
|
drop(loader); // Release write lock
|
||||||
|
|
||||||
|
// Load all plugins
|
||||||
|
let loader = get_global_loader_v2();
|
||||||
|
let loader = loader.read().unwrap();
|
||||||
|
loader.load_all_plugins()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user