- Eliminate static_mut_refs warnings by using once_cell::sync::Lazy - Make FileMode enum public to fix private_interfaces warning - Reduce warnings from 6 to 3 - Prepare for Rust 2024 edition compatibility
134 lines
4.8 KiB
Rust
134 lines
4.8 KiB
Rust
use crate::bid::{BidError, BidResult, NyashHostVtable, NyashPluginInfo, PluginHandle, PLUGIN_ABI_SYMBOL, PLUGIN_INIT_SYMBOL, PLUGIN_INVOKE_SYMBOL, PLUGIN_SHUTDOWN_SYMBOL};
|
|
#[cfg(all(feature = "plugins", not(target_arch = "wasm32")))]
|
|
use libloading::{Library, Symbol};
|
|
use std::path::{Path, PathBuf};
|
|
|
|
/// Loaded plugin with FFI entry points and metadata
|
|
pub struct LoadedPlugin {
|
|
pub library: Library,
|
|
pub handle: PluginHandle,
|
|
pub type_id: u32,
|
|
pub plugin_info: NyashPluginInfo, // プラグイン情報を保存
|
|
}
|
|
|
|
impl LoadedPlugin {
|
|
/// Load a plugin dynamic library from file path and initialize it
|
|
pub fn load_from_file(path: &Path) -> BidResult<Self> {
|
|
// Load library
|
|
let library = unsafe { Library::new(path) }
|
|
.map_err(|_| BidError::PluginError)?;
|
|
|
|
// Resolve symbols
|
|
unsafe {
|
|
let abi: Symbol<unsafe extern "C" fn() -> u32> = library
|
|
.get(PLUGIN_ABI_SYMBOL.as_bytes())
|
|
.map_err(|_| BidError::PluginError)?;
|
|
let init: Symbol<unsafe extern "C" fn(*const NyashHostVtable, *mut NyashPluginInfo) -> i32> = library
|
|
.get(PLUGIN_INIT_SYMBOL.as_bytes())
|
|
.map_err(|_| BidError::PluginError)?;
|
|
let invoke: Symbol<unsafe extern "C" fn(u32, u32, u32, *const u8, usize, *mut u8, *mut usize) -> i32> = library
|
|
.get(PLUGIN_INVOKE_SYMBOL.as_bytes())
|
|
.map_err(|_| BidError::PluginError)?;
|
|
let shutdown: Symbol<unsafe extern "C" fn()> = library
|
|
.get(PLUGIN_SHUTDOWN_SYMBOL.as_bytes())
|
|
.map_err(|_| BidError::PluginError)?;
|
|
|
|
let handle = PluginHandle {
|
|
abi: *abi,
|
|
init: *init,
|
|
invoke: *invoke,
|
|
shutdown: *shutdown,
|
|
};
|
|
|
|
// ABI check
|
|
handle.check_abi()?;
|
|
|
|
// Initialize plugin
|
|
let host = default_host_vtable();
|
|
let mut info = NyashPluginInfo::empty();
|
|
handle.initialize(&host, &mut info)?;
|
|
let type_id = info.type_id;
|
|
|
|
Ok(Self { library, handle, type_id, plugin_info: info })
|
|
}
|
|
}
|
|
|
|
/// Get the plugin's Box type name
|
|
pub fn get_type_name(&self) -> BidResult<&str> {
|
|
unsafe { self.plugin_info.name() }
|
|
}
|
|
|
|
/// Get all available methods for this plugin
|
|
pub fn get_methods(&self) -> BidResult<Vec<(u32, String, u32)>> {
|
|
let mut methods = Vec::new();
|
|
|
|
unsafe {
|
|
let methods_slice = self.plugin_info.methods_slice()?;
|
|
for method_info in methods_slice {
|
|
let method_name = method_info.name()?.to_string();
|
|
methods.push((
|
|
method_info.method_id,
|
|
method_name,
|
|
method_info.signature_hash,
|
|
));
|
|
}
|
|
}
|
|
|
|
Ok(methods)
|
|
}
|
|
|
|
/// Find a method by name and return its info
|
|
pub fn find_method(&self, method_name: &str) -> BidResult<Option<(u32, u32)>> {
|
|
unsafe {
|
|
let methods_slice = self.plugin_info.methods_slice()?;
|
|
for method_info in methods_slice {
|
|
if method_info.name()? == method_name {
|
|
return Ok(Some((method_info.method_id, method_info.signature_hash)));
|
|
}
|
|
}
|
|
}
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
// Static host vtable to ensure lifetime
|
|
static HOST_VTABLE_STORAGE: std::sync::LazyLock<NyashHostVtable> = std::sync::LazyLock::new(|| {
|
|
unsafe extern "C" fn host_alloc(size: usize) -> *mut u8 {
|
|
let layout = std::alloc::Layout::from_size_align(size, 8).unwrap();
|
|
std::alloc::alloc(layout)
|
|
}
|
|
unsafe extern "C" fn host_free(_ptr: *mut u8) {
|
|
// In this prototype we cannot deallocate without size. No-op.
|
|
}
|
|
unsafe extern "C" fn host_wake(_id: u64) {}
|
|
unsafe extern "C" fn host_log(_level: i32, _msg: *const i8) {
|
|
// Debug output for plugin logs
|
|
if !_msg.is_null() {
|
|
let msg = unsafe { std::ffi::CStr::from_ptr(_msg) };
|
|
if let Ok(s) = msg.to_str() {
|
|
eprintln!("🔌 Plugin log [{}]: {}", _level, s);
|
|
}
|
|
}
|
|
}
|
|
|
|
NyashHostVtable { alloc: host_alloc, free: host_free, wake: host_wake, log: host_log }
|
|
});
|
|
|
|
/// Build a minimal host vtable for plugins
|
|
fn default_host_vtable() -> &'static NyashHostVtable {
|
|
&*HOST_VTABLE_STORAGE
|
|
}
|
|
|
|
/// Helper: find plugin file path by name and candidate directories
|
|
pub fn resolve_plugin_path(plugin_name: &str, candidates: &[PathBuf]) -> Option<PathBuf> {
|
|
// Expected filenames by platform (Linux only for now)
|
|
let file = format!("lib{}{}.so", plugin_name.replace('-', "_"), "");
|
|
for dir in candidates {
|
|
let candidate = dir.join(&file);
|
|
if candidate.exists() {
|
|
return Some(candidate);
|
|
}
|
|
}
|
|
None
|
|
}
|