use crate::module::loader::{load_module, DefaultModuleLoader, ModuleLoader}; use crate::ContextRef; use oden_js_sys as sys; use std::cell::RefCell; use std::ffi::CStr; use std::sync::Arc; struct PrivateState { refs: u64, loader: Arc>, } impl PrivateState { unsafe extern "C" fn module_loader( ctx: *mut sys::JSContext, path: *const i8, opaque: *mut std::os::raw::c_void, ) -> *mut sys::JSModuleDef { let path = match CStr::from_ptr(path).to_str() { Ok(s) => s, Err(_) => return std::ptr::null_mut(), }; let loader = unsafe { let ptr = opaque as *const RefCell; ptr.as_ref() .expect("We already know this runtime is one of ours!") .borrow() .loader .clone() }; let context = ContextRef::from_raw(ctx); load_module(&context, path, &loader) } } #[derive(Debug)] pub struct Runtime { pub(crate) rt: *mut sys::JSRuntime, } impl Runtime { pub fn new() -> Runtime { Self::with_loader(DefaultModuleLoader::new()) } pub fn with_loader(loader: TLoader) -> Runtime { let state = Box::new(RefCell::new(PrivateState { refs: 1, loader: Arc::new(Box::new(loader)), })); let rt = unsafe { let rt = sys::JS_NewRuntime(); let state = Box::into_raw(state) as *mut _; sys::JS_SetRuntimeOpaque(rt, state); sys::JS_SetModuleLoaderFunc(rt, None, Some(PrivateState::module_loader), state); rt }; Runtime { rt } } pub(crate) fn from_raw(rt: *mut sys::JSRuntime) -> Self { let mut state = unsafe { let ptr = sys::JS_GetRuntimeOpaque(rt) as *const RefCell; ptr.as_ref() .expect("We already know this runtime is one of ours!") .borrow_mut() }; state.refs += 1; Runtime { rt } } pub fn set_memory_limit(&mut self, limit: usize) { unsafe { sys::JS_SetMemoryLimit(self.rt, limit); } } pub fn set_gc_threshold(&mut self, threshold: usize) { unsafe { sys::JS_SetGCThreshold(self.rt, threshold); } } /// Pass in 0 to disable the maximum size check. pub fn set_max_stack_size(&mut self, max_stack: usize) { unsafe { sys::JS_SetMaxStackSize(self.rt, max_stack); } } pub fn run_gc(&mut self) { unsafe { sys::JS_RunGC(self.rt); } } } impl Clone for Runtime { fn clone(&self) -> Self { Runtime::from_raw(self.rt) } } impl Drop for Runtime { fn drop(&mut self) { let should_free = { let mut state = unsafe { let ptr = sys::JS_GetRuntimeOpaque(self.rt) as *const RefCell; ptr.as_ref() .expect("We already know this runtime is one of ours!") .borrow_mut() }; state.refs -= 1; state.refs == 0 }; if should_free { unsafe { let opaque = sys::JS_GetRuntimeOpaque(self.rt); sys::JS_RunGC(self.rt); sys::JS_FreeRuntime(self.rt); if !opaque.is_null() { // Just let the system drop it here. let _ = Box::from_raw(opaque as *mut RefCell); } } } } }