1use std::cell::Cell;
8use std::char;
9use std::default::Default;
10use std::ffi::{CStr, CString};
11use std::marker::PhantomData;
12use std::mem::MaybeUninit;
13use std::ops::{Deref, DerefMut};
14use std::ptr::{self, NonNull};
15use std::slice;
16use std::str;
17use std::sync::atomic::{AtomicU32, Ordering};
18use std::sync::{Arc, Mutex, RwLock};
19
20use crate::consts::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_RESERVED_SLOTS_MASK};
21use crate::consts::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
22use crate::conversions::jsstr_to_string;
23use crate::default_heapsize;
24pub use crate::gc::*;
25use crate::glue::AppendToRootedObjectVector;
26use crate::glue::{CreateRootedIdVector, CreateRootedObjectVector};
27use crate::glue::{
28 DeleteCompileOptions, DeleteRootedObjectVector, DescribeScriptedCaller, DestroyRootedIdVector,
29};
30use crate::glue::{DeleteJSAutoStructuredCloneBuffer, NewJSAutoStructuredCloneBuffer};
31use crate::glue::{
32 GetIdVectorAddress, GetObjectVectorAddress, NewCompileOptions, SliceRootedIdVector,
33};
34use crate::jsapi;
35use crate::jsapi::glue::{DeleteRealmOptions, JS_Init, JS_NewRealmOptions};
36use crate::jsapi::js::frontend::InitialStencilAndDelazifications;
37use crate::jsapi::mozilla::Utf8Unit;
38use crate::jsapi::shadow::BaseShape;
39use crate::jsapi::HandleObjectVector as RawHandleObjectVector;
40use crate::jsapi::HandleValue as RawHandleValue;
41use crate::jsapi::JS_AddExtraGCRootsTracer;
42use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
43use crate::jsapi::{already_AddRefed, jsid};
44use crate::jsapi::{BuildStackString, CaptureCurrentStack, StackFormat};
45use crate::jsapi::{Evaluate2, HandleValueArray, StencilRelease};
46use crate::jsapi::{InitSelfHostedCode, IsWindowSlow};
47use crate::jsapi::{
48 JSAutoRealm, JS_SetGCParameter, JS_SetNativeStackQuota, JS_WrapObject, JS_WrapValue,
49};
50use crate::jsapi::{JSAutoStructuredCloneBuffer, JSStructuredCloneCallbacks, StructuredCloneScope};
51use crate::jsapi::{JSClass, JSClassOps, JSContext, Realm, JSCLASS_RESERVED_SLOTS_SHIFT};
52use crate::jsapi::{JSErrorReport, JSFunctionSpec, JSGCParamKey};
53use crate::jsapi::{JSObject, JSPropertySpec, JSRuntime};
54use crate::jsapi::{JSString, Object, PersistentRootedIdVector};
55use crate::jsapi::{JS_DefineFunctions, JS_DefineProperties, JS_DestroyContext, JS_ShutDown};
56use crate::jsapi::{JS_EnumerateStandardClasses, JS_GetRuntime, JS_GlobalObjectTraceHook};
57use crate::jsapi::{JS_MayResolveStandardClass, JS_NewContext, JS_ResolveStandardClass};
58use crate::jsapi::{JS_RequestInterruptCallback, JS_RequestInterruptCallbackCanWait};
59use crate::jsapi::{JS_StackCapture_AllFrames, JS_StackCapture_MaxFrames};
60use crate::jsapi::{PersistentRootedObjectVector, ReadOnlyCompileOptions, RootingContext};
61use crate::jsapi::{SetWarningReporter, SourceText, ToBooleanSlow};
62use crate::jsapi::{ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow};
63use crate::jsapi::{ToUint32Slow, ToUint64Slow, ToWindowProxyIfWindowSlow};
64use crate::jsval::ObjectValue;
65use crate::panic::maybe_resume_unwind;
66use log::{debug, warn};
67use mozjs_sys::jsapi::JS::SavedFrameResult;
68pub use mozjs_sys::jsgc::{GCMethods, IntoHandle, IntoMutableHandle};
69pub use mozjs_sys::trace::Traceable as Trace;
70
71use crate::rooted;
72
73const STACK_QUOTA: usize = 128 * 8 * 1024;
77
78const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
104
105const TRUSTED_SCRIPT_BUFFER: usize = 8 * 12800;
107
108trait ToResult {
109 fn to_result(self) -> Result<(), ()>;
110}
111
112impl ToResult for bool {
113 fn to_result(self) -> Result<(), ()> {
114 if self {
115 Ok(())
116 } else {
117 Err(())
118 }
119 }
120}
121
122pub struct RealmOptions(*mut jsapi::RealmOptions);
126
127impl Deref for RealmOptions {
128 type Target = jsapi::RealmOptions;
129 fn deref(&self) -> &Self::Target {
130 unsafe { &*self.0 }
131 }
132}
133
134impl DerefMut for RealmOptions {
135 fn deref_mut(&mut self) -> &mut Self::Target {
136 unsafe { &mut *self.0 }
137 }
138}
139
140impl Default for RealmOptions {
141 fn default() -> RealmOptions {
142 RealmOptions(unsafe { JS_NewRealmOptions() })
143 }
144}
145
146impl Drop for RealmOptions {
147 fn drop(&mut self) {
148 unsafe { DeleteRealmOptions(self.0) }
149 }
150}
151
152thread_local!(static CONTEXT: Cell<*mut JSContext> = Cell::new(ptr::null_mut()));
153
154#[derive(PartialEq)]
155enum EngineState {
156 Uninitialized,
157 InitFailed,
158 Initialized,
159 ShutDown,
160}
161
162static ENGINE_STATE: Mutex<EngineState> = Mutex::new(EngineState::Uninitialized);
163
164#[derive(Debug)]
165pub enum JSEngineError {
166 AlreadyInitialized,
167 AlreadyShutDown,
168 InitFailed,
169}
170
171pub struct JSEngine {
175 outstanding_handles: Arc<AtomicU32>,
177 marker: PhantomData<*mut ()>,
179}
180
181pub struct JSEngineHandle(Arc<AtomicU32>);
182
183impl Clone for JSEngineHandle {
184 fn clone(&self) -> JSEngineHandle {
185 self.0.fetch_add(1, Ordering::SeqCst);
186 JSEngineHandle(self.0.clone())
187 }
188}
189
190impl Drop for JSEngineHandle {
191 fn drop(&mut self) {
192 self.0.fetch_sub(1, Ordering::SeqCst);
193 }
194}
195
196impl JSEngine {
197 pub fn init() -> Result<JSEngine, JSEngineError> {
199 let mut state = ENGINE_STATE.lock().unwrap();
200 match *state {
201 EngineState::Initialized => return Err(JSEngineError::AlreadyInitialized),
202 EngineState::InitFailed => return Err(JSEngineError::InitFailed),
203 EngineState::ShutDown => return Err(JSEngineError::AlreadyShutDown),
204 EngineState::Uninitialized => (),
205 }
206 if unsafe { !JS_Init() } {
207 *state = EngineState::InitFailed;
208 Err(JSEngineError::InitFailed)
209 } else {
210 *state = EngineState::Initialized;
211 Ok(JSEngine {
212 outstanding_handles: Arc::new(AtomicU32::new(0)),
213 marker: PhantomData,
214 })
215 }
216 }
217
218 pub fn can_shutdown(&self) -> bool {
219 self.outstanding_handles.load(Ordering::SeqCst) == 0
220 }
221
222 pub fn handle(&self) -> JSEngineHandle {
224 self.outstanding_handles.fetch_add(1, Ordering::SeqCst);
225 JSEngineHandle(self.outstanding_handles.clone())
226 }
227}
228
229impl Drop for JSEngine {
232 fn drop(&mut self) {
233 let mut state = ENGINE_STATE.lock().unwrap();
234 if *state == EngineState::Initialized {
235 assert_eq!(
236 self.outstanding_handles.load(Ordering::SeqCst),
237 0,
238 "There are outstanding JS engine handles"
239 );
240 *state = EngineState::ShutDown;
241 unsafe {
242 JS_ShutDown();
243 }
244 }
245 }
246}
247
248pub fn transform_str_to_source_text(source: &str) -> SourceText<Utf8Unit> {
249 SourceText {
250 units_: source.as_ptr() as *const _,
251 length_: source.len() as u32,
252 ownsUnits_: false,
253 _phantom_0: PhantomData,
254 }
255}
256
257pub fn transform_u16_to_source_text(source: &[u16]) -> SourceText<u16> {
258 SourceText {
259 units_: source.as_ptr() as *const _,
260 length_: source.len() as u32,
261 ownsUnits_: false,
262 _phantom_0: PhantomData,
263 }
264}
265
266pub struct ParentRuntime {
270 parent: *mut JSRuntime,
272 engine: JSEngineHandle,
274 children_of_parent: Arc<()>,
276}
277unsafe impl Send for ParentRuntime {}
278
279pub struct Runtime {
281 cx: *mut JSContext,
283 engine: JSEngineHandle,
285 _parent_child_count: Option<Arc<()>>,
290 outstanding_children: Arc<()>,
296 thread_safe_handle: Arc<RwLock<Option<*mut JSContext>>>,
300}
301
302impl Runtime {
303 pub fn get() -> Option<NonNull<JSContext>> {
305 let cx = CONTEXT.with(|context| context.get());
306 NonNull::new(cx)
307 }
308
309 pub fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
311 ThreadSafeJSContext(self.thread_safe_handle.clone())
312 }
313
314 pub fn new(engine: JSEngineHandle) -> Runtime {
316 unsafe { Self::create(engine, None) }
317 }
318
319 pub fn prepare_for_new_child(&self) -> ParentRuntime {
325 ParentRuntime {
326 parent: self.rt(),
327 engine: self.engine.clone(),
328 children_of_parent: self.outstanding_children.clone(),
329 }
330 }
331
332 pub unsafe fn create_with_parent(parent: ParentRuntime) -> Runtime {
340 Self::create(parent.engine.clone(), Some(parent))
341 }
342
343 unsafe fn create(engine: JSEngineHandle, parent: Option<ParentRuntime>) -> Runtime {
344 let parent_runtime = parent.as_ref().map_or(ptr::null_mut(), |r| r.parent);
345 let js_context = JS_NewContext(default_heapsize + (ChunkSize as u32), parent_runtime);
346 assert!(!js_context.is_null());
347
348 JS_SetGCParameter(js_context, JSGCParamKey::JSGC_MAX_BYTES, u32::MAX);
354
355 JS_AddExtraGCRootsTracer(js_context, Some(trace_traceables), ptr::null_mut());
356
357 JS_SetNativeStackQuota(
358 js_context,
359 STACK_QUOTA,
360 STACK_QUOTA - SYSTEM_CODE_BUFFER,
361 STACK_QUOTA - SYSTEM_CODE_BUFFER - TRUSTED_SCRIPT_BUFFER,
362 );
363
364 CONTEXT.with(|context| {
365 assert!(context.get().is_null());
366 context.set(js_context);
367 });
368
369 #[cfg(target_pointer_width = "64")]
370 let cache = crate::jsapi::__BindgenOpaqueArray::<u64, 2>::default();
371 #[cfg(target_pointer_width = "32")]
372 let cache = crate::jsapi::__BindgenOpaqueArray::<u32, 2>::default();
373
374 InitSelfHostedCode(js_context, cache, None);
375
376 SetWarningReporter(js_context, Some(report_warning));
377
378 Runtime {
379 engine,
380 _parent_child_count: parent.map(|p| p.children_of_parent),
381 cx: js_context,
382 outstanding_children: Arc::new(()),
383 thread_safe_handle: Arc::new(RwLock::new(Some(js_context))),
384 }
385 }
386
387 pub fn rt(&self) -> *mut JSRuntime {
389 unsafe { JS_GetRuntime(self.cx) }
390 }
391
392 pub fn cx(&self) -> *mut JSContext {
394 self.cx
395 }
396
397 pub fn evaluate_script(
398 &self,
399 glob: HandleObject,
400 script: &str,
401 rval: MutableHandleValue,
402 options: CompileOptionsWrapper,
403 ) -> Result<(), ()> {
404 debug!(
405 "Evaluating script from {} with content {}",
406 options.filename(),
407 script
408 );
409
410 let _ac = JSAutoRealm::new(self.cx(), glob.get());
411
412 unsafe {
413 let mut source = transform_str_to_source_text(&script);
414 if !Evaluate2(self.cx(), options.ptr, &mut source, rval.into()) {
415 debug!("...err!");
416 maybe_resume_unwind();
417 Err(())
418 } else {
419 debug!("...ok!");
422 Ok(())
423 }
424 }
425 }
426
427 pub fn new_compile_options(&self, filename: &str, line: u32) -> CompileOptionsWrapper {
428 unsafe { CompileOptionsWrapper::new(self.cx(), filename, line) }
430 }
431}
432
433impl Drop for Runtime {
434 fn drop(&mut self) {
435 self.thread_safe_handle.write().unwrap().take();
436 assert!(
437 Arc::get_mut(&mut self.outstanding_children).is_some(),
438 "This runtime still has live children."
439 );
440 unsafe {
441 JS_DestroyContext(self.cx);
442
443 CONTEXT.with(|context| {
444 assert_eq!(context.get(), self.cx);
445 context.set(ptr::null_mut());
446 });
447 }
448 }
449}
450
451#[derive(Clone)]
455pub struct ThreadSafeJSContext(Arc<RwLock<Option<*mut JSContext>>>);
456
457unsafe impl Send for ThreadSafeJSContext {}
458unsafe impl Sync for ThreadSafeJSContext {}
459
460impl ThreadSafeJSContext {
461 pub fn request_interrupt_callback(&self) {
465 if let Some(&cx) = self.0.read().unwrap().as_ref() {
466 unsafe {
467 JS_RequestInterruptCallback(cx);
468 }
469 }
470 }
471
472 pub fn request_interrupt_callback_can_wait(&self) {
476 if let Some(&cx) = self.0.read().unwrap().as_ref() {
477 unsafe {
478 JS_RequestInterruptCallbackCanWait(cx);
479 }
480 }
481 }
482}
483
484const ChunkShift: usize = 20;
485const ChunkSize: usize = 1 << ChunkShift;
486
487#[cfg(target_pointer_width = "32")]
488const ChunkLocationOffset: usize = ChunkSize - 2 * 4 - 8;
489
490pub struct RootedObjectVectorWrapper {
494 pub ptr: *mut PersistentRootedObjectVector,
495}
496
497impl RootedObjectVectorWrapper {
498 pub fn new(cx: *mut JSContext) -> RootedObjectVectorWrapper {
499 RootedObjectVectorWrapper {
500 ptr: unsafe { CreateRootedObjectVector(cx) },
501 }
502 }
503
504 pub fn append(&self, obj: *mut JSObject) -> bool {
505 unsafe { AppendToRootedObjectVector(self.ptr, obj) }
506 }
507
508 pub fn handle(&self) -> RawHandleObjectVector {
509 RawHandleObjectVector {
510 ptr: unsafe { GetObjectVectorAddress(self.ptr) },
511 }
512 }
513}
514
515impl Drop for RootedObjectVectorWrapper {
516 fn drop(&mut self) {
517 unsafe { DeleteRootedObjectVector(self.ptr) }
518 }
519}
520
521pub struct CompileOptionsWrapper {
522 pub ptr: *mut ReadOnlyCompileOptions,
523 filename: CString,
524}
525
526impl CompileOptionsWrapper {
527 pub unsafe fn new(cx: *mut JSContext, filename: &str, line: u32) -> Self {
531 let filename = CString::new(filename.as_bytes()).unwrap();
532 let ptr = NewCompileOptions(cx, filename.as_ptr(), line);
533 assert!(!ptr.is_null());
534 Self { ptr, filename }
535 }
536
537 pub fn filename(&self) -> &str {
538 self.filename.to_str().expect("Guaranteed by new")
539 }
540
541 pub fn set_introduction_type(&mut self, introduction_type: &'static CStr) {
542 unsafe {
543 (*self.ptr)._base.introductionType = introduction_type.as_ptr();
544 }
545 }
546}
547
548impl Drop for CompileOptionsWrapper {
549 fn drop(&mut self) {
550 unsafe { DeleteCompileOptions(self.ptr) }
551 }
552}
553
554pub struct JSAutoStructuredCloneBufferWrapper {
555 ptr: NonNull<JSAutoStructuredCloneBuffer>,
556}
557
558impl JSAutoStructuredCloneBufferWrapper {
559 pub unsafe fn new(
560 scope: StructuredCloneScope,
561 callbacks: *const JSStructuredCloneCallbacks,
562 ) -> Self {
563 let raw_ptr = NewJSAutoStructuredCloneBuffer(scope, callbacks);
564 Self {
565 ptr: NonNull::new(raw_ptr).unwrap(),
566 }
567 }
568
569 pub fn as_raw_ptr(&self) -> *mut JSAutoStructuredCloneBuffer {
570 self.ptr.as_ptr()
571 }
572}
573
574impl Drop for JSAutoStructuredCloneBufferWrapper {
575 fn drop(&mut self) {
576 unsafe {
577 DeleteJSAutoStructuredCloneBuffer(self.ptr.as_ptr());
578 }
579 }
580}
581
582pub struct Stencil {
583 inner: already_AddRefed<InitialStencilAndDelazifications>,
584}
585
586impl Drop for Stencil {
590 fn drop(&mut self) {
591 if self.is_null() {
592 return;
593 }
594 unsafe {
595 StencilRelease(self.inner.mRawPtr);
596 }
597 }
598}
599
600impl Deref for Stencil {
601 type Target = *mut InitialStencilAndDelazifications;
602
603 fn deref(&self) -> &Self::Target {
604 &self.inner.mRawPtr
605 }
606}
607
608impl Stencil {
609 pub fn is_null(&self) -> bool {
610 self.inner.mRawPtr.is_null()
611 }
612}
613
614#[inline]
618pub unsafe fn ToBoolean(v: HandleValue) -> bool {
619 let val = *v.ptr;
620
621 if val.is_boolean() {
622 return val.to_boolean();
623 }
624
625 if val.is_int32() {
626 return val.to_int32() != 0;
627 }
628
629 if val.is_null_or_undefined() {
630 return false;
631 }
632
633 if val.is_double() {
634 let d = val.to_double();
635 return !d.is_nan() && d != 0f64;
636 }
637
638 if val.is_symbol() {
639 return true;
640 }
641
642 ToBooleanSlow(v.into())
643}
644
645#[inline]
646pub unsafe fn ToNumber(cx: *mut JSContext, v: HandleValue) -> Result<f64, ()> {
647 let val = *v.ptr;
648 if val.is_number() {
649 return Ok(val.to_number());
650 }
651
652 let mut out = Default::default();
653 if ToNumberSlow(cx, v.into_handle(), &mut out) {
654 Ok(out)
655 } else {
656 Err(())
657 }
658}
659
660#[inline]
661unsafe fn convert_from_int32<T: Default + Copy>(
662 cx: *mut JSContext,
663 v: HandleValue,
664 conv_fn: unsafe extern "C" fn(*mut JSContext, RawHandleValue, *mut T) -> bool,
665) -> Result<T, ()> {
666 let val = *v.ptr;
667 if val.is_int32() {
668 let intval: i64 = val.to_int32() as i64;
669 let intval = *(&intval as *const i64 as *const T);
671 return Ok(intval);
672 }
673
674 let mut out = Default::default();
675 if conv_fn(cx, v.into(), &mut out) {
676 Ok(out)
677 } else {
678 Err(())
679 }
680}
681
682#[inline]
683pub unsafe fn ToInt32(cx: *mut JSContext, v: HandleValue) -> Result<i32, ()> {
684 convert_from_int32::<i32>(cx, v, ToInt32Slow)
685}
686
687#[inline]
688pub unsafe fn ToUint32(cx: *mut JSContext, v: HandleValue) -> Result<u32, ()> {
689 convert_from_int32::<u32>(cx, v, ToUint32Slow)
690}
691
692#[inline]
693pub unsafe fn ToUint16(cx: *mut JSContext, v: HandleValue) -> Result<u16, ()> {
694 convert_from_int32::<u16>(cx, v, ToUint16Slow)
695}
696
697#[inline]
698pub unsafe fn ToInt64(cx: *mut JSContext, v: HandleValue) -> Result<i64, ()> {
699 convert_from_int32::<i64>(cx, v, ToInt64Slow)
700}
701
702#[inline]
703pub unsafe fn ToUint64(cx: *mut JSContext, v: HandleValue) -> Result<u64, ()> {
704 convert_from_int32::<u64>(cx, v, ToUint64Slow)
705}
706
707#[inline]
708pub unsafe fn ToString(cx: *mut JSContext, v: HandleValue) -> *mut JSString {
709 let val = *v.ptr;
710 if val.is_string() {
711 return val.to_string();
712 }
713
714 ToStringSlow(cx, v.into())
715}
716
717pub unsafe fn ToWindowProxyIfWindow(obj: *mut JSObject) -> *mut JSObject {
718 if is_window(obj) {
719 ToWindowProxyIfWindowSlow(obj)
720 } else {
721 obj
722 }
723}
724
725pub unsafe extern "C" fn report_warning(_cx: *mut JSContext, report: *mut JSErrorReport) {
726 fn latin1_to_string(bytes: &[u8]) -> String {
727 bytes
728 .iter()
729 .map(|c| char::from_u32(*c as u32).unwrap())
730 .collect()
731 }
732
733 let fnptr = (*report)._base.filename.data_;
734 let fname = if !fnptr.is_null() {
735 let c_str = CStr::from_ptr(fnptr);
736 latin1_to_string(c_str.to_bytes())
737 } else {
738 "none".to_string()
739 };
740
741 let lineno = (*report)._base.lineno;
742 let column = (*report)._base.column._base;
743
744 let msg_ptr = (*report)._base.message_.data_ as *const u8;
745 let msg_len = (0usize..)
746 .find(|&i| *msg_ptr.offset(i as isize) == 0)
747 .unwrap();
748 let msg_slice = slice::from_raw_parts(msg_ptr, msg_len);
749 let msg = str::from_utf8_unchecked(msg_slice);
750
751 warn!("Warning at {}:{}:{}: {}\n", fname, lineno, column, msg);
752}
753
754pub struct IdVector(*mut PersistentRootedIdVector);
755
756impl IdVector {
757 pub unsafe fn new(cx: *mut JSContext) -> IdVector {
758 let vector = CreateRootedIdVector(cx);
759 assert!(!vector.is_null());
760 IdVector(vector)
761 }
762
763 pub fn handle_mut(&mut self) -> RawMutableHandleIdVector {
764 RawMutableHandleIdVector {
765 ptr: unsafe { GetIdVectorAddress(self.0) },
766 }
767 }
768}
769
770impl Drop for IdVector {
771 fn drop(&mut self) {
772 unsafe { DestroyRootedIdVector(self.0) }
773 }
774}
775
776impl Deref for IdVector {
777 type Target = [jsid];
778
779 fn deref(&self) -> &[jsid] {
780 unsafe {
781 let mut length = 0;
782 let pointer = SliceRootedIdVector(self.0, &mut length);
783 slice::from_raw_parts(pointer, length)
784 }
785 }
786}
787
788pub unsafe fn define_methods(
804 cx: *mut JSContext,
805 obj: HandleObject,
806 methods: &'static [JSFunctionSpec],
807) -> Result<(), ()> {
808 assert!({
809 match methods.last() {
810 Some(&JSFunctionSpec {
811 name,
812 call,
813 nargs,
814 flags,
815 selfHostedName,
816 }) => {
817 name.string_.is_null()
818 && call.is_zeroed()
819 && nargs == 0
820 && flags == 0
821 && selfHostedName.is_null()
822 }
823 None => false,
824 }
825 });
826
827 JS_DefineFunctions(cx, obj.into(), methods.as_ptr()).to_result()
828}
829
830pub unsafe fn define_properties(
846 cx: *mut JSContext,
847 obj: HandleObject,
848 properties: &'static [JSPropertySpec],
849) -> Result<(), ()> {
850 assert!({
851 match properties.last() {
852 Some(spec) => spec.is_zeroed(),
853 None => false,
854 }
855 });
856
857 JS_DefineProperties(cx, obj.into(), properties.as_ptr()).to_result()
858}
859
860static SIMPLE_GLOBAL_CLASS_OPS: JSClassOps = JSClassOps {
861 addProperty: None,
862 delProperty: None,
863 enumerate: Some(JS_EnumerateStandardClasses),
864 newEnumerate: None,
865 resolve: Some(JS_ResolveStandardClass),
866 mayResolve: Some(JS_MayResolveStandardClass),
867 finalize: None,
868 call: None,
869 construct: None,
870 trace: Some(JS_GlobalObjectTraceHook),
871};
872
873pub static SIMPLE_GLOBAL_CLASS: JSClass = JSClass {
875 name: c"Global".as_ptr(),
876 flags: JSCLASS_IS_GLOBAL
877 | ((JSCLASS_GLOBAL_SLOT_COUNT & JSCLASS_RESERVED_SLOTS_MASK)
878 << JSCLASS_RESERVED_SLOTS_SHIFT),
879 cOps: &SIMPLE_GLOBAL_CLASS_OPS as *const JSClassOps,
880 spec: ptr::null(),
881 ext: ptr::null(),
882 oOps: ptr::null(),
883};
884
885#[inline]
886unsafe fn get_object_group(obj: *mut JSObject) -> *mut BaseShape {
887 assert!(!obj.is_null());
888 let obj = obj as *mut Object;
889 (*(*obj).shape).base
890}
891
892#[inline]
893pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
894 (*get_object_group(obj)).clasp as *const _
895}
896
897#[inline]
898pub unsafe fn get_object_realm(obj: *mut JSObject) -> *mut Realm {
899 (*get_object_group(obj)).realm
900}
901
902#[inline]
903pub unsafe fn get_context_realm(cx: *mut JSContext) -> *mut Realm {
904 let cx = cx as *mut RootingContext;
905 (*cx).realm_
906}
907
908#[inline]
909pub fn is_dom_class(class: &JSClass) -> bool {
910 class.flags & JSCLASS_IS_DOMJSCLASS != 0
911}
912
913#[inline]
914pub unsafe fn is_dom_object(obj: *mut JSObject) -> bool {
915 is_dom_class(&*get_object_class(obj))
916}
917
918#[inline]
919pub unsafe fn is_window(obj: *mut JSObject) -> bool {
920 (*get_object_class(obj)).flags & JSCLASS_IS_GLOBAL != 0 && IsWindowSlow(obj)
921}
922
923#[inline]
924pub unsafe fn try_to_outerize(mut rval: MutableHandleValue) {
925 let obj = rval.to_object();
926 if is_window(obj) {
927 let obj = ToWindowProxyIfWindowSlow(obj);
928 assert!(!obj.is_null());
929 rval.set(ObjectValue(&mut *obj));
930 }
931}
932
933#[inline]
934pub unsafe fn try_to_outerize_object(mut rval: MutableHandleObject) {
935 if is_window(*rval) {
936 let obj = ToWindowProxyIfWindowSlow(*rval);
937 assert!(!obj.is_null());
938 rval.set(obj);
939 }
940}
941
942#[inline]
943pub unsafe fn maybe_wrap_object(cx: *mut JSContext, mut obj: MutableHandleObject) {
944 if get_object_realm(*obj) != get_context_realm(cx) {
945 assert!(JS_WrapObject(cx, obj.reborrow().into()));
946 }
947 try_to_outerize_object(obj);
948}
949
950#[inline]
951pub unsafe fn maybe_wrap_object_value(cx: *mut JSContext, rval: MutableHandleValue) {
952 assert!(rval.is_object());
953 let obj = rval.to_object();
954 if get_object_realm(obj) != get_context_realm(cx) {
955 assert!(JS_WrapValue(cx, rval.into()));
956 } else if is_dom_object(obj) {
957 try_to_outerize(rval);
958 }
959}
960
961#[inline]
962pub unsafe fn maybe_wrap_object_or_null_value(cx: *mut JSContext, rval: MutableHandleValue) {
963 assert!(rval.is_object_or_null());
964 if !rval.is_null() {
965 maybe_wrap_object_value(cx, rval);
966 }
967}
968
969#[inline]
970pub unsafe fn maybe_wrap_value(cx: *mut JSContext, rval: MutableHandleValue) {
971 if rval.is_string() {
972 assert!(JS_WrapValue(cx, rval.into()));
973 } else if rval.is_object() {
974 maybe_wrap_object_value(cx, rval);
975 }
976}
977
978#[macro_export]
980macro_rules! new_jsjitinfo_bitfield_1 {
981 (
982 $type_: expr,
983 $aliasSet_: expr,
984 $returnType_: expr,
985 $isInfallible: expr,
986 $isMovable: expr,
987 $isEliminatable: expr,
988 $isAlwaysInSlot: expr,
989 $isLazilyCachedInSlot: expr,
990 $isTypedMethod: expr,
991 $slotIndex: expr,
992 ) => {
993 0 | (($type_ as u32) << 0u32)
994 | (($aliasSet_ as u32) << 4u32)
995 | (($returnType_ as u32) << 8u32)
996 | (($isInfallible as u32) << 16u32)
997 | (($isMovable as u32) << 17u32)
998 | (($isEliminatable as u32) << 18u32)
999 | (($isAlwaysInSlot as u32) << 19u32)
1000 | (($isLazilyCachedInSlot as u32) << 20u32)
1001 | (($isTypedMethod as u32) << 21u32)
1002 | (($slotIndex as u32) << 22u32)
1003 };
1004}
1005
1006#[derive(Debug, Default)]
1007pub struct ScriptedCaller {
1008 pub filename: String,
1009 pub line: u32,
1010 pub col: u32,
1011}
1012
1013pub unsafe fn describe_scripted_caller(cx: *mut JSContext) -> Result<ScriptedCaller, ()> {
1014 let mut buf = [0; 1024];
1015 let mut line = 0;
1016 let mut col = 0;
1017 if !DescribeScriptedCaller(cx, buf.as_mut_ptr(), buf.len(), &mut line, &mut col) {
1018 return Err(());
1019 }
1020 let filename = CStr::from_ptr((&buf) as *const _ as *const _);
1021 Ok(ScriptedCaller {
1022 filename: String::from_utf8_lossy(filename.to_bytes()).into_owned(),
1023 line,
1024 col,
1025 })
1026}
1027
1028pub struct CapturedJSStack<'a> {
1029 cx: *mut JSContext,
1030 stack: RootedGuard<'a, *mut JSObject>,
1031}
1032
1033impl<'a> CapturedJSStack<'a> {
1034 pub unsafe fn new(
1035 cx: *mut JSContext,
1036 mut guard: RootedGuard<'a, *mut JSObject>,
1037 max_frame_count: Option<u32>,
1038 ) -> Option<Self> {
1039 let ref mut stack_capture = MaybeUninit::uninit();
1040 match max_frame_count {
1041 None => JS_StackCapture_AllFrames(stack_capture.as_mut_ptr()),
1042 Some(count) => JS_StackCapture_MaxFrames(count, stack_capture.as_mut_ptr()),
1043 };
1044 let ref mut stack_capture = stack_capture.assume_init();
1045
1046 if !CaptureCurrentStack(
1047 cx,
1048 guard.handle_mut().raw(),
1049 stack_capture,
1050 HandleObject::null().into(),
1051 ) {
1052 None
1053 } else {
1054 Some(CapturedJSStack { cx, stack: guard })
1055 }
1056 }
1057
1058 pub fn as_string(&self, indent: Option<usize>, format: StackFormat) -> Option<String> {
1059 unsafe {
1060 let stack_handle = self.stack.handle();
1061 rooted!(in(self.cx) let mut js_string = ptr::null_mut::<JSString>());
1062 let mut string_handle = js_string.handle_mut();
1063
1064 if !BuildStackString(
1065 self.cx,
1066 ptr::null_mut(),
1067 stack_handle.into(),
1068 string_handle.raw(),
1069 indent.unwrap_or(0),
1070 format,
1071 ) {
1072 return None;
1073 }
1074
1075 Some(jsstr_to_string(self.cx, NonNull::new(string_handle.get())?))
1076 }
1077 }
1078
1079 pub fn for_each_stack_frame<F>(&self, mut f: F)
1081 where
1082 F: FnMut(Handle<*mut JSObject>),
1083 {
1084 rooted!(in(self.cx) let mut current_element = self.stack.clone());
1085 rooted!(in(self.cx) let mut next_element = ptr::null_mut::<JSObject>());
1086
1087 loop {
1088 f(current_element.handle());
1089
1090 unsafe {
1091 let result = jsapi::GetSavedFrameParent(
1092 self.cx,
1093 ptr::null_mut(),
1094 current_element.handle().into_handle(),
1095 next_element.handle_mut().into_handle_mut(),
1096 jsapi::SavedFrameSelfHosted::Include,
1097 );
1098
1099 if result != SavedFrameResult::Ok || next_element.is_null() {
1100 return;
1101 }
1102 }
1103 current_element.set(next_element.get());
1104 }
1105 }
1106}
1107
1108#[macro_export]
1109macro_rules! capture_stack {
1110 (in($cx:expr) let $name:ident = with max depth($max_frame_count:expr)) => {
1111 rooted!(in($cx) let mut __obj = ::std::ptr::null_mut());
1112 let $name = $crate::rust::CapturedJSStack::new($cx, __obj, Some($max_frame_count));
1113 };
1114 (in($cx:expr) let $name:ident ) => {
1115 rooted!(in($cx) let mut __obj = ::std::ptr::null_mut());
1116 let $name = $crate::rust::CapturedJSStack::new($cx, __obj, None);
1117 }
1118}
1119
1120pub struct EnvironmentChain {
1121 chain: *mut crate::jsapi::JS::EnvironmentChain,
1122}
1123
1124impl EnvironmentChain {
1125 pub fn new(
1126 cx: *mut JSContext,
1127 support_unscopeables: crate::jsapi::JS::SupportUnscopables,
1128 ) -> Self {
1129 unsafe {
1130 Self {
1131 chain: crate::jsapi::glue::NewEnvironmentChain(cx, support_unscopeables),
1132 }
1133 }
1134 }
1135
1136 pub fn append(&self, obj: *mut JSObject) {
1137 unsafe {
1138 assert!(crate::jsapi::glue::AppendToEnvironmentChain(
1139 self.chain, obj
1140 ));
1141 }
1142 }
1143
1144 pub fn get(&self) -> *mut crate::jsapi::JS::EnvironmentChain {
1145 self.chain
1146 }
1147}
1148
1149impl Drop for EnvironmentChain {
1150 fn drop(&mut self) {
1151 unsafe {
1152 crate::jsapi::glue::DeleteEnvironmentChain(self.chain);
1153 }
1154 }
1155}
1156
1157pub mod wrappers {
1159 macro_rules! wrap {
1160 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: *const Handle<$gentype:ty>, $($rest:tt)*) => {
1165 wrap!(@inner $saved <> ($($acc,)* if $arg.is_null() { std::ptr::null() } else { &(*$arg).into() },) <> $($rest)*);
1166 };
1167 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: Handle<$gentype:ty>, $($rest:tt)*) => {
1168 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1169 };
1170 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandle<$gentype:ty>, $($rest:tt)*) => {
1171 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1172 };
1173 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: Handle, $($rest:tt)*) => {
1174 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1175 };
1176 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandle, $($rest:tt)*) => {
1177 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1178 };
1179 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleFunction , $($rest:tt)*) => {
1180 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1181 };
1182 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleId , $($rest:tt)*) => {
1183 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1184 };
1185 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleObject , $($rest:tt)*) => {
1186 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1187 };
1188 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleScript , $($rest:tt)*) => {
1189 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1190 };
1191 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleString , $($rest:tt)*) => {
1192 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1193 };
1194 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleSymbol , $($rest:tt)*) => {
1195 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1196 };
1197 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleValue , $($rest:tt)*) => {
1198 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1199 };
1200 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleFunction , $($rest:tt)*) => {
1201 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1202 };
1203 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleId , $($rest:tt)*) => {
1204 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1205 };
1206 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleObject , $($rest:tt)*) => {
1207 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1208 };
1209 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleScript , $($rest:tt)*) => {
1210 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1211 };
1212 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleString , $($rest:tt)*) => {
1213 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1214 };
1215 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleSymbol , $($rest:tt)*) => {
1216 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1217 };
1218 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleValue , $($rest:tt)*) => {
1219 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1220 };
1221 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: $type:ty, $($rest:tt)*) => {
1222 wrap!(@inner $saved <> ($($acc,)* $arg,) <> $($rest)*);
1223 };
1224 (@inner ($module:tt: $func_name:ident ($($args:tt)*) -> $outtype:ty) <> ($($argexprs:expr,)*) <> ) => {
1225 #[inline]
1226 pub unsafe fn $func_name($($args)*) -> $outtype {
1227 $module::$func_name($($argexprs),*)
1228 }
1229 };
1230 ($module:tt: pub fn $func_name:ident($($args:tt)*) -> $outtype:ty) => {
1231 wrap!(@inner ($module: $func_name ($($args)*) -> $outtype) <> () <> $($args)* ,);
1232 };
1233 ($module:tt: pub fn $func_name:ident($($args:tt)*)) => {
1234 wrap!($module: pub fn $func_name($($args)*) -> ());
1235 }
1236 }
1237
1238 use super::*;
1239 use crate::glue;
1240 use crate::glue::EncodedStringCallback;
1241 use crate::jsapi;
1242 use crate::jsapi::jsid;
1243 use crate::jsapi::mozilla::Utf8Unit;
1244 use crate::jsapi::BigInt;
1245 use crate::jsapi::CallArgs;
1246 use crate::jsapi::CloneDataPolicy;
1247 use crate::jsapi::ColumnNumberOneOrigin;
1248 use crate::jsapi::CompartmentTransplantCallback;
1249 use crate::jsapi::EnvironmentChain;
1250 use crate::jsapi::JSONParseHandler;
1251 use crate::jsapi::Latin1Char;
1252 use crate::jsapi::PropertyKey;
1253 use crate::jsapi::TaggedColumnNumberOneOrigin;
1254 use crate::jsapi::ESClass;
1256 use crate::jsapi::ExceptionStackBehavior;
1257 use crate::jsapi::ForOfIterator;
1258 use crate::jsapi::ForOfIterator_NonIterableBehavior;
1259 use crate::jsapi::HandleObjectVector;
1260 use crate::jsapi::InstantiateOptions;
1261 use crate::jsapi::JSClass;
1262 use crate::jsapi::JSErrorReport;
1263 use crate::jsapi::JSExnType;
1264 use crate::jsapi::JSFunctionSpecWithHelp;
1265 use crate::jsapi::JSJitInfo;
1266 use crate::jsapi::JSONWriteCallback;
1267 use crate::jsapi::JSPrincipals;
1268 use crate::jsapi::JSPropertySpec;
1269 use crate::jsapi::JSPropertySpec_Name;
1270 use crate::jsapi::JSProtoKey;
1271 use crate::jsapi::JSScript;
1272 use crate::jsapi::JSStructuredCloneData;
1273 use crate::jsapi::JSType;
1274 use crate::jsapi::ModuleErrorBehaviour;
1275 use crate::jsapi::ModuleType;
1276 use crate::jsapi::MutableHandleIdVector;
1277 use crate::jsapi::PromiseState;
1278 use crate::jsapi::PromiseUserInputEventHandlingState;
1279 use crate::jsapi::ReadOnlyCompileOptions;
1280 use crate::jsapi::Realm;
1281 use crate::jsapi::RefPtr;
1282 use crate::jsapi::RegExpFlags;
1283 use crate::jsapi::ScriptEnvironmentPreparer_Closure;
1284 use crate::jsapi::SourceText;
1285 use crate::jsapi::StackCapture;
1286 use crate::jsapi::Stencil;
1287 use crate::jsapi::StructuredCloneScope;
1288 use crate::jsapi::Symbol;
1289 use crate::jsapi::SymbolCode;
1290 use crate::jsapi::TranscodeBuffer;
1291 use crate::jsapi::TwoByteChars;
1292 use crate::jsapi::UniqueChars;
1293 use crate::jsapi::Value;
1294 use crate::jsapi::WasmModule;
1295 use crate::jsapi::{ElementAdder, IsArrayAnswer, PropertyDescriptor};
1296 use crate::jsapi::{JSContext, JSFunction, JSNative, JSObject, JSString};
1297 use crate::jsapi::{
1298 JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
1299 };
1300 use crate::jsapi::{MallocSizeOf, ObjectOpResult, ObjectPrivateVisitor, TabSizes};
1301 use crate::jsapi::{SavedFrameResult, SavedFrameSelfHosted};
1302 include!("jsapi_wrappers.in.rs");
1303 include!("glue_wrappers.in.rs");
1304}