rquickjs_core/runtime/
base.rs1use super::{
4 opaque::Opaque, raw::RawRuntime, InterruptHandler, MemoryUsage, PromiseHook, RejectionTracker,
5};
6use crate::allocator::Allocator;
7#[cfg(feature = "loader")]
8use crate::loader::{Loader, Resolver};
9use crate::{qjs, result::JobException, Context, Mut, Ref, Result, Weak};
10use alloc::{ffi::CString, vec::Vec};
11use core::{ptr::NonNull, result::Result as StdResult};
12
13#[derive(Clone)]
17#[repr(transparent)]
18pub struct WeakRuntime(Weak<Mut<RawRuntime>>);
19
20impl WeakRuntime {
21 pub fn try_ref(&self) -> Option<Runtime> {
22 self.0.upgrade().map(|inner| Runtime { inner })
23 }
24}
25
26#[derive(Clone)]
28#[repr(transparent)]
29pub struct Runtime {
30 pub(crate) inner: Ref<Mut<RawRuntime>>,
31}
32
33impl Runtime {
34 pub fn new() -> Result<Self> {
41 let opaque = Opaque::new();
42 let rt = unsafe { RawRuntime::new(opaque)? };
43 Ok(Self {
44 inner: Ref::new(Mut::new(rt)),
45 })
46 }
47
48 pub fn new_with_alloc<A>(allocator: A) -> Result<Self>
52 where
53 A: Allocator + 'static,
54 {
55 let opaque = Opaque::new();
56 let rt = unsafe { RawRuntime::new_with_allocator(opaque, allocator)? };
57 Ok(Self {
58 inner: Ref::new(Mut::new(rt)),
59 })
60 }
61
62 pub fn weak(&self) -> WeakRuntime {
64 WeakRuntime(Ref::downgrade(&self.inner))
65 }
66
67 #[cfg(feature = "jit-abi")]
69 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "jit-abi")))]
70 pub fn attach_jit_backend<B>(
71 &self,
72 backend: B,
73 ) -> StdResult<super::RuntimeJitGuard, super::JitBackendAttachError>
74 where
75 B: super::JitBackend,
76 {
77 super::RuntimeJitGuard::attach(self, backend)
78 }
79
80 #[cfg(feature = "jit-abi")]
82 #[doc(hidden)]
83 pub fn jit_runtime_id(&self) -> u64 {
84 self.inner.lock().jit_runtime_id()
85 }
86
87 #[inline]
89 pub fn set_promise_hook(&self, tracker: Option<PromiseHook>) {
90 unsafe {
91 self.inner.lock().set_promise_hook(tracker);
92 }
93 }
94
95 #[inline]
97 pub fn set_host_promise_rejection_tracker(&self, tracker: Option<RejectionTracker>) {
98 unsafe {
99 self.inner
100 .lock()
101 .set_host_promise_rejection_tracker(tracker);
102 }
103 }
104
105 #[inline]
109 pub fn set_interrupt_handler(&self, handler: Option<InterruptHandler>) {
110 unsafe {
111 self.inner.lock().set_interrupt_handler(handler);
112 }
113 }
114
115 #[cfg(feature = "loader")]
117 #[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "loader")))]
118 pub fn set_loader<R, L>(&self, resolver: R, loader: L)
119 where
120 R: Resolver + 'static,
121 L: Loader + 'static,
122 {
123 unsafe {
124 self.inner.lock().set_loader(resolver, loader);
125 }
126 }
127
128 pub fn set_info<S: Into<Vec<u8>>>(&self, info: S) -> Result<()> {
130 let string = CString::new(info)?;
131 unsafe {
132 self.inner.lock().set_info(string);
133 }
134 Ok(())
135 }
136
137 pub fn set_memory_limit(&self, limit: usize) {
144 unsafe {
145 self.inner.lock().set_memory_limit(limit);
146 }
147 }
148
149 pub fn set_max_stack_size(&self, limit: usize) {
153 unsafe {
154 self.inner.lock().set_max_stack_size(limit);
155 }
156 }
157
158 pub fn set_gc_threshold(&self, threshold: usize) {
160 unsafe {
161 self.inner.lock().set_gc_threshold(threshold);
162 }
163 }
164
165 pub fn set_dump_flags(&self, flags: u64) {
167 unsafe {
168 self.inner.lock().set_dump_flags(flags);
169 }
170 }
171
172 pub fn run_gc(&self) {
179 unsafe {
180 self.inner.lock().run_gc();
181 }
182 }
183
184 pub fn memory_usage(&self) -> MemoryUsage {
186 unsafe { self.inner.lock().memory_usage() }
187 }
188
189 #[inline]
193 pub fn is_job_pending(&self) -> bool {
194 self.inner.lock().is_job_pending()
195 }
196
197 #[inline]
201 pub fn execute_pending_job(&self) -> StdResult<bool, JobException> {
202 let mut lock = self.inner.lock();
203 lock.update_stack_top();
204 lock.execute_pending_job().map_err(|e| {
205 let ptr = NonNull::new(e).expect("QuickJS returned null ptr for job error");
206 unsafe { qjs::JS_DupContext(ptr.as_ptr()) };
209 JobException(unsafe { Context::from_raw(ptr, self.clone()) })
210 })
211 }
212}
213
214#[cfg(feature = "parallel")]
217unsafe impl Send for Runtime {}
218#[cfg(feature = "parallel")]
219unsafe impl Send for WeakRuntime {}
220
221#[cfg(feature = "parallel")]
225unsafe impl Sync for Runtime {}
226#[cfg(feature = "parallel")]
227unsafe impl Sync for WeakRuntime {}
228
229#[cfg(test)]
230mod test {
231 use super::*;
232 #[test]
233 fn base_runtime() {
234 let rt = Runtime::new().unwrap();
235 rt.set_info("test runtime").unwrap();
236 rt.set_memory_limit(0xFFFF);
237 rt.set_gc_threshold(0xFF);
238 rt.run_gc();
239 }
240
241 #[test]
242 fn set_max_stack_size_large_values() {
243 let rt = Runtime::new().unwrap();
244 rt.set_max_stack_size(usize::MAX);
245 let ctx = crate::Context::full(&rt).unwrap();
246 ctx.with(|ctx| {
247 ctx.eval::<i32, _>("1 + 1").unwrap();
248 });
249 rt.set_max_stack_size(isize::MAX as usize);
250 ctx.with(|ctx| {
251 ctx.eval::<i32, _>("1 + 1").unwrap();
252 });
253 rt.set_max_stack_size(0);
254 ctx.with(|ctx| {
255 ctx.eval::<i32, _>("1 + 1").unwrap();
256 });
257 rt.set_max_stack_size(256 * 1024);
258 ctx.with(|ctx| {
259 ctx.eval::<i32, _>("1 + 1").unwrap();
260 });
261 }
262}