Skip to main content

mozjs/
rust.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5//! Rust wrappers around the raw JS apis
6
7use std::cell::Cell;
8use std::char;
9use std::default::Default;
10use std::ffi::{c_char, c_void, CStr, CString};
11use std::marker::PhantomData;
12use std::mem;
13use std::mem::MaybeUninit;
14use std::ops::{ControlFlow, Deref, DerefMut};
15use std::ptr::{self, NonNull};
16use std::slice;
17use std::str;
18use std::sync::atomic::{AtomicU32, Ordering};
19use std::sync::{Arc, Mutex, RwLock};
20
21use self::wrappers2::{
22    BuildStackString, CaptureCurrentStack, CreateRootedIdVector, CreateRootedObjectVector,
23    JS_DefineFunctions, JS_DefineProperties, JS_WrapObject, JS_WrapValue,
24    StackGCVectorStringAtIndex, StackGCVectorStringLength, StackGCVectorValueAtIndex,
25    StackGCVectorValueLength, ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow,
26    ToUint32Slow, ToUint64Slow,
27};
28use crate::consts::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_RESERVED_SLOTS_MASK};
29use crate::consts::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
30use crate::default_heapsize;
31pub use crate::gc::*;
32use crate::glue::AppendToRootedObjectVector;
33use crate::glue::OwningCompileOptions_for_fc;
34use crate::glue::{DeleteCompileOptions, DeleteRootedObjectVector, DestroyRootedIdVector};
35use crate::glue::{DeleteJSAutoStructuredCloneBuffer, NewJSAutoStructuredCloneBuffer};
36use crate::glue::{GetIdVectorAddress, GetObjectVectorAddress, SliceRootedIdVector};
37use crate::jsapi;
38use crate::jsapi::glue::{DeleteRealmOptions, JS_Init, JS_NewRealmOptions};
39use crate::jsapi::js;
40use crate::jsapi::js::frontend::InitialStencilAndDelazifications;
41use crate::jsapi::mozilla::Utf8Unit;
42use crate::jsapi::shadow::BaseShape;
43use crate::jsapi::HandleObjectVector as RawHandleObjectVector;
44use crate::jsapi::InstantiateOptions;
45use crate::jsapi::JS_AddExtraGCRootsTracer;
46use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
47use crate::jsapi::StackFormat;
48use crate::jsapi::{already_AddRefed, jsid};
49use crate::jsapi::{BorrowedErrorReport, Rooted};
50use crate::jsapi::{DeleteOwningCompileOptions, OwningCompileOptions};
51use crate::jsapi::{HandleValueArray, StencilRelease};
52use crate::jsapi::{InitSelfHostedCode, IsWindowSlow};
53use crate::jsapi::{JSAutoStructuredCloneBuffer, JSStructuredCloneCallbacks, StructuredCloneScope};
54use crate::jsapi::{JSClass, JSClassOps, JSContext, Realm, JSCLASS_RESERVED_SLOTS_SHIFT};
55use crate::jsapi::{JSErrorReport, JSFunctionSpec, JSGCParamKey};
56use crate::jsapi::{JSObject, JSPropertySpec, JSRuntime};
57use crate::jsapi::{JSString, Object, PersistentRootedIdVector};
58use crate::jsapi::{JS_DestroyContext, JS_ShutDown};
59use crate::jsapi::{JS_EnumerateStandardClasses, JS_GlobalObjectTraceHook};
60use crate::jsapi::{JS_MayResolveStandardClass, JS_NewContext, JS_ResolveStandardClass};
61use crate::jsapi::{JS_RequestInterruptCallback, JS_RequestInterruptCallbackCanWait};
62use crate::jsapi::{JS_SetGCParameter, JS_SetNativeStackQuota};
63use crate::jsapi::{JS_StackCapture_AllFrames, JS_StackCapture_MaxFrames};
64use crate::jsapi::{PersistentRootedObjectVector, ReadOnlyCompileOptions, RootingContext};
65use crate::jsapi::{RootedObject, RootedValue, ToWindowProxyIfWindowSlow};
66use crate::jsapi::{SetWarningReporter, SourceText, ToBooleanSlow};
67use crate::jsval::{JSVal, ObjectValue, UndefinedValue};
68use crate::offthread::FrontendContext;
69use crate::panic::maybe_resume_unwind;
70use crate::realm::AutoRealm;
71use log::{debug, warn};
72use mozjs_sys::jsapi::JS::SavedFrameResult;
73pub use mozjs_sys::jsgc::{GCMethods, IntoHandle, IntoMutableHandle};
74pub use mozjs_sys::trace::Traceable as Trace;
75
76use crate::rooted;
77
78// From Gecko:
79// Our "default" stack is what we use in configurations where we don't have a compelling reason to
80// do things differently. This is effectively 1MB on 64-bit platforms.
81const STACK_QUOTA: usize = 128 * 8 * 1024;
82
83// From Gecko:
84// The JS engine permits us to set different stack limits for system code,
85// trusted script, and untrusted script. We have tests that ensure that
86// we can always execute 10 "heavy" (eval+with) stack frames deeper in
87// privileged code. Our stack sizes vary greatly in different configurations,
88// so satisfying those tests requires some care. Manual measurements of the
89// number of heavy stack frames achievable gives us the following rough data,
90// ordered by the effective categories in which they are grouped in the
91// JS_SetNativeStackQuota call (which predates this analysis).
92//
93// (NB: These numbers may have drifted recently - see bug 938429)
94// OSX 64-bit Debug: 7MB stack, 636 stack frames => ~11.3k per stack frame
95// OSX64 Opt: 7MB stack, 2440 stack frames => ~3k per stack frame
96//
97// Linux 32-bit Debug: 2MB stack, 426 stack frames => ~4.8k per stack frame
98// Linux 64-bit Debug: 4MB stack, 455 stack frames => ~9.0k per stack frame
99//
100// Windows (Opt+Debug): 900K stack, 235 stack frames => ~3.4k per stack frame
101//
102// Linux 32-bit Opt: 1MB stack, 272 stack frames => ~3.8k per stack frame
103// Linux 64-bit Opt: 2MB stack, 316 stack frames => ~6.5k per stack frame
104//
105// We tune the trusted/untrusted quotas for each configuration to achieve our
106// invariants while attempting to minimize overhead. In contrast, our buffer
107// between system code and trusted script is a very unscientific 10k.
108const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
109
110// Gecko's value on 64-bit.
111const TRUSTED_SCRIPT_BUFFER: usize = 8 * 12800;
112
113trait ToResult {
114    fn to_result(self) -> Result<(), ()>;
115}
116
117impl ToResult for bool {
118    fn to_result(self) -> Result<(), ()> {
119        if self {
120            Ok(())
121        } else {
122            Err(())
123        }
124    }
125}
126
127// ___________________________________________________________________________
128// friendly Rustic API to runtimes
129
130pub struct RealmOptions(*mut jsapi::RealmOptions);
131
132impl Deref for RealmOptions {
133    type Target = jsapi::RealmOptions;
134    fn deref(&self) -> &Self::Target {
135        unsafe { &*self.0 }
136    }
137}
138
139impl DerefMut for RealmOptions {
140    fn deref_mut(&mut self) -> &mut Self::Target {
141        unsafe { &mut *self.0 }
142    }
143}
144
145impl Default for RealmOptions {
146    fn default() -> RealmOptions {
147        RealmOptions(unsafe { JS_NewRealmOptions() })
148    }
149}
150
151impl Drop for RealmOptions {
152    fn drop(&mut self) {
153        unsafe { DeleteRealmOptions(self.0) }
154    }
155}
156
157thread_local!(static CONTEXT: Cell<Option<NonNull<JSContext>>> = Cell::new(None));
158
159#[derive(PartialEq)]
160enum EngineState {
161    Uninitialized,
162    InitFailed,
163    Initialized,
164    ShutDown,
165}
166
167static ENGINE_STATE: Mutex<EngineState> = Mutex::new(EngineState::Uninitialized);
168
169#[derive(Debug)]
170pub enum JSEngineError {
171    AlreadyInitialized,
172    AlreadyShutDown,
173    InitFailed,
174}
175
176/// A handle that must be kept alive in order to create new Runtimes.
177/// When this handle is dropped, the engine is shut down and cannot
178/// be reinitialized.
179pub struct JSEngine {
180    /// The count of alive handles derived from this initialized instance.
181    outstanding_handles: Arc<AtomicU32>,
182    // Ensure this type cannot be sent between threads.
183    marker: PhantomData<*mut ()>,
184}
185
186pub struct JSEngineHandle(Arc<AtomicU32>);
187
188impl Clone for JSEngineHandle {
189    fn clone(&self) -> JSEngineHandle {
190        self.0.fetch_add(1, Ordering::SeqCst);
191        JSEngineHandle(self.0.clone())
192    }
193}
194
195impl Drop for JSEngineHandle {
196    fn drop(&mut self) {
197        self.0.fetch_sub(1, Ordering::SeqCst);
198    }
199}
200
201impl JSEngine {
202    /// Initialize the JS engine to prepare for creating new JS runtimes.
203    pub fn init() -> Result<JSEngine, JSEngineError> {
204        let mut state = ENGINE_STATE.lock().unwrap();
205        match *state {
206            EngineState::Initialized => return Err(JSEngineError::AlreadyInitialized),
207            EngineState::InitFailed => return Err(JSEngineError::InitFailed),
208            EngineState::ShutDown => return Err(JSEngineError::AlreadyShutDown),
209            EngineState::Uninitialized => (),
210        }
211        if unsafe { !JS_Init() } {
212            *state = EngineState::InitFailed;
213            Err(JSEngineError::InitFailed)
214        } else {
215            *state = EngineState::Initialized;
216            Ok(JSEngine {
217                outstanding_handles: Arc::new(AtomicU32::new(0)),
218                marker: PhantomData,
219            })
220        }
221    }
222
223    pub fn can_shutdown(&self) -> bool {
224        self.outstanding_handles.load(Ordering::SeqCst) == 0
225    }
226
227    /// Create a handle to this engine.
228    pub fn handle(&self) -> JSEngineHandle {
229        self.outstanding_handles.fetch_add(1, Ordering::SeqCst);
230        JSEngineHandle(self.outstanding_handles.clone())
231    }
232}
233
234/// Shut down the JS engine, invalidating any existing runtimes and preventing
235/// any new ones from being created.
236impl Drop for JSEngine {
237    fn drop(&mut self) {
238        let mut state = ENGINE_STATE.lock().unwrap();
239        if *state == EngineState::Initialized {
240            assert_eq!(
241                self.outstanding_handles.load(Ordering::SeqCst),
242                0,
243                "There are outstanding JS engine handles"
244            );
245            *state = EngineState::ShutDown;
246            unsafe {
247                JS_ShutDown();
248            }
249        }
250    }
251}
252
253pub fn transform_str_to_source_text(source: &str) -> SourceText<Utf8Unit> {
254    SourceText {
255        units_: source.as_ptr() as *const _,
256        length_: source.len() as u32,
257        ownsUnits_: false,
258        _phantom_0: PhantomData,
259    }
260}
261
262pub fn transform_u16_to_source_text(source: &[u16]) -> SourceText<u16> {
263    SourceText {
264        units_: source.as_ptr() as *const _,
265        length_: source.len() as u32,
266        ownsUnits_: false,
267        _phantom_0: PhantomData,
268    }
269}
270
271/// A handle to a Runtime that will be used to create a new runtime in another
272/// thread. This handle and the new runtime must be destroyed before the original
273/// runtime can be dropped.
274pub struct ParentRuntime {
275    /// Raw pointer to the underlying SpiderMonkey runtime.
276    parent: *mut JSRuntime,
277    /// Handle to ensure the JS engine remains running while this handle exists.
278    engine: JSEngineHandle,
279    /// The number of children of the runtime that created this ParentRuntime value.
280    children_of_parent: Arc<()>,
281}
282unsafe impl Send for ParentRuntime {}
283
284/// A wrapper for the `JSContext` structure in SpiderMonkey.
285pub struct Runtime {
286    /// Safe SpiderMonkey context.
287    cx: crate::context::JSContext,
288    /// The engine that this runtime is associated with.
289    engine: JSEngineHandle,
290    /// If this Runtime was created with a parent, this member exists to ensure
291    /// that that parent's count of outstanding children (see [outstanding_children])
292    /// remains accurate and will be automatically decreased when this Runtime value
293    /// is dropped.
294    _parent_child_count: Option<Arc<()>>,
295    /// The strong references to this value represent the number of child runtimes
296    /// that have been created using this Runtime as a parent. Since Runtime values
297    /// must be associated with a particular thread, we cannot simply use Arc<Runtime>
298    /// to represent the resulting ownership graph and risk destroying a Runtime on
299    /// the wrong thread.
300    outstanding_children: Arc<()>,
301    /// An `Option` that holds the same pointer as `cx`.
302    /// This is shared with all [`ThreadSafeJSContext`]s, so
303    /// they can detect when it's destroyed on the main thread.
304    thread_safe_handle: Arc<RwLock<Option<NonNull<JSContext>>>>,
305}
306
307impl Runtime {
308    /// Get the `JSContext` for this thread.
309    ///
310    /// This will eventually be removed for in favour of [crate::context::JSContext]
311    pub fn get() -> Option<NonNull<JSContext>> {
312        CONTEXT.with(|context| context.get())
313    }
314
315    /// Create a [`ThreadSafeJSContext`] that can detect when this `Runtime` is destroyed.
316    pub fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
317        // Existence of `ThreadSafeJSContext` does not actually break invariant of
318        // JSContext, because it can be used for limited subset of methods and they do not trigger GC
319        ThreadSafeJSContext(self.thread_safe_handle.clone())
320    }
321
322    /// Creates a new `JSContext`.
323    pub fn new(engine: JSEngineHandle) -> Runtime {
324        unsafe { Self::create(engine, None) }
325    }
326
327    /// Signal that a new child runtime will be created in the future, and ensure
328    /// that this runtime will not allow itself to be destroyed before the new
329    /// child runtime. Returns a handle that can be passed to `create_with_parent`
330    /// in order to create a new runtime on another thread that is associated with
331    /// this runtime.
332    pub fn prepare_for_new_child(&self) -> ParentRuntime {
333        ParentRuntime {
334            parent: self.rt(),
335            engine: self.engine.clone(),
336            children_of_parent: self.outstanding_children.clone(),
337        }
338    }
339
340    /// Creates a new `JSContext` with a parent runtime. If the parent does not outlive
341    /// the new runtime, its destructor will assert.
342    ///
343    /// Unsafety:
344    /// If panicking does not abort the program, any threads with child runtimes will
345    /// continue executing after the thread with the parent runtime panics, but they
346    /// will be in an invalid and undefined state.
347    pub unsafe fn create_with_parent(parent: ParentRuntime) -> Runtime {
348        Self::create(parent.engine.clone(), Some(parent))
349    }
350
351    unsafe fn create(engine: JSEngineHandle, parent: Option<ParentRuntime>) -> Runtime {
352        let parent_runtime = parent.as_ref().map_or(ptr::null_mut(), |r| r.parent);
353        let js_context = NonNull::new(JS_NewContext(
354            default_heapsize + (ChunkSize as u32),
355            parent_runtime,
356        ))
357        .unwrap();
358
359        // Unconstrain the runtime's threshold on nominal heap size, to avoid
360        // triggering GC too often if operating continuously near an arbitrary
361        // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
362        // still in effect to cause periodical, and we hope hygienic,
363        // last-ditch GCs from within the GC's allocator.
364        JS_SetGCParameter(js_context.as_ptr(), JSGCParamKey::JSGC_MAX_BYTES, u32::MAX);
365
366        JS_AddExtraGCRootsTracer(js_context.as_ptr(), Some(trace_traceables), ptr::null_mut());
367
368        JS_SetNativeStackQuota(
369            js_context.as_ptr(),
370            STACK_QUOTA,
371            STACK_QUOTA - SYSTEM_CODE_BUFFER,
372            STACK_QUOTA - SYSTEM_CODE_BUFFER - TRUSTED_SCRIPT_BUFFER,
373        );
374
375        CONTEXT.with(|context| {
376            assert!(context.get().is_none());
377            context.set(Some(js_context));
378        });
379
380        #[cfg(target_pointer_width = "64")]
381        let cache = crate::jsapi::__BindgenOpaqueArray::<u64, 2>::default();
382        #[cfg(target_pointer_width = "32")]
383        let cache = crate::jsapi::__BindgenOpaqueArray::<u32, 2>::default();
384
385        InitSelfHostedCode(js_context.as_ptr(), cache, None);
386
387        SetWarningReporter(js_context.as_ptr(), Some(report_warning));
388
389        Runtime {
390            engine,
391            _parent_child_count: parent.map(|p| p.children_of_parent),
392            cx: crate::context::JSContext::from_ptr(js_context),
393            outstanding_children: Arc::new(()),
394            thread_safe_handle: Arc::new(RwLock::new(Some(js_context))),
395        }
396    }
397
398    /// Returns the `JSRuntime` object.
399    pub fn rt(&self) -> *mut JSRuntime {
400        unsafe { wrappers2::JS_GetRuntime(self.cx_no_gc()) }
401    }
402
403    /// Returns the `JSContext` object.
404    pub fn cx<'rt>(&'rt mut self) -> &'rt mut crate::context::JSContext {
405        &mut self.cx
406    }
407
408    /// Returns the `JSContext` object.
409    pub fn cx_no_gc<'rt>(&'rt self) -> &'rt crate::context::JSContext {
410        &self.cx
411    }
412}
413
414pub fn evaluate_script(
415    cx: &mut crate::context::JSContext,
416    glob: HandleObject,
417    script: &str,
418    rval: MutableHandleValue,
419    options: CompileOptionsWrapper,
420) -> Result<(), ()> {
421    debug!(
422        "Evaluating script from {} with content {}",
423        options.filename(),
424        script
425    );
426
427    let mut realm = AutoRealm::new_from_handle(cx, glob);
428
429    unsafe {
430        let mut source = transform_str_to_source_text(&script);
431        if !wrappers2::Evaluate2(&mut realm, options.ptr, &mut source, rval.into()) {
432            debug!("...err!");
433            maybe_resume_unwind();
434            Err(())
435        } else {
436            // we could return the script result but then we'd have
437            // to root it and so forth and, really, who cares?
438            debug!("...ok!");
439            Ok(())
440        }
441    }
442}
443
444impl Drop for Runtime {
445    fn drop(&mut self) {
446        self.thread_safe_handle.write().unwrap().take();
447        assert!(
448            Arc::get_mut(&mut self.outstanding_children).is_some(),
449            "This runtime still has live children."
450        );
451        unsafe {
452            JS_DestroyContext(self.cx.raw_cx());
453
454            CONTEXT.with(|context| {
455                assert!(context.take().is_some());
456            });
457        }
458    }
459}
460
461/// A version of the [`JSContext`] that can be used from other threads and is thus
462/// `Send` and `Sync`. This should only ever expose operations that are marked as
463/// thread-safe by the SpiderMonkey API, ie ones that only atomic fields in JSContext.
464#[derive(Clone)]
465pub struct ThreadSafeJSContext(Arc<RwLock<Option<NonNull<JSContext>>>>);
466
467unsafe impl Send for ThreadSafeJSContext {}
468unsafe impl Sync for ThreadSafeJSContext {}
469
470impl ThreadSafeJSContext {
471    /// Call `JS_RequestInterruptCallback` from the SpiderMonkey API.
472    /// This is thread-safe according to
473    /// <https://searchfox.org/mozilla-central/rev/7a85a111b5f42cdc07f438e36f9597c4c6dc1d48/js/public/Interrupt.h#19>
474    pub fn request_interrupt_callback(&self) {
475        if let Some(cx) = self.0.read().unwrap().as_ref() {
476            unsafe {
477                JS_RequestInterruptCallback(cx.as_ptr());
478            }
479        }
480    }
481
482    /// Call `JS_RequestInterruptCallbackCanWait` from the SpiderMonkey API.
483    /// This is thread-safe according to
484    /// <https://searchfox.org/mozilla-central/rev/7a85a111b5f42cdc07f438e36f9597c4c6dc1d48/js/public/Interrupt.h#19>
485    pub fn request_interrupt_callback_can_wait(&self) {
486        if let Some(cx) = self.0.read().unwrap().as_ref() {
487            unsafe {
488                JS_RequestInterruptCallbackCanWait(cx.as_ptr());
489            }
490        }
491    }
492}
493
494const ChunkShift: usize = 20;
495const ChunkSize: usize = 1 << ChunkShift;
496
497#[cfg(target_pointer_width = "32")]
498const ChunkLocationOffset: usize = ChunkSize - 2 * 4 - 8;
499
500// ___________________________________________________________________________
501// Wrappers around things in jsglue.cpp
502
503pub struct RootedObjectVectorWrapper {
504    pub ptr: *mut PersistentRootedObjectVector,
505}
506
507impl RootedObjectVectorWrapper {
508    pub fn new(cx: &mut crate::context::JSContext) -> RootedObjectVectorWrapper {
509        RootedObjectVectorWrapper {
510            ptr: unsafe { CreateRootedObjectVector(cx) },
511        }
512    }
513
514    pub fn append(&self, obj: *mut JSObject) -> bool {
515        unsafe { AppendToRootedObjectVector(self.ptr, obj) }
516    }
517
518    pub fn handle(&self) -> RawHandleObjectVector {
519        RawHandleObjectVector {
520            ptr: unsafe { GetObjectVectorAddress(self.ptr) },
521        }
522    }
523}
524
525impl Drop for RootedObjectVectorWrapper {
526    fn drop(&mut self) {
527        unsafe { DeleteRootedObjectVector(self.ptr) }
528    }
529}
530
531pub struct OwningCompileOptionsWrapper {
532    pub ptr: *mut OwningCompileOptions,
533}
534
535impl OwningCompileOptionsWrapper {
536    pub fn new_for_fc(fc: &FrontendContext, options: *const ReadOnlyCompileOptions) -> Self {
537        Self {
538            ptr: unsafe { OwningCompileOptions_for_fc(**fc, options) },
539        }
540    }
541
542    pub fn read_only(&self) -> &ReadOnlyCompileOptions {
543        unsafe { &(*self.ptr)._base }
544    }
545}
546
547unsafe impl Send for OwningCompileOptionsWrapper {}
548
549impl Drop for OwningCompileOptionsWrapper {
550    fn drop(&mut self) {
551        unsafe { DeleteOwningCompileOptions(self.ptr) }
552    }
553}
554
555impl From<&OwningCompileOptionsWrapper> for InstantiateOptions {
556    fn from(value: &OwningCompileOptionsWrapper) -> Self {
557        let options = value.read_only();
558        InstantiateOptions {
559            skipFilenameValidation: options._base.skipFilenameValidation_,
560            hideScriptFromDebugger: options._base.hideScriptFromDebugger_,
561            deferDebugMetadata: options._base.deferDebugMetadata_,
562            eagerDelazificationStrategy_: options._base.eagerDelazificationStrategy_,
563            eagerBaselineStrategy_: options._base.eagerBaselineStrategy_,
564        }
565    }
566}
567
568pub struct CompileOptionsWrapper {
569    pub ptr: *mut ReadOnlyCompileOptions,
570    filename: CString,
571}
572
573impl CompileOptionsWrapper {
574    pub fn new(cx: &crate::context::JSContext, filename: CString, line: u32) -> Self {
575        let ptr = unsafe { wrappers2::NewCompileOptions(cx, filename.as_ptr(), line) };
576        assert!(!ptr.is_null());
577        Self { ptr, filename }
578    }
579
580    pub fn filename(&self) -> &str {
581        self.filename.to_str().expect("Guaranteed by new")
582    }
583
584    pub fn set_introduction_type(&mut self, introduction_type: &'static CStr) {
585        unsafe {
586            (*self.ptr)._base.introductionType = introduction_type.as_ptr();
587        }
588    }
589
590    pub fn set_muted_errors(&mut self, muted_errors: bool) {
591        unsafe {
592            (*self.ptr)._base.mutedErrors_ = muted_errors;
593        }
594    }
595
596    pub fn set_is_run_once(&mut self, is_run_once: bool) {
597        unsafe {
598            (*self.ptr).isRunOnce = is_run_once;
599        }
600    }
601
602    pub fn set_no_script_rval(&mut self, no_script_rval: bool) {
603        unsafe {
604            (*self.ptr).noScriptRval = no_script_rval;
605        }
606    }
607}
608
609impl Drop for CompileOptionsWrapper {
610    fn drop(&mut self) {
611        unsafe { DeleteCompileOptions(self.ptr) }
612    }
613}
614
615pub struct JSAutoStructuredCloneBufferWrapper {
616    ptr: NonNull<JSAutoStructuredCloneBuffer>,
617}
618
619impl JSAutoStructuredCloneBufferWrapper {
620    pub unsafe fn new(
621        scope: StructuredCloneScope,
622        callbacks: *const JSStructuredCloneCallbacks,
623    ) -> Self {
624        let raw_ptr = NewJSAutoStructuredCloneBuffer(scope, callbacks);
625        Self {
626            ptr: NonNull::new(raw_ptr).unwrap(),
627        }
628    }
629
630    pub fn as_raw_ptr(&self) -> *mut JSAutoStructuredCloneBuffer {
631        self.ptr.as_ptr()
632    }
633}
634
635impl Drop for JSAutoStructuredCloneBufferWrapper {
636    fn drop(&mut self) {
637        unsafe {
638            DeleteJSAutoStructuredCloneBuffer(self.ptr.as_ptr());
639        }
640    }
641}
642
643pub struct Stencil {
644    inner: already_AddRefed<InitialStencilAndDelazifications>,
645}
646
647unsafe impl Send for Stencil {}
648
649impl Drop for Stencil {
650    fn drop(&mut self) {
651        if self.is_null() {
652            return;
653        }
654        unsafe {
655            StencilRelease(self.inner.mRawPtr);
656        }
657    }
658}
659
660impl Deref for Stencil {
661    type Target = *mut InitialStencilAndDelazifications;
662
663    fn deref(&self) -> &Self::Target {
664        &self.inner.mRawPtr
665    }
666}
667
668impl Stencil {
669    pub fn is_null(&self) -> bool {
670        self.inner.mRawPtr.is_null()
671    }
672
673    pub unsafe fn from_raw(inner: already_AddRefed<InitialStencilAndDelazifications>) -> Self {
674        Self { inner }
675    }
676}
677
678// ___________________________________________________________________________
679// Fast inline converters
680
681#[inline]
682pub unsafe fn ToBoolean(v: HandleValue) -> bool {
683    let val = *v.ptr.as_ptr();
684
685    if val.is_boolean() {
686        return val.to_boolean();
687    }
688
689    if val.is_int32() {
690        return val.to_int32() != 0;
691    }
692
693    if val.is_null_or_undefined() {
694        return false;
695    }
696
697    if val.is_double() {
698        let d = val.to_double();
699        return !d.is_nan() && d != 0f64;
700    }
701
702    if val.is_symbol() {
703        return true;
704    }
705
706    ToBooleanSlow(v.into())
707}
708
709#[inline]
710pub unsafe fn ToNumber(cx: &mut crate::context::JSContext, v: HandleValue) -> Result<f64, ()> {
711    let val = *v.ptr.as_ptr();
712    if val.is_number() {
713        return Ok(val.to_number());
714    }
715
716    let mut out = Default::default();
717    if ToNumberSlow(cx, v, &mut out) {
718        Ok(out)
719    } else {
720        Err(())
721    }
722}
723
724#[inline]
725unsafe fn convert_from_int32<T: Default + Copy>(
726    cx: &mut crate::context::JSContext,
727    v: HandleValue,
728    conv_fn: unsafe fn(&mut crate::context::JSContext, HandleValue, *mut T) -> bool,
729) -> Result<T, ()> {
730    let val = *v.ptr.as_ptr();
731    if val.is_int32() {
732        let intval: i64 = val.to_int32() as i64;
733        // TODO: do something better here that works on big endian
734        let intval = *(&intval as *const i64 as *const T);
735        return Ok(intval);
736    }
737
738    let mut out = Default::default();
739    if conv_fn(cx, v, &mut out) {
740        Ok(out)
741    } else {
742        Err(())
743    }
744}
745
746#[inline]
747pub unsafe fn ToInt32(cx: &mut crate::context::JSContext, v: HandleValue) -> Result<i32, ()> {
748    convert_from_int32::<i32>(cx, v, ToInt32Slow)
749}
750
751#[inline]
752pub unsafe fn ToUint32(cx: &mut crate::context::JSContext, v: HandleValue) -> Result<u32, ()> {
753    convert_from_int32::<u32>(cx, v, ToUint32Slow)
754}
755
756#[inline]
757pub unsafe fn ToUint16(cx: &mut crate::context::JSContext, v: HandleValue) -> Result<u16, ()> {
758    convert_from_int32::<u16>(cx, v, ToUint16Slow)
759}
760
761#[inline]
762pub unsafe fn ToInt64(cx: &mut crate::context::JSContext, v: HandleValue) -> Result<i64, ()> {
763    convert_from_int32::<i64>(cx, v, ToInt64Slow)
764}
765
766#[inline]
767pub unsafe fn ToUint64(cx: &mut crate::context::JSContext, v: HandleValue) -> Result<u64, ()> {
768    convert_from_int32::<u64>(cx, v, ToUint64Slow)
769}
770
771#[inline]
772pub unsafe fn ToString(cx: &mut crate::context::JSContext, v: HandleValue) -> *mut JSString {
773    let val = *v.ptr.as_ptr();
774    if val.is_string() {
775        return val.to_string();
776    }
777
778    ToStringSlow(cx, v.into())
779}
780
781pub unsafe fn ToWindowProxyIfWindow(obj: *mut JSObject) -> *mut JSObject {
782    if is_window(obj) {
783        ToWindowProxyIfWindowSlow(obj)
784    } else {
785        obj
786    }
787}
788
789pub unsafe extern "C" fn report_warning(_cx: *mut JSContext, report: *mut JSErrorReport) {
790    fn latin1_to_string(bytes: &[u8]) -> String {
791        bytes
792            .iter()
793            .map(|c| char::from_u32(*c as u32).unwrap())
794            .collect()
795    }
796
797    let fnptr = (*report)._base.filename.data_;
798    let fname = if !fnptr.is_null() {
799        let c_str = CStr::from_ptr(fnptr);
800        latin1_to_string(c_str.to_bytes())
801    } else {
802        "none".to_string()
803    };
804
805    let lineno = (*report)._base.lineno;
806    let column = (*report)._base.column._base;
807
808    let msg_ptr = (*report)._base.message_.data_ as *const u8;
809    let msg_len = (0usize..)
810        .find(|&i| *msg_ptr.offset(i as isize) == 0)
811        .unwrap();
812    let msg_slice = slice::from_raw_parts(msg_ptr, msg_len);
813    let msg = str::from_utf8_unchecked(msg_slice);
814
815    warn!("Warning at {}:{}:{}: {}\n", fname, lineno, column, msg);
816}
817
818pub struct IdVector(*mut PersistentRootedIdVector);
819
820impl IdVector {
821    pub fn new(cx: &mut crate::context::JSContext) -> IdVector {
822        let vector = unsafe { CreateRootedIdVector(cx) };
823        assert!(!vector.is_null());
824        IdVector(vector)
825    }
826
827    pub fn handle_mut(&mut self) -> RawMutableHandleIdVector {
828        RawMutableHandleIdVector {
829            ptr: unsafe { GetIdVectorAddress(self.0) },
830        }
831    }
832}
833
834impl Drop for IdVector {
835    fn drop(&mut self) {
836        unsafe { DestroyRootedIdVector(self.0) }
837    }
838}
839
840impl Deref for IdVector {
841    type Target = [jsid];
842
843    fn deref(&self) -> &[jsid] {
844        unsafe {
845            let mut length = 0;
846            let pointer = SliceRootedIdVector(self.0, &mut length);
847            slice::from_raw_parts(pointer, length)
848        }
849    }
850}
851
852/// Defines methods on `obj`. The last entry of `methods` must contain zeroed
853/// memory.
854///
855/// # Failures
856///
857/// Returns `Err` on JSAPI failure.
858///
859/// # Panics
860///
861/// Panics if the last entry of `methods` does not contain zeroed memory.
862///
863/// # Safety
864///
865/// - This function calls into unaudited C++ code.
866pub unsafe fn define_methods(
867    cx: &mut crate::context::JSContext,
868    obj: HandleObject,
869    methods: &'static [JSFunctionSpec],
870) -> Result<(), ()> {
871    assert!({
872        match methods.last() {
873            Some(&JSFunctionSpec {
874                name,
875                call,
876                nargs,
877                flags,
878                selfHostedName,
879            }) => {
880                name.string_.is_null()
881                    && call.is_zeroed()
882                    && nargs == 0
883                    && flags == 0
884                    && selfHostedName.is_null()
885            }
886            None => false,
887        }
888    });
889
890    JS_DefineFunctions(cx, obj, methods.as_ptr()).to_result()
891}
892
893/// Defines attributes on `obj`. The last entry of `properties` must contain
894/// zeroed memory.
895///
896/// # Failures
897///
898/// Returns `Err` on JSAPI failure.
899///
900/// # Panics
901///
902/// Panics if the last entry of `properties` does not contain zeroed memory.
903///
904/// # Safety
905///
906/// - This function calls into unaudited C++ code.
907pub unsafe fn define_properties(
908    cx: &mut crate::context::JSContext,
909    obj: HandleObject,
910    properties: &'static [JSPropertySpec],
911) -> Result<(), ()> {
912    assert!({
913        match properties.last() {
914            Some(spec) => spec.is_zeroed(),
915            None => false,
916        }
917    });
918
919    JS_DefineProperties(cx, obj, properties.as_ptr()).to_result()
920}
921
922static SIMPLE_GLOBAL_CLASS_OPS: JSClassOps = JSClassOps {
923    addProperty: None,
924    delProperty: None,
925    enumerate: Some(JS_EnumerateStandardClasses),
926    newEnumerate: None,
927    resolve: Some(JS_ResolveStandardClass),
928    mayResolve: Some(JS_MayResolveStandardClass),
929    finalize: None,
930    call: None,
931    construct: None,
932    trace: Some(JS_GlobalObjectTraceHook),
933};
934
935/// This is a simple `JSClass` for global objects, primarily intended for tests.
936pub static SIMPLE_GLOBAL_CLASS: JSClass = JSClass {
937    name: c"Global".as_ptr(),
938    flags: JSCLASS_IS_GLOBAL
939        | ((JSCLASS_GLOBAL_SLOT_COUNT & JSCLASS_RESERVED_SLOTS_MASK)
940            << JSCLASS_RESERVED_SLOTS_SHIFT),
941    cOps: &SIMPLE_GLOBAL_CLASS_OPS as *const JSClassOps,
942    spec: ptr::null(),
943    ext: ptr::null(),
944    oOps: ptr::null(),
945};
946
947#[inline]
948unsafe fn get_object_group(obj: *mut JSObject) -> *mut BaseShape {
949    assert!(!obj.is_null());
950    let obj = obj as *mut Object;
951    (*(*obj).shape).base
952}
953
954#[inline]
955pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
956    (*get_object_group(obj)).clasp as *const _
957}
958
959#[inline]
960pub unsafe fn get_object_realm(obj: *mut JSObject) -> *mut Realm {
961    (*get_object_group(obj)).realm
962}
963
964#[inline]
965pub unsafe fn get_context_realm(cx: *mut JSContext) -> *mut Realm {
966    let cx = cx as *mut RootingContext;
967    (*cx).realm_
968}
969
970#[inline]
971pub fn is_dom_class(class: &JSClass) -> bool {
972    class.flags & JSCLASS_IS_DOMJSCLASS != 0
973}
974
975#[inline]
976pub unsafe fn is_dom_object(obj: *mut JSObject) -> bool {
977    is_dom_class(&*get_object_class(obj))
978}
979
980#[inline]
981pub unsafe fn is_window(obj: *mut JSObject) -> bool {
982    (*get_object_class(obj)).flags & JSCLASS_IS_GLOBAL != 0 && IsWindowSlow(obj)
983}
984
985#[inline]
986pub unsafe fn try_to_outerize(mut rval: MutableHandleValue) {
987    let obj = rval.to_object();
988    if is_window(obj) {
989        let obj = ToWindowProxyIfWindowSlow(obj);
990        assert!(!obj.is_null());
991        rval.set(ObjectValue(&mut *obj));
992    }
993}
994
995#[inline]
996pub unsafe fn try_to_outerize_object(mut rval: MutableHandleObject) {
997    if is_window(*rval) {
998        let obj = ToWindowProxyIfWindowSlow(*rval);
999        assert!(!obj.is_null());
1000        rval.set(obj);
1001    }
1002}
1003
1004#[inline]
1005pub unsafe fn maybe_wrap_object(cx: &mut crate::context::JSContext, mut obj: MutableHandleObject) {
1006    if get_object_realm(*obj) != get_context_realm(cx.raw_cx()) {
1007        assert!(JS_WrapObject(cx, obj.reborrow()));
1008    }
1009    try_to_outerize_object(obj);
1010}
1011
1012#[inline]
1013pub unsafe fn maybe_wrap_object_value(
1014    cx: &mut crate::context::JSContext,
1015    rval: MutableHandleValue,
1016) {
1017    assert!(rval.is_object());
1018    let obj = rval.to_object();
1019    if get_object_realm(obj) != get_context_realm(cx.raw_cx()) {
1020        assert!(JS_WrapValue(cx, rval));
1021    } else if is_dom_object(obj) {
1022        try_to_outerize(rval);
1023    }
1024}
1025
1026#[inline]
1027pub fn maybe_wrap_object_or_null_value(
1028    cx: &mut crate::context::JSContext,
1029    rval: MutableHandleValue,
1030) {
1031    assert!(rval.is_object_or_null());
1032    if !rval.is_null() {
1033        unsafe { maybe_wrap_object_value(cx, rval) };
1034    }
1035}
1036
1037#[inline]
1038pub fn maybe_wrap_value(cx: &mut crate::context::JSContext, rval: MutableHandleValue) {
1039    if rval.is_string() {
1040        assert!(unsafe { JS_WrapValue(cx, rval) });
1041    } else if rval.is_object() {
1042        unsafe { maybe_wrap_object_value(cx, rval) };
1043    }
1044}
1045
1046/// Like `JSJitInfo::new_bitfield_1`, but usable in `const` contexts.
1047#[macro_export]
1048macro_rules! new_jsjitinfo_bitfield_1 {
1049    (
1050        $type_: expr,
1051        $aliasSet_: expr,
1052        $returnType_: expr,
1053        $isInfallible: expr,
1054        $isMovable: expr,
1055        $isEliminatable: expr,
1056        $isAlwaysInSlot: expr,
1057        $isLazilyCachedInSlot: expr,
1058        $isTypedMethod: expr,
1059        $slotIndex: expr,
1060    ) => {
1061        0 | (($type_ as u32) << 0u32)
1062            | (($aliasSet_ as u32) << 4u32)
1063            | (($returnType_ as u32) << 8u32)
1064            | (($isInfallible as u32) << 16u32)
1065            | (($isMovable as u32) << 17u32)
1066            | (($isEliminatable as u32) << 18u32)
1067            | (($isAlwaysInSlot as u32) << 19u32)
1068            | (($isLazilyCachedInSlot as u32) << 20u32)
1069            | (($isTypedMethod as u32) << 21u32)
1070            | (($slotIndex as u32) << 22u32)
1071    };
1072}
1073
1074#[derive(Debug, Default)]
1075pub struct ScriptedCaller {
1076    pub filename: String,
1077    pub line: u32,
1078    pub col: u32,
1079}
1080
1081pub fn describe_scripted_caller(cx: &crate::context::JSContext) -> Result<ScriptedCaller, ()> {
1082    let mut filename = String::new();
1083    let mut line = 0;
1084    let mut col = 0;
1085    if unsafe {
1086        !wrappers2::DescribeScriptedCaller(
1087            cx,
1088            Some(fill_string_callback),
1089            &raw mut filename as *mut c_void,
1090            &mut line,
1091            &mut col,
1092        )
1093    } {
1094        return Err(());
1095    }
1096    Ok(ScriptedCaller {
1097        filename,
1098        line,
1099        col,
1100    })
1101}
1102
1103pub struct ErrorInfo {
1104    pub message: String,
1105    pub filename: String,
1106    pub line: u32,
1107    pub col: u32,
1108}
1109
1110/// Append a C string of `len` bytes into a Rust String pointed to by target
1111///
1112/// The C string is expected to be `utf-8` encoded. If it is not, the conversion will be lossy.
1113///
1114/// ## Safety
1115///
1116/// - `target` must be a valid `*mut String`, with no other active borrows.
1117/// - `ptr` must point to a C char* array of `len` bytes.
1118unsafe extern "C" fn fill_string_callback(ptr: *const c_char, len: usize, target: *mut c_void) {
1119    assert!(!ptr.is_null());
1120    let target = &mut *(target as *mut String);
1121
1122    let slice = String::from_utf8_lossy(slice::from_raw_parts(ptr as *const u8, len));
1123    target.push_str(&slice);
1124}
1125
1126/// Retrieve error info from the pending exception stack, by clearing it.
1127/// Return None if there isn't one or if it is a warning.
1128pub fn error_info_from_exception_stack(
1129    cx: &mut crate::context::JSContext,
1130    rval: MutableHandleValue,
1131) -> Option<ErrorInfo> {
1132    let mut message = String::new();
1133    let mut filename = String::new();
1134
1135    let mut line = 0;
1136    let mut col = 0;
1137
1138    unsafe {
1139        if !wrappers2::PendingExceptionStackInfo(
1140            cx,
1141            Some(fill_string_callback),
1142            &raw mut message as *mut c_void,
1143            &raw mut filename as *mut c_void,
1144            &mut line,
1145            &mut col,
1146            rval,
1147        ) {
1148            return None;
1149        }
1150    }
1151
1152    Some(ErrorInfo {
1153        message,
1154        filename,
1155        line,
1156        col,
1157    })
1158}
1159
1160pub struct CapturedJSStack<'a> {
1161    cx: &'a mut crate::context::JSContext,
1162    stack: RootedGuard<'a, *mut JSObject>,
1163}
1164
1165impl<'a> CapturedJSStack<'a> {
1166    pub unsafe fn new(
1167        cx: &'a mut crate::context::JSContext,
1168        mut guard: RootedGuard<'a, *mut JSObject>,
1169        max_frame_count: Option<u32>,
1170    ) -> Option<Self> {
1171        let ref mut stack_capture = MaybeUninit::uninit();
1172        match max_frame_count {
1173            None => JS_StackCapture_AllFrames(stack_capture.as_mut_ptr()),
1174            Some(count) => JS_StackCapture_MaxFrames(count, stack_capture.as_mut_ptr()),
1175        };
1176        let ref mut stack_capture = stack_capture.assume_init();
1177
1178        if !CaptureCurrentStack(cx, guard.handle_mut(), stack_capture, HandleObject::null()) {
1179            None
1180        } else {
1181            Some(CapturedJSStack { cx, stack: guard })
1182        }
1183    }
1184
1185    pub fn as_string(&mut self, indent: Option<usize>, format: StackFormat) -> Option<String> {
1186        let stack_handle = self.stack.handle();
1187        rooted!(&in(self.cx) let mut js_string = ptr::null_mut::<JSString>());
1188
1189        unsafe {
1190            if !BuildStackString(
1191                self.cx,
1192                ptr::null_mut(),
1193                stack_handle,
1194                js_string.handle_mut(),
1195                indent.unwrap_or(0),
1196                format,
1197            ) {
1198                return None;
1199            }
1200
1201            Some(crate::conversions::jsstr_to_string(
1202                self.cx,
1203                NonNull::new(js_string.get())?,
1204            ))
1205        }
1206    }
1207
1208    /// Executes the provided closure for each frame on the js stack
1209    pub fn for_each_stack_frame<F>(&mut self, mut f: F)
1210    where
1211        F: FnMut(&mut crate::context::JSContext, Handle<*mut JSObject>),
1212    {
1213        rooted!(&in(self.cx) let mut current_element = self.stack.clone());
1214        rooted!(&in(self.cx) let mut next_element = ptr::null_mut::<JSObject>());
1215
1216        loop {
1217            f(self.cx, current_element.handle());
1218
1219            unsafe {
1220                let result = wrappers2::GetSavedFrameParent(
1221                    self.cx,
1222                    ptr::null_mut(),
1223                    current_element.handle(),
1224                    next_element.handle_mut(),
1225                    jsapi::SavedFrameSelfHosted::Include,
1226                );
1227
1228                if result != SavedFrameResult::Ok || next_element.is_null() {
1229                    return;
1230                }
1231            }
1232            current_element.set(next_element.get());
1233        }
1234    }
1235}
1236
1237#[macro_export]
1238macro_rules! capture_stack {
1239    (&in($cx:expr) let $name:ident = with max depth($max_frame_count:expr)) => {
1240        rooted!(&in($cx) let mut __obj = ::std::ptr::null_mut());
1241        let $name = $crate::rust::CapturedJSStack::new($cx, __obj, Some($max_frame_count));
1242    };
1243    (&in($cx:expr) let $name:ident ) => {
1244        rooted!(&in($cx) let mut __obj = ::std::ptr::null_mut());
1245        let $name = $crate::rust::CapturedJSStack::new($cx, __obj, None);
1246    }
1247}
1248
1249pub struct EnvironmentChain {
1250    chain: *mut crate::jsapi::JS::EnvironmentChain,
1251}
1252
1253impl EnvironmentChain {
1254    pub fn new(
1255        cx: &mut crate::context::JSContext,
1256        support_unscopeables: crate::jsapi::JS::SupportUnscopables,
1257    ) -> Self {
1258        Self {
1259            chain: unsafe { wrappers2::NewEnvironmentChain(cx, support_unscopeables) },
1260        }
1261    }
1262
1263    pub fn append(&self, obj: *mut JSObject) {
1264        unsafe {
1265            assert!(crate::jsapi::glue::AppendToEnvironmentChain(
1266                self.chain, obj
1267            ));
1268        }
1269    }
1270
1271    pub fn get(&self) -> *mut crate::jsapi::JS::EnvironmentChain {
1272        self.chain
1273    }
1274}
1275
1276impl Drop for EnvironmentChain {
1277    fn drop(&mut self) {
1278        unsafe {
1279            crate::jsapi::glue::DeleteEnvironmentChain(self.chain);
1280        }
1281    }
1282}
1283
1284impl<'a> Handle<'a, StackGCVector<JSVal, js::TempAllocPolicy>> {
1285    pub fn at(&'a self, index: u32) -> Option<Handle<'a, JSVal>> {
1286        if index >= self.len() {
1287            return None;
1288        }
1289        let handle =
1290            unsafe { Handle::from_marked_location(StackGCVectorValueAtIndex(*self, index)) };
1291        Some(handle)
1292    }
1293
1294    pub fn len(&self) -> u32 {
1295        unsafe { StackGCVectorValueLength(*self) }
1296    }
1297}
1298
1299impl<'a> Handle<'a, StackGCVector<*mut JSString, js::TempAllocPolicy>> {
1300    pub fn at(&'a self, index: u32) -> Option<Handle<'a, *mut JSString>> {
1301        if index >= self.len() {
1302            return None;
1303        }
1304        let handle =
1305            unsafe { Handle::from_marked_location(StackGCVectorStringAtIndex(*self, index)) };
1306        Some(handle)
1307    }
1308
1309    pub fn len(&self) -> u32 {
1310        unsafe { StackGCVectorStringLength(*self) }
1311    }
1312}
1313
1314#[derive(Clone, Copy, Debug)]
1315pub enum ForOfIterationFailure<OtherError> {
1316    ValueIsNotIterable,
1317    /// There is a pending exception
1318    JSFailed,
1319    Other(OtherError),
1320}
1321
1322impl<OtherError> From<OtherError> for ForOfIterationFailure<OtherError> {
1323    fn from(value: OtherError) -> Self {
1324        Self::Other(value)
1325    }
1326}
1327
1328/// Helper for running `for .. of` iteration from rust.
1329///
1330/// If `Ok()` is returned then the iteration completed without unexpected failures.
1331///
1332/// The callback returns `Err()` to indicate a pending exception or `Ok()` containing a boolean
1333/// value that is `true` if the iterator should continue iterating.
1334pub fn for_of<Context, Callback, OtherError>(
1335    cx: &mut Context,
1336    iterable: HandleValue<'_>,
1337    mut callback: Callback,
1338) -> Result<(), ForOfIterationFailure<OtherError>>
1339where
1340    Context: AsMut<crate::context::JSContext>,
1341    Callback: FnMut(
1342        &mut Context,
1343        HandleValue<'_>,
1344    ) -> Result<ControlFlow<()>, ForOfIterationFailure<OtherError>>,
1345{
1346    let raw_cx = unsafe { cx.as_mut().raw_cx() };
1347
1348    // Depending on the version of LLVM in use, bindgen can end up including
1349    // a padding field in the ForOfIterator. To support multiple versions of
1350    // LLVM that may not have the same fields as a result, we create an empty
1351    // iterator instance and initialize a non-empty instance using the empty
1352    // instance as a base value.
1353    #[allow(unused_variables)]
1354    let zero = unsafe { mem::zeroed() };
1355    let mut iterator = jsapi::ForOfIterator {
1356        cx_: raw_cx,
1357        iterator: RootedObject::new_unrooted(ptr::null_mut()),
1358        nextMethod: RootedValue::new_unrooted(JSVal { asBits_: 0 }),
1359        index: ::std::u32::MAX, // NOT_ARRAY
1360        ..zero
1361    };
1362
1363    // This code would benefit from https://github.com/rust-lang/rust/issues/144426
1364    struct IteratorRootGuard<'a> {
1365        inner: &'a mut jsapi::ForOfIterator,
1366    }
1367
1368    impl<'a> Drop for IteratorRootGuard<'a> {
1369        fn drop(&mut self) {
1370            // SAFETY: These values won't be used anymore
1371            unsafe {
1372                self.inner.iterator.remove_from_root_stack();
1373                self.inner.nextMethod.remove_from_root_stack();
1374            }
1375        }
1376    }
1377    let guard = IteratorRootGuard {
1378        inner: &mut iterator,
1379    };
1380    let iterator = &mut *guard.inner;
1381
1382    unsafe {
1383        RootedObject::add_to_root_stack(&raw mut iterator.iterator, raw_cx);
1384        RootedValue::add_to_root_stack(&raw mut iterator.nextMethod, raw_cx);
1385    }
1386
1387    let success = unsafe {
1388        iterator.init(
1389            iterable.into_handle(),
1390            jsapi::ForOfIterator_NonIterableBehavior::AllowNonIterable,
1391        )
1392    };
1393    if !success {
1394        return Err(ForOfIterationFailure::JSFailed);
1395    }
1396    if !iterator.is_iterable() {
1397        return Err(ForOfIterationFailure::ValueIsNotIterable);
1398    }
1399
1400    let mut done = false;
1401    rooted!(&in(cx.as_mut()) let mut value = UndefinedValue());
1402    loop {
1403        if !unsafe { iterator.next(value.handle_mut().into(), &mut done) } {
1404            return Err(ForOfIterationFailure::JSFailed);
1405        }
1406
1407        if done {
1408            break;
1409        }
1410
1411        if callback(cx, value.handle())?.is_break() {
1412            break;
1413        }
1414    }
1415
1416    Ok(())
1417}
1418
1419/// Helper for creating a `BorrowedErrorReport`
1420///
1421/// `BorrowedErrorReport` needs to be pinned and rooted.
1422pub fn borrowed_error_report<F, R>(cx: &crate::context::JSContext, f: F) -> R
1423where
1424    F: FnOnce(&crate::context::JSContext, &mut BorrowedErrorReport) -> R,
1425{
1426    let mut report = BorrowedErrorReport {
1427        owner_: Rooted::new_unrooted(ptr::null_mut()),
1428        report_: ptr::null_mut(),
1429    };
1430    unsafe {
1431        Rooted::add_to_root_stack(&mut report.owner_, cx.raw_cx_no_gc());
1432    }
1433    let result = f(cx, &mut report);
1434    unsafe {
1435        report.owner_.remove_from_root_stack();
1436    }
1437    result
1438}
1439
1440/// Wrappers for JSAPI/glue methods that accept lifetimed [crate::rust::Handle] and [crate::rust::MutableHandle] arguments and [crate::context::JSContext]
1441pub mod wrappers2 {
1442    macro_rules! wrap {
1443        // The invocation of @inner has the following form:
1444        // @inner (input args) <> (arg signture accumulator) <> (arg expr accumulator) <> unparsed tokens
1445        // when `unparsed tokens == \eps`, accumulator contains the final result
1446        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: Handle<$gentype:ty>, $($rest:tt)*) => {
1447            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: Handle<$gentype>) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1448        };
1449        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandle<$gentype:ty>, $($rest:tt)*) => {
1450            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandle<$gentype>) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1451        };
1452        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: Handle, $($rest:tt)*) => {
1453            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: Handle) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1454        };
1455        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandle, $($rest:tt)*) => {
1456            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandle) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1457        };
1458        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleFunction , $($rest:tt)*) => {
1459            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleFunction) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1460        };
1461        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleId , $($rest:tt)*) => {
1462            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleId) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1463        };
1464        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleObject , $($rest:tt)*) => {
1465            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleObject) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1466        };
1467        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleScript , $($rest:tt)*) => {
1468            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleScript) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1469        };
1470        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleString , $($rest:tt)*) => {
1471            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleString) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1472        };
1473        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleSymbol , $($rest:tt)*) => {
1474            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleSymbol) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1475        };
1476        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: HandleValue , $($rest:tt)*) => {
1477            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: HandleValue) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1478        };
1479        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleFunction , $($rest:tt)*) => {
1480            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleFunction) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1481        };
1482        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleId , $($rest:tt)*) => {
1483            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleId) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1484        };
1485        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleObject , $($rest:tt)*) => {
1486            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleObject) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1487        };
1488        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleScript , $($rest:tt)*) => {
1489            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleScript) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1490        };
1491        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleString , $($rest:tt)*) => {
1492            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleString) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1493        };
1494        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleSymbol , $($rest:tt)*) => {
1495            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleSymbol) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1496        };
1497        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: MutableHandleValue , $($rest:tt)*) => {
1498            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: MutableHandleValue) <> ($($arg_expr_acc,)* $arg.into(),) <> $($rest)*);
1499        };
1500        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: &mut JSContext , $($rest:tt)*) => {
1501            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: &mut JSContext) <> ($($arg_expr_acc,)* $arg.raw_cx(),) <> $($rest)*);
1502        };
1503        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: &JSContext , $($rest:tt)*) => {
1504            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: &JSContext) <> ($($arg_expr_acc,)* $arg.raw_cx_no_gc(),) <> $($rest)*);
1505        };
1506        // functions that take *const AutoRequireNoGC already have &JSContext, so we can remove this mareker argument
1507        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: *const AutoRequireNoGC , $($rest:tt)*) => {
1508            wrap!(@inner $saved <> ($($arg_sig_acc)*) <> ($($arg_expr_acc,)* ::std::ptr::null(),) <> $($rest)*);
1509        };
1510        (@inner $saved:tt <> ($($arg_sig_acc:tt)*) <> ($($arg_expr_acc:expr,)*) <> $arg:ident: $type:ty, $($rest:tt)*) => {
1511            wrap!(@inner $saved <> ($($arg_sig_acc)* , $arg: $type) <> ($($arg_expr_acc,)* $arg,) <> $($rest)*);
1512        };
1513        (@inner ($module:tt: $func_name:ident -> $outtype:ty) <> (, $($args:tt)*) <> ($($argexprs:expr,)*) <> ) => {
1514            #[inline]
1515            pub unsafe fn $func_name($($args)*) -> $outtype {
1516                $module::$func_name($($argexprs),*)
1517            }
1518        };
1519        ($module:tt: pub fn $func_name:ident($($args:tt)*) -> $outtype:ty) => {
1520            wrap!(@inner ($module: $func_name -> $outtype) <> () <> () <> $($args)* ,);
1521        };
1522        ($module:tt: pub fn $func_name:ident($($args:tt)*)) => {
1523            wrap!($module: pub fn $func_name($($args)*) -> ());
1524        }
1525    }
1526
1527    use super::*;
1528    use super::{
1529        Handle, HandleFunction, HandleId, HandleObject, HandleScript, HandleString, HandleValue,
1530        HandleValueArray, MutableHandle, MutableHandleId, MutableHandleObject, MutableHandleString,
1531        MutableHandleValue, StackGCVector,
1532    };
1533    use crate::context::JSContext;
1534    use crate::glue;
1535    use crate::glue::*;
1536    use crate::jsapi;
1537    use crate::jsapi::js::TempAllocPolicy;
1538    use crate::jsapi::mozilla::Utf8Unit;
1539    use crate::jsapi::mozilla::*;
1540    use crate::jsapi::BigInt;
1541    use crate::jsapi::CallArgs;
1542    use crate::jsapi::CloneDataPolicy;
1543    use crate::jsapi::CompartmentTransplantCallback;
1544    use crate::jsapi::ESClass;
1545    use crate::jsapi::EnvironmentChain;
1546    use crate::jsapi::ExceptionStackBehavior;
1547    use crate::jsapi::ForOfIterator;
1548    use crate::jsapi::ForOfIterator_NonIterableBehavior;
1549    use crate::jsapi::FrontendContext;
1550    use crate::jsapi::HandleObjectVector;
1551    use crate::jsapi::InstantiateOptions;
1552    use crate::jsapi::JSClass;
1553    use crate::jsapi::JSErrorReport;
1554    use crate::jsapi::JSExnType;
1555    use crate::jsapi::JSFunctionSpecWithHelp;
1556    use crate::jsapi::JSJitInfo;
1557    use crate::jsapi::JSONParseHandler;
1558    use crate::jsapi::JSONWriteCallback;
1559    use crate::jsapi::JSPrincipals;
1560    use crate::jsapi::JSPropertySpec;
1561    use crate::jsapi::JSPropertySpec_Name;
1562    use crate::jsapi::JSProtoKey;
1563    use crate::jsapi::JSScript;
1564    use crate::jsapi::JSStructuredCloneData;
1565    use crate::jsapi::JSType;
1566    use crate::jsapi::Latin1Char;
1567    use crate::jsapi::ModuleErrorBehaviour;
1568    use crate::jsapi::ModuleType;
1569    use crate::jsapi::MutableHandleIdVector;
1570    use crate::jsapi::PromiseState;
1571    use crate::jsapi::PromiseUserInputEventHandlingState;
1572    use crate::jsapi::PropertyKey;
1573    use crate::jsapi::ReadOnlyCompileOptions;
1574    use crate::jsapi::Realm;
1575    use crate::jsapi::RealmOptions;
1576    use crate::jsapi::RefPtr;
1577    use crate::jsapi::RegExpFlags;
1578    use crate::jsapi::ScriptEnvironmentPreparer_Closure;
1579    use crate::jsapi::SourceText;
1580    use crate::jsapi::StackCapture;
1581    use crate::jsapi::Stencil;
1582    use crate::jsapi::StructuredCloneScope;
1583    use crate::jsapi::Symbol;
1584    use crate::jsapi::SymbolCode;
1585    use crate::jsapi::TaggedColumnNumberOneOrigin;
1586    use crate::jsapi::TwoByteChars;
1587    use crate::jsapi::UniqueChars;
1588    use crate::jsapi::Value;
1589    use crate::jsapi::WasmModule;
1590    use crate::jsapi::*;
1591    use crate::jsapi::{ElementAdder, IsArrayAnswer, PropertyDescriptor};
1592    use crate::jsapi::{JSFunction, JSNative, JSObject, JSString};
1593    use crate::jsapi::{
1594        JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
1595    };
1596    use crate::jsapi::{MallocSizeOf, ObjectOpResult, ObjectPrivateVisitor, TabSizes};
1597    use crate::jsapi::{SavedFrameResult, SavedFrameSelfHosted};
1598    include!("jsapi2_wrappers.in.rs");
1599    include!("glue2_wrappers.in.rs");
1600
1601    #[inline]
1602    pub unsafe fn SetPropertyIgnoringNamedGetter(
1603        cx: &mut JSContext,
1604        obj: HandleObject,
1605        id: HandleId,
1606        v: HandleValue,
1607        receiver: HandleValue,
1608        ownDesc: Option<Handle<PropertyDescriptor>>,
1609        result: *mut ObjectOpResult,
1610    ) -> bool {
1611        if let Some(ownDesc) = ownDesc {
1612            let ownDesc = ownDesc.into();
1613            jsapi::SetPropertyIgnoringNamedGetter(
1614                cx.raw_cx(),
1615                obj.into(),
1616                id.into(),
1617                v.into(),
1618                receiver.into(),
1619                &raw const ownDesc,
1620                result,
1621            )
1622        } else {
1623            jsapi::SetPropertyIgnoringNamedGetter(
1624                cx.raw_cx(),
1625                obj.into(),
1626                id.into(),
1627                v.into(),
1628                receiver.into(),
1629                ptr::null(),
1630                result,
1631            )
1632        }
1633    }
1634}