use super::shared::{self, HandlerShim, PluginCache};
use interoptopus::lang::plugin::{Plugin as PluginTrait, PluginLoadError};
use interoptopus::trampoline::{TRAMPOLINE_UNCAUGHT_EXCEPTION, TRAMPOLINE_UNCAUGHT_EXCEPTION_CTX};
use std::path::Path;
use std::sync::{Arc, Mutex, OnceLock};
struct Inner {
plugins: PluginCache,
libraries: Vec<libloading::Library>,
}
pub struct AotRuntime {
inner: Mutex<Inner>,
exception_handler: OnceLock<Arc<dyn Fn(String) + Send + Sync>>,
}
unsafe impl Send for AotRuntime {}
unsafe impl Sync for AotRuntime {}
impl AotRuntime {
fn new() -> Self {
let inner = Mutex::new(Inner { plugins: PluginCache::new(), libraries: Vec::new() });
Self { inner, exception_handler: OnceLock::new() }
}
pub fn exception_handler(&self, handler: impl Fn(String) + Send + Sync + 'static) {
assert!(self.exception_handler.set(Arc::new(handler)).is_ok(), "exception handler already set");
}
pub fn load<T: PluginTrait + Send + Sync + 'static>(&self, lib_path: impl AsRef<Path>) -> Result<super::Plugin<T>, PluginLoadError> {
let path = lib_path.as_ref().to_path_buf();
{
let inner = self.inner.lock().expect("runtime mutex poisoned");
inner.plugins.check_uniqueness::<T>(&path)?;
if let Some(arc) = inner.plugins.get_cached::<T>(&path) {
return Ok(super::Plugin::new(arc));
}
}
let lib = unsafe { libloading::Library::new(path.as_os_str()) }.map_err(|e| PluginLoadError::load_failed(e.to_string()))?;
let plugin = T::load_from(|symbol| {
let symbol_bytes: Vec<u8> = symbol.bytes().chain(std::iter::once(0)).collect();
match unsafe { lib.get::<extern "system" fn()>(&*symbol_bytes) } {
Ok(f) => *f as *const u8,
Err(_) => std::ptr::null(),
}
})?;
let register_fn = plugin.register_trampoline_fn();
interoptopus::register_wire_trampolines!(|id, ptr| {
(register_fn)(id, ptr);
});
if let Some(handler) = self.exception_handler.get() {
let ctx = Box::into_raw(Box::new(HandlerShim { handler: Arc::clone(handler) })) as *const u8;
register_fn(TRAMPOLINE_UNCAUGHT_EXCEPTION, shared::uncaught_exception_callback as *const u8);
register_fn(TRAMPOLINE_UNCAUGHT_EXCEPTION_CTX, ctx);
}
plugin.verify_api_guard()?;
let arc = Arc::new(plugin);
{
let mut inner = self.inner.lock().expect("runtime mutex poisoned");
inner.plugins.insert::<T>(path, Arc::clone(&arc));
inner.libraries.push(lib);
}
Ok(super::Plugin::new(arc))
}
}
static RUNTIME: OnceLock<AotRuntime> = OnceLock::new();
pub fn runtime() -> &'static AotRuntime {
RUNTIME.get_or_init(AotRuntime::new)
}