use crate::{QJSContext, QJSValue, qjs};
use rong_core::{JSEngine, JSRuntimeImpl};
use std::cell::RefCell;
use std::collections::HashMap;
use std::rc::{Rc, Weak};
thread_local! {
static RUNTIME_REGISTRY: RefCell<HashMap<usize, Weak<QJSRuntimeInner>>> =
RefCell::new(HashMap::new());
}
pub(crate) struct QJSRuntimeInner {
pub(crate) rt: *mut qjs::JSRuntime,
}
impl Drop for QJSRuntimeInner {
fn drop(&mut self) {
let key = self.rt as usize;
RUNTIME_REGISTRY.with(|m| {
m.borrow_mut().remove(&key);
});
unsafe {
let mut ctx = std::ptr::null_mut();
let mut iterations: usize = 0;
const MAX_JOB_DRAIN: usize = 10_000;
while qjs::JS_IsJobPending(self.rt) && iterations < MAX_JOB_DRAIN {
let rc = qjs::JS_ExecutePendingJob(self.rt, &mut ctx);
if rc < 0 {
break;
}
iterations += 1;
}
qjs::JS_RunGC(self.rt);
qjs::JS_RunGC(self.rt);
qjs::JS_FreeRuntime(self.rt);
}
}
}
#[derive(Clone)]
pub struct QJSRuntime {
pub(crate) inner: Rc<QJSRuntimeInner>,
}
impl JSRuntimeImpl for QJSRuntime {
type RawRuntime = *mut qjs::JSRuntime;
type Context = QJSContext;
fn new() -> Self {
let rt = unsafe { qjs::JS_NewRuntime() };
let inner = Rc::new(QJSRuntimeInner { rt });
let key = rt as usize;
RUNTIME_REGISTRY.with(|m| {
m.borrow_mut().insert(key, Rc::downgrade(&inner));
});
#[cfg(debug_assertions)]
if let Ok(flags) = std::env::var("DUMPFLAGS") {
let flags = if flags.starts_with("0x") {
u64::from_str_radix(flags.trim_start_matches("0x"), 16).unwrap_or(0x4000 | 0x200)
} else {
0x4000 | 0x200
};
println!("Dump flags: 0x{:x}", flags);
unsafe {
qjs::JS_SetDumpFlags(rt, flags);
}
}
Self { inner }
}
fn to_raw(&self) -> Self::RawRuntime {
self.inner.rt
}
fn run_pending_jobs(&self) -> i32 {
unsafe {
let mut ctx = std::ptr::null_mut();
while qjs::JS_IsJobPending(self.inner.rt) {
qjs::JS_ExecutePendingJob(self.inner.rt, &mut ctx);
}
}
0
}
fn run_gc(&self) {
unsafe {
#[cfg(debug_assertions)]
println!("run gc");
qjs::JS_RunGC(self.inner.rt);
}
}
}
pub(crate) fn runtime_guard_from_ctx(ctx: *mut qjs::JSContext) -> Option<Rc<QJSRuntimeInner>> {
if ctx.is_null() {
return None;
}
let rt = unsafe { qjs::JS_GetRuntime(ctx) };
let key = rt as usize;
RUNTIME_REGISTRY.with(|m| m.borrow().get(&key).and_then(|w| w.upgrade()))
}
pub struct QuickJS;
impl JSEngine for QuickJS {
type Value = QJSValue;
type Context = QJSContext;
type Runtime = QJSRuntime;
fn name() -> &'static str {
"QuickJS-NG"
}
fn version() -> String {
unsafe {
let c_str = qjs::JS_GetVersion();
std::ffi::CStr::from_ptr(c_str)
.to_str()
.map(|s| s.to_string())
.unwrap()
}
}
}