use super::{
opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, PromiseHook, RejectionTracker,
};
use crate::allocator::Allocator;
#[cfg(feature = "loader")]
use crate::loader::{Loader, Resolver};
use crate::{result::JobException, Context, Mut, Ref, Result, Weak};
use alloc::{ffi::CString, vec::Vec};
use core::{ptr::NonNull, result::Result as StdResult};
#[derive(Clone)]
#[repr(transparent)]
pub struct WeakRuntime(Weak<Mut<RawRuntime>>);
impl WeakRuntime {
pub fn try_ref(&self) -> Option<Runtime> {
self.0.upgrade().map(|inner| Runtime { inner })
}
}
#[derive(Clone)]
#[repr(transparent)]
pub struct Runtime {
pub(crate) inner: Ref<Mut<RawRuntime>>,
}
impl Runtime {
pub fn new() -> Result<Self> {
let opaque = Opaque::new();
let rt = unsafe { RawRuntime::new(opaque)? };
Ok(Self {
inner: Ref::new(Mut::new(rt)),
})
}
pub fn new_with_alloc<A>(allocator: A) -> Result<Self>
where
A: Allocator + 'static,
{
let opaque = Opaque::new();
let rt = unsafe { RawRuntime::new_with_allocator(opaque, allocator)? };
Ok(Self {
inner: Ref::new(Mut::new(rt)),
})
}
pub fn weak(&self) -> WeakRuntime {
WeakRuntime(Ref::downgrade(&self.inner))
}
#[inline]
pub fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
unsafe {
self.inner.lock().set_promise_hook(tracker);
}
}
#[inline]
pub fn set_host_promise_rejection_tracker(&self, tracker: Option<RejectionTracker>) {
unsafe {
self.inner
.lock()
.set_host_promise_rejection_tracker(tracker);
}
}
#[inline]
pub fn set_interrupt_handler(&self, handler: Option<InterruptHandler>) {
unsafe {
self.inner.lock().set_interrupt_handler(handler);
}
}
#[cfg(feature = "loader")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "loader")))]
pub fn set_loader<R, L>(&self, resolver: R, loader: L)
where
R: Resolver + 'static,
L: Loader + 'static,
{
unsafe {
self.inner.lock().set_loader(resolver, loader);
}
}
pub fn set_info<S: Into<Vec<u8>>>(&self, info: S) -> Result<()> {
let string = CString::new(info)?;
unsafe {
self.inner.lock().set_info(string);
}
Ok(())
}
pub fn set_memory_limit(&self, limit: usize) {
unsafe {
self.inner.lock().set_memory_limit(limit);
}
}
pub fn set_max_stack_size(&self, limit: usize) {
unsafe {
self.inner.lock().set_max_stack_size(limit);
}
}
pub fn set_gc_threshold(&self, threshold: usize) {
unsafe {
self.inner.lock().set_gc_threshold(threshold);
}
}
pub fn set_dump_flags(&self, flags: u64) {
unsafe {
self.inner.lock().set_dump_flags(flags);
}
}
pub fn run_gc(&self) {
unsafe {
self.inner.lock().run_gc();
}
}
pub fn memory_usage(&self) -> MemoryUsage {
unsafe { self.inner.lock().memory_usage() }
}
#[inline]
pub fn is_job_pending(&self) -> bool {
self.inner.lock().is_job_pending()
}
#[inline]
pub fn execute_pending_job(&self) -> StdResult<bool, JobException> {
let mut lock = self.inner.lock();
lock.update_stack_top();
lock.execute_pending_job().map_err(|e| {
JobException(unsafe {
Context::from_raw(
NonNull::new(e).expect("QuickJS returned null ptr for job error"),
self.clone(),
)
})
})
}
}
#[cfg(feature = "parallel")]
unsafe impl Send for Runtime {}
#[cfg(feature = "parallel")]
unsafe impl Send for WeakRuntime {}
#[cfg(feature = "parallel")]
unsafe impl Sync for Runtime {}
#[cfg(feature = "parallel")]
unsafe impl Sync for WeakRuntime {}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn base_runtime() {
let rt = Runtime::new().unwrap();
rt.set_info("test runtime").unwrap();
rt.set_memory_limit(0xFFFF);
rt.set_gc_threshold(0xFF);
rt.run_gc();
}
}