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 self::wrappers::{
21 StackGCVectorStringAtIndex, StackGCVectorStringLength, StackGCVectorValueAtIndex,
22 StackGCVectorValueLength,
23};
24use crate::consts::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_RESERVED_SLOTS_MASK};
25use crate::consts::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
26use crate::conversions::jsstr_to_string;
27use crate::default_heapsize;
28pub use crate::gc::*;
29use crate::glue::AppendToRootedObjectVector;
30use crate::glue::{CreateRootedIdVector, CreateRootedObjectVector};
31use crate::glue::{
32 DeleteCompileOptions, DeleteRootedObjectVector, DescribeScriptedCaller, DestroyRootedIdVector,
33};
34use crate::glue::{DeleteJSAutoStructuredCloneBuffer, NewJSAutoStructuredCloneBuffer};
35use crate::glue::{
36 GetIdVectorAddress, GetObjectVectorAddress, NewCompileOptions, SliceRootedIdVector,
37};
38use crate::jsapi;
39use crate::jsapi::glue::{DeleteRealmOptions, JS_Init, JS_NewRealmOptions};
40use crate::jsapi::js;
41use crate::jsapi::js::frontend::InitialStencilAndDelazifications;
42use crate::jsapi::mozilla::Utf8Unit;
43use crate::jsapi::shadow::BaseShape;
44use crate::jsapi::HandleObjectVector as RawHandleObjectVector;
45use crate::jsapi::HandleValue as RawHandleValue;
46use crate::jsapi::JS_AddExtraGCRootsTracer;
47use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
48use crate::jsapi::{already_AddRefed, jsid};
49use crate::jsapi::{BuildStackString, CaptureCurrentStack, StackFormat};
50use crate::jsapi::{Evaluate2, HandleValueArray, StencilRelease};
51use crate::jsapi::{InitSelfHostedCode, IsWindowSlow};
52use crate::jsapi::{
53 JSAutoRealm, JS_SetGCParameter, JS_SetNativeStackQuota, JS_WrapObject, JS_WrapValue,
54};
55use crate::jsapi::{JSAutoStructuredCloneBuffer, JSStructuredCloneCallbacks, StructuredCloneScope};
56use crate::jsapi::{JSClass, JSClassOps, JSContext, Realm, JSCLASS_RESERVED_SLOTS_SHIFT};
57use crate::jsapi::{JSErrorReport, JSFunctionSpec, JSGCParamKey};
58use crate::jsapi::{JSObject, JSPropertySpec, JSRuntime};
59use crate::jsapi::{JSString, Object, PersistentRootedIdVector};
60use crate::jsapi::{JS_DefineFunctions, JS_DefineProperties, JS_DestroyContext, JS_ShutDown};
61use crate::jsapi::{JS_EnumerateStandardClasses, JS_GetRuntime, JS_GlobalObjectTraceHook};
62use crate::jsapi::{JS_MayResolveStandardClass, JS_NewContext, JS_ResolveStandardClass};
63use crate::jsapi::{JS_RequestInterruptCallback, JS_RequestInterruptCallbackCanWait};
64use crate::jsapi::{JS_StackCapture_AllFrames, JS_StackCapture_MaxFrames};
65use crate::jsapi::{PersistentRootedObjectVector, ReadOnlyCompileOptions, RootingContext};
66use crate::jsapi::{SetWarningReporter, SourceText, ToBooleanSlow};
67use crate::jsapi::{ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow};
68use crate::jsapi::{ToUint32Slow, ToUint64Slow, ToWindowProxyIfWindowSlow};
69use crate::jsval::{JSVal, ObjectValue};
70use crate::panic::maybe_resume_unwind;
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
78const STACK_QUOTA: usize = 128 * 8 * 1024;
82
83const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
109
110const 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
127pub 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<*mut JSContext> = Cell::new(ptr::null_mut()));
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
176pub struct JSEngine {
180 outstanding_handles: Arc<AtomicU32>,
182 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 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 pub fn handle(&self) -> JSEngineHandle {
229 self.outstanding_handles.fetch_add(1, Ordering::SeqCst);
230 JSEngineHandle(self.outstanding_handles.clone())
231 }
232}
233
234impl 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
271pub struct ParentRuntime {
275 parent: *mut JSRuntime,
277 engine: JSEngineHandle,
279 children_of_parent: Arc<()>,
281}
282unsafe impl Send for ParentRuntime {}
283
284pub struct Runtime {
286 cx: *mut JSContext,
288 engine: JSEngineHandle,
290 _parent_child_count: Option<Arc<()>>,
295 outstanding_children: Arc<()>,
301 thread_safe_handle: Arc<RwLock<Option<*mut JSContext>>>,
305}
306
307impl Runtime {
308 pub fn get() -> Option<NonNull<JSContext>> {
310 let cx = CONTEXT.with(|context| context.get());
311 NonNull::new(cx)
312 }
313
314 pub fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
316 ThreadSafeJSContext(self.thread_safe_handle.clone())
317 }
318
319 pub fn new(engine: JSEngineHandle) -> Runtime {
321 unsafe { Self::create(engine, None) }
322 }
323
324 pub fn prepare_for_new_child(&self) -> ParentRuntime {
330 ParentRuntime {
331 parent: self.rt(),
332 engine: self.engine.clone(),
333 children_of_parent: self.outstanding_children.clone(),
334 }
335 }
336
337 pub unsafe fn create_with_parent(parent: ParentRuntime) -> Runtime {
345 Self::create(parent.engine.clone(), Some(parent))
346 }
347
348 unsafe fn create(engine: JSEngineHandle, parent: Option<ParentRuntime>) -> Runtime {
349 let parent_runtime = parent.as_ref().map_or(ptr::null_mut(), |r| r.parent);
350 let js_context = JS_NewContext(default_heapsize + (ChunkSize as u32), parent_runtime);
351 assert!(!js_context.is_null());
352
353 JS_SetGCParameter(js_context, JSGCParamKey::JSGC_MAX_BYTES, u32::MAX);
359
360 JS_AddExtraGCRootsTracer(js_context, Some(trace_traceables), ptr::null_mut());
361
362 JS_SetNativeStackQuota(
363 js_context,
364 STACK_QUOTA,
365 STACK_QUOTA - SYSTEM_CODE_BUFFER,
366 STACK_QUOTA - SYSTEM_CODE_BUFFER - TRUSTED_SCRIPT_BUFFER,
367 );
368
369 CONTEXT.with(|context| {
370 assert!(context.get().is_null());
371 context.set(js_context);
372 });
373
374 #[cfg(target_pointer_width = "64")]
375 let cache = crate::jsapi::__BindgenOpaqueArray::<u64, 2>::default();
376 #[cfg(target_pointer_width = "32")]
377 let cache = crate::jsapi::__BindgenOpaqueArray::<u32, 2>::default();
378
379 InitSelfHostedCode(js_context, cache, None);
380
381 SetWarningReporter(js_context, Some(report_warning));
382
383 Runtime {
384 engine,
385 _parent_child_count: parent.map(|p| p.children_of_parent),
386 cx: js_context,
387 outstanding_children: Arc::new(()),
388 thread_safe_handle: Arc::new(RwLock::new(Some(js_context))),
389 }
390 }
391
392 pub fn rt(&self) -> *mut JSRuntime {
394 unsafe { JS_GetRuntime(self.cx) }
395 }
396
397 pub fn cx(&self) -> *mut JSContext {
399 self.cx
400 }
401
402 pub fn evaluate_script(
403 &self,
404 glob: HandleObject,
405 script: &str,
406 rval: MutableHandleValue,
407 options: CompileOptionsWrapper,
408 ) -> Result<(), ()> {
409 debug!(
410 "Evaluating script from {} with content {}",
411 options.filename(),
412 script
413 );
414
415 let _ac = JSAutoRealm::new(self.cx(), glob.get());
416
417 unsafe {
418 let mut source = transform_str_to_source_text(&script);
419 if !Evaluate2(self.cx(), options.ptr, &mut source, rval.into()) {
420 debug!("...err!");
421 maybe_resume_unwind();
422 Err(())
423 } else {
424 debug!("...ok!");
427 Ok(())
428 }
429 }
430 }
431
432 pub fn new_compile_options(&self, filename: &str, line: u32) -> CompileOptionsWrapper {
433 unsafe { CompileOptionsWrapper::new(self.cx(), filename, line) }
435 }
436}
437
438impl Drop for Runtime {
439 fn drop(&mut self) {
440 self.thread_safe_handle.write().unwrap().take();
441 assert!(
442 Arc::get_mut(&mut self.outstanding_children).is_some(),
443 "This runtime still has live children."
444 );
445 unsafe {
446 JS_DestroyContext(self.cx);
447
448 CONTEXT.with(|context| {
449 assert_eq!(context.get(), self.cx);
450 context.set(ptr::null_mut());
451 });
452 }
453 }
454}
455
456#[derive(Clone)]
460pub struct ThreadSafeJSContext(Arc<RwLock<Option<*mut JSContext>>>);
461
462unsafe impl Send for ThreadSafeJSContext {}
463unsafe impl Sync for ThreadSafeJSContext {}
464
465impl ThreadSafeJSContext {
466 pub fn request_interrupt_callback(&self) {
470 if let Some(&cx) = self.0.read().unwrap().as_ref() {
471 unsafe {
472 JS_RequestInterruptCallback(cx);
473 }
474 }
475 }
476
477 pub fn request_interrupt_callback_can_wait(&self) {
481 if let Some(&cx) = self.0.read().unwrap().as_ref() {
482 unsafe {
483 JS_RequestInterruptCallbackCanWait(cx);
484 }
485 }
486 }
487}
488
489const ChunkShift: usize = 20;
490const ChunkSize: usize = 1 << ChunkShift;
491
492#[cfg(target_pointer_width = "32")]
493const ChunkLocationOffset: usize = ChunkSize - 2 * 4 - 8;
494
495pub struct RootedObjectVectorWrapper {
499 pub ptr: *mut PersistentRootedObjectVector,
500}
501
502impl RootedObjectVectorWrapper {
503 pub fn new(cx: *mut JSContext) -> RootedObjectVectorWrapper {
504 RootedObjectVectorWrapper {
505 ptr: unsafe { CreateRootedObjectVector(cx) },
506 }
507 }
508
509 pub fn append(&self, obj: *mut JSObject) -> bool {
510 unsafe { AppendToRootedObjectVector(self.ptr, obj) }
511 }
512
513 pub fn handle(&self) -> RawHandleObjectVector {
514 RawHandleObjectVector {
515 ptr: unsafe { GetObjectVectorAddress(self.ptr) },
516 }
517 }
518}
519
520impl Drop for RootedObjectVectorWrapper {
521 fn drop(&mut self) {
522 unsafe { DeleteRootedObjectVector(self.ptr) }
523 }
524}
525
526pub struct CompileOptionsWrapper {
527 pub ptr: *mut ReadOnlyCompileOptions,
528 filename: CString,
529}
530
531impl CompileOptionsWrapper {
532 pub unsafe fn new(cx: *mut JSContext, filename: &str, line: u32) -> Self {
536 let filename = CString::new(filename.as_bytes()).unwrap();
537 let ptr = NewCompileOptions(cx, filename.as_ptr(), line);
538 assert!(!ptr.is_null());
539 Self { ptr, filename }
540 }
541
542 pub fn filename(&self) -> &str {
543 self.filename.to_str().expect("Guaranteed by new")
544 }
545
546 pub fn set_introduction_type(&mut self, introduction_type: &'static CStr) {
547 unsafe {
548 (*self.ptr)._base.introductionType = introduction_type.as_ptr();
549 }
550 }
551}
552
553impl Drop for CompileOptionsWrapper {
554 fn drop(&mut self) {
555 unsafe { DeleteCompileOptions(self.ptr) }
556 }
557}
558
559pub struct JSAutoStructuredCloneBufferWrapper {
560 ptr: NonNull<JSAutoStructuredCloneBuffer>,
561}
562
563impl JSAutoStructuredCloneBufferWrapper {
564 pub unsafe fn new(
565 scope: StructuredCloneScope,
566 callbacks: *const JSStructuredCloneCallbacks,
567 ) -> Self {
568 let raw_ptr = NewJSAutoStructuredCloneBuffer(scope, callbacks);
569 Self {
570 ptr: NonNull::new(raw_ptr).unwrap(),
571 }
572 }
573
574 pub fn as_raw_ptr(&self) -> *mut JSAutoStructuredCloneBuffer {
575 self.ptr.as_ptr()
576 }
577}
578
579impl Drop for JSAutoStructuredCloneBufferWrapper {
580 fn drop(&mut self) {
581 unsafe {
582 DeleteJSAutoStructuredCloneBuffer(self.ptr.as_ptr());
583 }
584 }
585}
586
587pub struct Stencil {
588 inner: already_AddRefed<InitialStencilAndDelazifications>,
589}
590
591impl Drop for Stencil {
595 fn drop(&mut self) {
596 if self.is_null() {
597 return;
598 }
599 unsafe {
600 StencilRelease(self.inner.mRawPtr);
601 }
602 }
603}
604
605impl Deref for Stencil {
606 type Target = *mut InitialStencilAndDelazifications;
607
608 fn deref(&self) -> &Self::Target {
609 &self.inner.mRawPtr
610 }
611}
612
613impl Stencil {
614 pub fn is_null(&self) -> bool {
615 self.inner.mRawPtr.is_null()
616 }
617}
618
619#[inline]
623pub unsafe fn ToBoolean(v: HandleValue) -> bool {
624 let val = *v.ptr;
625
626 if val.is_boolean() {
627 return val.to_boolean();
628 }
629
630 if val.is_int32() {
631 return val.to_int32() != 0;
632 }
633
634 if val.is_null_or_undefined() {
635 return false;
636 }
637
638 if val.is_double() {
639 let d = val.to_double();
640 return !d.is_nan() && d != 0f64;
641 }
642
643 if val.is_symbol() {
644 return true;
645 }
646
647 ToBooleanSlow(v.into())
648}
649
650#[inline]
651pub unsafe fn ToNumber(cx: *mut JSContext, v: HandleValue) -> Result<f64, ()> {
652 let val = *v.ptr;
653 if val.is_number() {
654 return Ok(val.to_number());
655 }
656
657 let mut out = Default::default();
658 if ToNumberSlow(cx, v.into_handle(), &mut out) {
659 Ok(out)
660 } else {
661 Err(())
662 }
663}
664
665#[inline]
666unsafe fn convert_from_int32<T: Default + Copy>(
667 cx: *mut JSContext,
668 v: HandleValue,
669 conv_fn: unsafe extern "C" fn(*mut JSContext, RawHandleValue, *mut T) -> bool,
670) -> Result<T, ()> {
671 let val = *v.ptr;
672 if val.is_int32() {
673 let intval: i64 = val.to_int32() as i64;
674 let intval = *(&intval as *const i64 as *const T);
676 return Ok(intval);
677 }
678
679 let mut out = Default::default();
680 if conv_fn(cx, v.into(), &mut out) {
681 Ok(out)
682 } else {
683 Err(())
684 }
685}
686
687#[inline]
688pub unsafe fn ToInt32(cx: *mut JSContext, v: HandleValue) -> Result<i32, ()> {
689 convert_from_int32::<i32>(cx, v, ToInt32Slow)
690}
691
692#[inline]
693pub unsafe fn ToUint32(cx: *mut JSContext, v: HandleValue) -> Result<u32, ()> {
694 convert_from_int32::<u32>(cx, v, ToUint32Slow)
695}
696
697#[inline]
698pub unsafe fn ToUint16(cx: *mut JSContext, v: HandleValue) -> Result<u16, ()> {
699 convert_from_int32::<u16>(cx, v, ToUint16Slow)
700}
701
702#[inline]
703pub unsafe fn ToInt64(cx: *mut JSContext, v: HandleValue) -> Result<i64, ()> {
704 convert_from_int32::<i64>(cx, v, ToInt64Slow)
705}
706
707#[inline]
708pub unsafe fn ToUint64(cx: *mut JSContext, v: HandleValue) -> Result<u64, ()> {
709 convert_from_int32::<u64>(cx, v, ToUint64Slow)
710}
711
712#[inline]
713pub unsafe fn ToString(cx: *mut JSContext, v: HandleValue) -> *mut JSString {
714 let val = *v.ptr;
715 if val.is_string() {
716 return val.to_string();
717 }
718
719 ToStringSlow(cx, v.into())
720}
721
722pub unsafe fn ToWindowProxyIfWindow(obj: *mut JSObject) -> *mut JSObject {
723 if is_window(obj) {
724 ToWindowProxyIfWindowSlow(obj)
725 } else {
726 obj
727 }
728}
729
730pub unsafe extern "C" fn report_warning(_cx: *mut JSContext, report: *mut JSErrorReport) {
731 fn latin1_to_string(bytes: &[u8]) -> String {
732 bytes
733 .iter()
734 .map(|c| char::from_u32(*c as u32).unwrap())
735 .collect()
736 }
737
738 let fnptr = (*report)._base.filename.data_;
739 let fname = if !fnptr.is_null() {
740 let c_str = CStr::from_ptr(fnptr);
741 latin1_to_string(c_str.to_bytes())
742 } else {
743 "none".to_string()
744 };
745
746 let lineno = (*report)._base.lineno;
747 let column = (*report)._base.column._base;
748
749 let msg_ptr = (*report)._base.message_.data_ as *const u8;
750 let msg_len = (0usize..)
751 .find(|&i| *msg_ptr.offset(i as isize) == 0)
752 .unwrap();
753 let msg_slice = slice::from_raw_parts(msg_ptr, msg_len);
754 let msg = str::from_utf8_unchecked(msg_slice);
755
756 warn!("Warning at {}:{}:{}: {}\n", fname, lineno, column, msg);
757}
758
759pub struct IdVector(*mut PersistentRootedIdVector);
760
761impl IdVector {
762 pub unsafe fn new(cx: *mut JSContext) -> IdVector {
763 let vector = CreateRootedIdVector(cx);
764 assert!(!vector.is_null());
765 IdVector(vector)
766 }
767
768 pub fn handle_mut(&mut self) -> RawMutableHandleIdVector {
769 RawMutableHandleIdVector {
770 ptr: unsafe { GetIdVectorAddress(self.0) },
771 }
772 }
773}
774
775impl Drop for IdVector {
776 fn drop(&mut self) {
777 unsafe { DestroyRootedIdVector(self.0) }
778 }
779}
780
781impl Deref for IdVector {
782 type Target = [jsid];
783
784 fn deref(&self) -> &[jsid] {
785 unsafe {
786 let mut length = 0;
787 let pointer = SliceRootedIdVector(self.0, &mut length);
788 slice::from_raw_parts(pointer, length)
789 }
790 }
791}
792
793pub unsafe fn define_methods(
809 cx: *mut JSContext,
810 obj: HandleObject,
811 methods: &'static [JSFunctionSpec],
812) -> Result<(), ()> {
813 assert!({
814 match methods.last() {
815 Some(&JSFunctionSpec {
816 name,
817 call,
818 nargs,
819 flags,
820 selfHostedName,
821 }) => {
822 name.string_.is_null()
823 && call.is_zeroed()
824 && nargs == 0
825 && flags == 0
826 && selfHostedName.is_null()
827 }
828 None => false,
829 }
830 });
831
832 JS_DefineFunctions(cx, obj.into(), methods.as_ptr()).to_result()
833}
834
835pub unsafe fn define_properties(
851 cx: *mut JSContext,
852 obj: HandleObject,
853 properties: &'static [JSPropertySpec],
854) -> Result<(), ()> {
855 assert!({
856 match properties.last() {
857 Some(spec) => spec.is_zeroed(),
858 None => false,
859 }
860 });
861
862 JS_DefineProperties(cx, obj.into(), properties.as_ptr()).to_result()
863}
864
865static SIMPLE_GLOBAL_CLASS_OPS: JSClassOps = JSClassOps {
866 addProperty: None,
867 delProperty: None,
868 enumerate: Some(JS_EnumerateStandardClasses),
869 newEnumerate: None,
870 resolve: Some(JS_ResolveStandardClass),
871 mayResolve: Some(JS_MayResolveStandardClass),
872 finalize: None,
873 call: None,
874 construct: None,
875 trace: Some(JS_GlobalObjectTraceHook),
876};
877
878pub static SIMPLE_GLOBAL_CLASS: JSClass = JSClass {
880 name: c"Global".as_ptr(),
881 flags: JSCLASS_IS_GLOBAL
882 | ((JSCLASS_GLOBAL_SLOT_COUNT & JSCLASS_RESERVED_SLOTS_MASK)
883 << JSCLASS_RESERVED_SLOTS_SHIFT),
884 cOps: &SIMPLE_GLOBAL_CLASS_OPS as *const JSClassOps,
885 spec: ptr::null(),
886 ext: ptr::null(),
887 oOps: ptr::null(),
888};
889
890#[inline]
891unsafe fn get_object_group(obj: *mut JSObject) -> *mut BaseShape {
892 assert!(!obj.is_null());
893 let obj = obj as *mut Object;
894 (*(*obj).shape).base
895}
896
897#[inline]
898pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
899 (*get_object_group(obj)).clasp as *const _
900}
901
902#[inline]
903pub unsafe fn get_object_realm(obj: *mut JSObject) -> *mut Realm {
904 (*get_object_group(obj)).realm
905}
906
907#[inline]
908pub unsafe fn get_context_realm(cx: *mut JSContext) -> *mut Realm {
909 let cx = cx as *mut RootingContext;
910 (*cx).realm_
911}
912
913#[inline]
914pub fn is_dom_class(class: &JSClass) -> bool {
915 class.flags & JSCLASS_IS_DOMJSCLASS != 0
916}
917
918#[inline]
919pub unsafe fn is_dom_object(obj: *mut JSObject) -> bool {
920 is_dom_class(&*get_object_class(obj))
921}
922
923#[inline]
924pub unsafe fn is_window(obj: *mut JSObject) -> bool {
925 (*get_object_class(obj)).flags & JSCLASS_IS_GLOBAL != 0 && IsWindowSlow(obj)
926}
927
928#[inline]
929pub unsafe fn try_to_outerize(mut rval: MutableHandleValue) {
930 let obj = rval.to_object();
931 if is_window(obj) {
932 let obj = ToWindowProxyIfWindowSlow(obj);
933 assert!(!obj.is_null());
934 rval.set(ObjectValue(&mut *obj));
935 }
936}
937
938#[inline]
939pub unsafe fn try_to_outerize_object(mut rval: MutableHandleObject) {
940 if is_window(*rval) {
941 let obj = ToWindowProxyIfWindowSlow(*rval);
942 assert!(!obj.is_null());
943 rval.set(obj);
944 }
945}
946
947#[inline]
948pub unsafe fn maybe_wrap_object(cx: *mut JSContext, mut obj: MutableHandleObject) {
949 if get_object_realm(*obj) != get_context_realm(cx) {
950 assert!(JS_WrapObject(cx, obj.reborrow().into()));
951 }
952 try_to_outerize_object(obj);
953}
954
955#[inline]
956pub unsafe fn maybe_wrap_object_value(cx: *mut JSContext, rval: MutableHandleValue) {
957 assert!(rval.is_object());
958 let obj = rval.to_object();
959 if get_object_realm(obj) != get_context_realm(cx) {
960 assert!(JS_WrapValue(cx, rval.into()));
961 } else if is_dom_object(obj) {
962 try_to_outerize(rval);
963 }
964}
965
966#[inline]
967pub unsafe fn maybe_wrap_object_or_null_value(cx: *mut JSContext, rval: MutableHandleValue) {
968 assert!(rval.is_object_or_null());
969 if !rval.is_null() {
970 maybe_wrap_object_value(cx, rval);
971 }
972}
973
974#[inline]
975pub unsafe fn maybe_wrap_value(cx: *mut JSContext, rval: MutableHandleValue) {
976 if rval.is_string() {
977 assert!(JS_WrapValue(cx, rval.into()));
978 } else if rval.is_object() {
979 maybe_wrap_object_value(cx, rval);
980 }
981}
982
983#[macro_export]
985macro_rules! new_jsjitinfo_bitfield_1 {
986 (
987 $type_: expr,
988 $aliasSet_: expr,
989 $returnType_: expr,
990 $isInfallible: expr,
991 $isMovable: expr,
992 $isEliminatable: expr,
993 $isAlwaysInSlot: expr,
994 $isLazilyCachedInSlot: expr,
995 $isTypedMethod: expr,
996 $slotIndex: expr,
997 ) => {
998 0 | (($type_ as u32) << 0u32)
999 | (($aliasSet_ as u32) << 4u32)
1000 | (($returnType_ as u32) << 8u32)
1001 | (($isInfallible as u32) << 16u32)
1002 | (($isMovable as u32) << 17u32)
1003 | (($isEliminatable as u32) << 18u32)
1004 | (($isAlwaysInSlot as u32) << 19u32)
1005 | (($isLazilyCachedInSlot as u32) << 20u32)
1006 | (($isTypedMethod as u32) << 21u32)
1007 | (($slotIndex as u32) << 22u32)
1008 };
1009}
1010
1011#[derive(Debug, Default)]
1012pub struct ScriptedCaller {
1013 pub filename: String,
1014 pub line: u32,
1015 pub col: u32,
1016}
1017
1018pub unsafe fn describe_scripted_caller(cx: *mut JSContext) -> Result<ScriptedCaller, ()> {
1019 let mut buf = [0; 1024];
1020 let mut line = 0;
1021 let mut col = 0;
1022 if !DescribeScriptedCaller(cx, buf.as_mut_ptr(), buf.len(), &mut line, &mut col) {
1023 return Err(());
1024 }
1025 let filename = CStr::from_ptr((&buf) as *const _ as *const _);
1026 Ok(ScriptedCaller {
1027 filename: String::from_utf8_lossy(filename.to_bytes()).into_owned(),
1028 line,
1029 col,
1030 })
1031}
1032
1033pub struct CapturedJSStack<'a> {
1034 cx: *mut JSContext,
1035 stack: RootedGuard<'a, *mut JSObject>,
1036}
1037
1038impl<'a> CapturedJSStack<'a> {
1039 pub unsafe fn new(
1040 cx: *mut JSContext,
1041 mut guard: RootedGuard<'a, *mut JSObject>,
1042 max_frame_count: Option<u32>,
1043 ) -> Option<Self> {
1044 let ref mut stack_capture = MaybeUninit::uninit();
1045 match max_frame_count {
1046 None => JS_StackCapture_AllFrames(stack_capture.as_mut_ptr()),
1047 Some(count) => JS_StackCapture_MaxFrames(count, stack_capture.as_mut_ptr()),
1048 };
1049 let ref mut stack_capture = stack_capture.assume_init();
1050
1051 if !CaptureCurrentStack(
1052 cx,
1053 guard.handle_mut().raw(),
1054 stack_capture,
1055 HandleObject::null().into(),
1056 ) {
1057 None
1058 } else {
1059 Some(CapturedJSStack { cx, stack: guard })
1060 }
1061 }
1062
1063 pub fn as_string(&self, indent: Option<usize>, format: StackFormat) -> Option<String> {
1064 unsafe {
1065 let stack_handle = self.stack.handle();
1066 rooted!(in(self.cx) let mut js_string = ptr::null_mut::<JSString>());
1067 let mut string_handle = js_string.handle_mut();
1068
1069 if !BuildStackString(
1070 self.cx,
1071 ptr::null_mut(),
1072 stack_handle.into(),
1073 string_handle.raw(),
1074 indent.unwrap_or(0),
1075 format,
1076 ) {
1077 return None;
1078 }
1079
1080 Some(jsstr_to_string(self.cx, NonNull::new(string_handle.get())?))
1081 }
1082 }
1083
1084 pub fn for_each_stack_frame<F>(&self, mut f: F)
1086 where
1087 F: FnMut(Handle<*mut JSObject>),
1088 {
1089 rooted!(in(self.cx) let mut current_element = self.stack.clone());
1090 rooted!(in(self.cx) let mut next_element = ptr::null_mut::<JSObject>());
1091
1092 loop {
1093 f(current_element.handle());
1094
1095 unsafe {
1096 let result = jsapi::GetSavedFrameParent(
1097 self.cx,
1098 ptr::null_mut(),
1099 current_element.handle().into_handle(),
1100 next_element.handle_mut().into_handle_mut(),
1101 jsapi::SavedFrameSelfHosted::Include,
1102 );
1103
1104 if result != SavedFrameResult::Ok || next_element.is_null() {
1105 return;
1106 }
1107 }
1108 current_element.set(next_element.get());
1109 }
1110 }
1111}
1112
1113#[macro_export]
1114macro_rules! capture_stack {
1115 (in($cx:expr) let $name:ident = with max depth($max_frame_count:expr)) => {
1116 rooted!(in($cx) let mut __obj = ::std::ptr::null_mut());
1117 let $name = $crate::rust::CapturedJSStack::new($cx, __obj, Some($max_frame_count));
1118 };
1119 (in($cx:expr) let $name:ident ) => {
1120 rooted!(in($cx) let mut __obj = ::std::ptr::null_mut());
1121 let $name = $crate::rust::CapturedJSStack::new($cx, __obj, None);
1122 }
1123}
1124
1125pub struct EnvironmentChain {
1126 chain: *mut crate::jsapi::JS::EnvironmentChain,
1127}
1128
1129impl EnvironmentChain {
1130 pub fn new(
1131 cx: *mut JSContext,
1132 support_unscopeables: crate::jsapi::JS::SupportUnscopables,
1133 ) -> Self {
1134 unsafe {
1135 Self {
1136 chain: crate::jsapi::glue::NewEnvironmentChain(cx, support_unscopeables),
1137 }
1138 }
1139 }
1140
1141 pub fn append(&self, obj: *mut JSObject) {
1142 unsafe {
1143 assert!(crate::jsapi::glue::AppendToEnvironmentChain(
1144 self.chain, obj
1145 ));
1146 }
1147 }
1148
1149 pub fn get(&self) -> *mut crate::jsapi::JS::EnvironmentChain {
1150 self.chain
1151 }
1152}
1153
1154impl Drop for EnvironmentChain {
1155 fn drop(&mut self) {
1156 unsafe {
1157 crate::jsapi::glue::DeleteEnvironmentChain(self.chain);
1158 }
1159 }
1160}
1161
1162impl<'a> Handle<'a, StackGCVector<JSVal, js::TempAllocPolicy>> {
1163 pub fn at(&'a self, index: u32) -> Option<Handle<'a, JSVal>> {
1164 if index >= self.len() {
1165 return None;
1166 }
1167 let handle =
1168 unsafe { Handle::from_marked_location(StackGCVectorValueAtIndex(*self, index)) };
1169 Some(handle)
1170 }
1171
1172 pub fn len(&self) -> u32 {
1173 unsafe { StackGCVectorValueLength(*self) }
1174 }
1175}
1176
1177impl<'a> Handle<'a, StackGCVector<*mut JSString, js::TempAllocPolicy>> {
1178 pub fn at(&'a self, index: u32) -> Option<Handle<'a, *mut JSString>> {
1179 if index >= self.len() {
1180 return None;
1181 }
1182 let handle =
1183 unsafe { Handle::from_marked_location(StackGCVectorStringAtIndex(*self, index)) };
1184 Some(handle)
1185 }
1186
1187 pub fn len(&self) -> u32 {
1188 unsafe { StackGCVectorStringLength(*self) }
1189 }
1190}
1191
1192pub mod wrappers {
1194 macro_rules! wrap {
1195 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: *const Handle<$gentype:ty>, $($rest:tt)*) => {
1200 wrap!(@inner $saved <> ($($acc,)* if $arg.is_null() { std::ptr::null() } else { &(*$arg).into() },) <> $($rest)*);
1201 };
1202 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: Handle<$gentype:ty>, $($rest:tt)*) => {
1203 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1204 };
1205 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandle<$gentype:ty>, $($rest:tt)*) => {
1206 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1207 };
1208 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: Handle, $($rest:tt)*) => {
1209 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1210 };
1211 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandle, $($rest:tt)*) => {
1212 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1213 };
1214 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleFunction , $($rest:tt)*) => {
1215 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1216 };
1217 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleId , $($rest:tt)*) => {
1218 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1219 };
1220 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleObject , $($rest:tt)*) => {
1221 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1222 };
1223 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleScript , $($rest:tt)*) => {
1224 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1225 };
1226 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleString , $($rest:tt)*) => {
1227 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1228 };
1229 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleSymbol , $($rest:tt)*) => {
1230 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1231 };
1232 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleValue , $($rest:tt)*) => {
1233 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1234 };
1235 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleFunction , $($rest:tt)*) => {
1236 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1237 };
1238 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleId , $($rest:tt)*) => {
1239 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1240 };
1241 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleObject , $($rest:tt)*) => {
1242 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1243 };
1244 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleScript , $($rest:tt)*) => {
1245 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1246 };
1247 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleString , $($rest:tt)*) => {
1248 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1249 };
1250 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleSymbol , $($rest:tt)*) => {
1251 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1252 };
1253 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleValue , $($rest:tt)*) => {
1254 wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
1255 };
1256 (@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: $type:ty, $($rest:tt)*) => {
1257 wrap!(@inner $saved <> ($($acc,)* $arg,) <> $($rest)*);
1258 };
1259 (@inner ($module:tt: $func_name:ident ($($args:tt)*) -> $outtype:ty) <> ($($argexprs:expr,)*) <> ) => {
1260 #[inline]
1261 pub unsafe fn $func_name($($args)*) -> $outtype {
1262 $module::$func_name($($argexprs),*)
1263 }
1264 };
1265 ($module:tt: pub fn $func_name:ident($($args:tt)*) -> $outtype:ty) => {
1266 wrap!(@inner ($module: $func_name ($($args)*) -> $outtype) <> () <> $($args)* ,);
1267 };
1268 ($module:tt: pub fn $func_name:ident($($args:tt)*)) => {
1269 wrap!($module: pub fn $func_name($($args)*) -> ());
1270 }
1271 }
1272
1273 use super::*;
1274 use crate::glue;
1275 use crate::glue::EncodedStringCallback;
1276 use crate::jsapi;
1277 use crate::jsapi::js::TempAllocPolicy;
1278 use crate::jsapi::jsid;
1279 use crate::jsapi::mozilla::Utf8Unit;
1280 use crate::jsapi::BigInt;
1281 use crate::jsapi::CallArgs;
1282 use crate::jsapi::CloneDataPolicy;
1283 use crate::jsapi::ColumnNumberOneOrigin;
1284 use crate::jsapi::CompartmentTransplantCallback;
1285 use crate::jsapi::EnvironmentChain;
1286 use crate::jsapi::JSONParseHandler;
1287 use crate::jsapi::Latin1Char;
1288 use crate::jsapi::PropertyKey;
1289 use crate::jsapi::TaggedColumnNumberOneOrigin;
1290 use crate::jsapi::ESClass;
1292 use crate::jsapi::ExceptionStackBehavior;
1293 use crate::jsapi::ForOfIterator;
1294 use crate::jsapi::ForOfIterator_NonIterableBehavior;
1295 use crate::jsapi::HandleObjectVector;
1296 use crate::jsapi::InstantiateOptions;
1297 use crate::jsapi::JSClass;
1298 use crate::jsapi::JSErrorReport;
1299 use crate::jsapi::JSExnType;
1300 use crate::jsapi::JSFunctionSpecWithHelp;
1301 use crate::jsapi::JSJitInfo;
1302 use crate::jsapi::JSONWriteCallback;
1303 use crate::jsapi::JSPrincipals;
1304 use crate::jsapi::JSPropertySpec;
1305 use crate::jsapi::JSPropertySpec_Name;
1306 use crate::jsapi::JSProtoKey;
1307 use crate::jsapi::JSScript;
1308 use crate::jsapi::JSStructuredCloneData;
1309 use crate::jsapi::JSType;
1310 use crate::jsapi::ModuleErrorBehaviour;
1311 use crate::jsapi::ModuleType;
1312 use crate::jsapi::MutableHandleIdVector;
1313 use crate::jsapi::PromiseState;
1314 use crate::jsapi::PromiseUserInputEventHandlingState;
1315 use crate::jsapi::ReadOnlyCompileOptions;
1316 use crate::jsapi::Realm;
1317 use crate::jsapi::RefPtr;
1318 use crate::jsapi::RegExpFlags;
1319 use crate::jsapi::ScriptEnvironmentPreparer_Closure;
1320 use crate::jsapi::SourceText;
1321 use crate::jsapi::StackCapture;
1322 use crate::jsapi::Stencil;
1323 use crate::jsapi::StructuredCloneScope;
1324 use crate::jsapi::Symbol;
1325 use crate::jsapi::SymbolCode;
1326 use crate::jsapi::TranscodeBuffer;
1327 use crate::jsapi::TwoByteChars;
1328 use crate::jsapi::UniqueChars;
1329 use crate::jsapi::Value;
1330 use crate::jsapi::WasmModule;
1331 use crate::jsapi::{ElementAdder, IsArrayAnswer, PropertyDescriptor};
1332 use crate::jsapi::{JSContext, JSFunction, JSNative, JSObject, JSString};
1333 use crate::jsapi::{
1334 JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
1335 };
1336 use crate::jsapi::{MallocSizeOf, ObjectOpResult, ObjectPrivateVisitor, TabSizes};
1337 use crate::jsapi::{SavedFrameResult, SavedFrameSelfHosted};
1338 include!("jsapi_wrappers.in.rs");
1339 include!("glue_wrappers.in.rs");
1340}