use crate::{JSCContext, JSCValue, jsc};
use rong_core::{JSEngine, JSRuntimeImpl};
#[cfg(jsc_interrupt)]
use std::cell::Cell;
#[cfg(jsc_interrupt)]
use std::sync::Arc;
#[cfg(jsc_interrupt)]
use std::sync::atomic::{AtomicBool, Ordering};
#[cfg(jsc_interrupt)]
const WATCHDOG_QUANTUM_SECONDS: f64 = 0.1;
#[cfg(jsc_interrupt)]
struct WatchdogState {
group: *const jsc::OpaqueJSContextGroup,
flag: Arc<AtomicBool>,
}
pub struct JSCRuntime {
raw: *const jsc::OpaqueJSContextGroup,
#[cfg(jsc_interrupt)]
watchdog_state: Cell<*mut WatchdogState>,
}
#[cfg(jsc_interrupt)]
unsafe extern "C" fn should_terminate(
_ctx: jsc::JSContextRef,
context: *mut std::os::raw::c_void,
) -> bool {
let state = unsafe { &*(context as *const WatchdogState) };
if state.flag.load(Ordering::Relaxed) {
return true;
}
unsafe {
jsc::JSContextGroupSetExecutionTimeLimit(
state.group,
WATCHDOG_QUANTUM_SECONDS,
Some(should_terminate),
context,
);
}
false
}
impl JSRuntimeImpl for JSCRuntime {
type RawRuntime = *const jsc::OpaqueJSContextGroup;
type Context = JSCContext;
fn new() -> Self {
#[cfg(jsc_source)]
jsc::ensure_initialized();
Self {
raw: unsafe { jsc::JSContextGroupCreate() },
#[cfg(jsc_interrupt)]
watchdog_state: Cell::new(std::ptr::null_mut()),
}
}
fn to_raw(&self) -> Self::RawRuntime {
self.raw
}
fn run_gc(&self) {}
#[cfg(jsc_interrupt)]
fn install_interrupt(&self, flag: Arc<AtomicBool>) -> bool {
let state = Box::into_raw(Box::new(WatchdogState {
group: self.raw,
flag,
}));
self.watchdog_state.set(state);
unsafe {
jsc::JSContextGroupSetExecutionTimeLimit(
self.raw,
WATCHDOG_QUANTUM_SECONDS,
Some(should_terminate),
state as *mut std::os::raw::c_void,
);
}
true
}
}
impl Drop for JSCRuntime {
fn drop(&mut self) {
unsafe {
#[cfg(jsc_interrupt)]
{
let state = self.watchdog_state.replace(std::ptr::null_mut());
if !state.is_null() {
jsc::JSContextGroupClearExecutionTimeLimit(self.raw);
drop(Box::from_raw(state));
}
}
jsc::JSContextGroupRelease(self.raw);
}
}
}
pub struct JavaScriptCore;
impl JSEngine for JavaScriptCore {
type Value = JSCValue;
type Context = JSCContext;
type Runtime = JSCRuntime;
fn name() -> &'static str {
"JavaScriptCore"
}
fn version() -> String {
#[cfg(jsc_source)]
{
option_env!("RONG_JSC_WEBKIT_REVISION")
.map(|revision| format!("source:{revision}"))
.unwrap_or_else(|| String::from("source"))
}
#[cfg(not(jsc_source))]
{
String::from("framework")
}
}
}