1mod base;
4mod exotic;
5pub(crate) mod opaque;
6pub(crate) mod raw;
7mod userdata;
8
9#[cfg(feature = "jit-abi")]
10mod jit;
11
12#[doc(hidden)]
13#[cfg(feature = "jit-test-support")]
14pub mod test_support;
15
16#[cfg(feature = "futures")]
17mod r#async;
18#[cfg(feature = "futures")]
19pub(crate) mod schedular;
20#[cfg(feature = "futures")]
21mod spawner;
22#[cfg(feature = "futures")]
23pub use spawner::DriveFuture;
24
25use alloc::boxed::Box;
26pub use base::{Runtime, WeakRuntime};
27#[cfg(feature = "jit-abi")]
28#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "jit-abi")))]
29pub use jit::{
30 JitBackend, JitBackendAttachError, JitFunctionRegistry, JitFunctionRegistryError,
31 RuntimeJitGuard,
32};
33pub use userdata::{UserDataError, UserDataGuard};
34
35#[cfg(feature = "futures")]
36pub(crate) use r#async::InnerRuntime;
37#[cfg(feature = "futures")]
38pub use r#async::{AsyncRuntime, AsyncWeakRuntime};
39
40use crate::value::promise::PromiseHookType;
41use crate::{Ctx, Value};
42
43#[cfg(not(feature = "parallel"))]
45pub type PromiseHook =
46 Box<dyn for<'a> Fn(Ctx<'a>, PromiseHookType, Value<'a>, Value<'a>) + 'static>;
47#[cfg(feature = "parallel")]
49pub type PromiseHook =
50 Box<dyn for<'a> Fn(Ctx<'a>, PromiseHookType, Value<'a>, Value<'a>) + Send + 'static>;
51
52#[cfg(not(feature = "parallel"))]
54pub type RejectionTracker = Box<dyn for<'a> Fn(Ctx<'a>, Value<'a>, Value<'a>, bool) + 'static>;
55#[cfg(feature = "parallel")]
57pub type RejectionTracker =
58 Box<dyn for<'a> Fn(Ctx<'a>, Value<'a>, Value<'a>, bool) + Send + 'static>;
59
60#[cfg(not(feature = "parallel"))]
62pub type InterruptHandler = Box<dyn FnMut() -> bool + 'static>;
63#[cfg(feature = "parallel")]
65pub type InterruptHandler = Box<dyn FnMut() -> bool + Send + 'static>;
66
67pub type MemoryUsage = crate::qjs::JSMemoryUsage;
69
70#[cfg(all(test, feature = "jit-abi"))]
71mod test {
72 use alloc::sync::Arc;
73 use core::{
74 mem,
75 sync::atomic::{AtomicBool, Ordering},
76 };
77
78 use super::{JitBackend, Runtime};
79 use crate::qjs;
80
81 struct DetachProbe(Arc<AtomicBool>);
82
83 unsafe impl JitBackend for DetachProbe {
84 fn runtime_detach(&mut self) {
85 self.0.store(true, Ordering::SeqCst);
86 }
87 }
88
89 #[test]
90 fn guard_detaches_backend_while_runtime_is_alive() {
91 let runtime = Runtime::new().unwrap();
92 let detached = Arc::new(AtomicBool::new(false));
93 let guard = runtime
94 .attach_jit_backend(DetachProbe(Arc::clone(&detached)))
95 .unwrap();
96 let clone = runtime.clone();
97
98 drop(runtime);
99 drop(guard);
100 assert!(detached.load(Ordering::SeqCst));
101 clone.run_gc();
102 }
103
104 #[test]
105 fn engine_rejects_a_mismatched_vtable_size() {
106 let runtime = Runtime::new().unwrap();
107 let raw = runtime.inner.lock();
108 let mut vtable = unsafe { mem::zeroed::<qjs::JSJitBackendVTable>() };
109 vtable.struct_size = mem::size_of::<qjs::JSJitBackendVTable>() as u32 - 1;
110 let status =
111 unsafe { qjs::JS_SetJitBackend(raw.rt.as_ptr(), &vtable, core::ptr::null_mut()) };
112 assert_eq!(status, qjs::JS_JIT_BACKEND_INVALID_VTABLE);
113 }
114
115 #[test]
116 fn engine_detach_is_idempotent() {
117 let runtime = Runtime::new().unwrap();
118 let raw = runtime.inner.lock();
119 for _ in 0..2 {
120 let status = unsafe {
121 qjs::JS_SetJitBackend(raw.rt.as_ptr(), core::ptr::null(), core::ptr::null_mut())
122 };
123 assert_eq!(status, qjs::JS_JIT_BACKEND_OK);
124 }
125 }
126}