use std::cell::Cell;
use std::char;
use std::default::Default;
use std::ffi;
use std::ffi::CStr;
use std::marker::PhantomData;
use std::mem::MaybeUninit;
use std::ops::{Deref, DerefMut};
use std::ptr;
use std::slice;
use std::str;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex, RwLock};
use crate::consts::{JSCLASS_GLOBAL_SLOT_COUNT, JSCLASS_RESERVED_SLOTS_MASK};
use crate::consts::{JSCLASS_IS_DOMJSCLASS, JSCLASS_IS_GLOBAL};
use crate::conversions::jsstr_to_string;
use crate::default_heapsize;
pub use crate::gc::Traceable as Trace;
pub use crate::gc::*;
use crate::glue::AppendToRootedObjectVector;
use crate::glue::{CreateRootedIdVector, CreateRootedObjectVector};
use crate::glue::{
DeleteCompileOptions, DeleteRootedObjectVector, DescribeScriptedCaller, DestroyRootedIdVector,
};
use crate::glue::{
GetIdVectorAddress, GetObjectVectorAddress, NewCompileOptions, SliceRootedIdVector,
};
use crate::jsapi;
use crate::jsapi::glue::{DeleteRealmOptions, JS_Init, JS_NewRealmOptions};
use crate::jsapi::js::frontend::CompilationStencil;
use crate::jsapi::mozilla::Utf8Unit;
use crate::jsapi::shadow::BaseShape;
use crate::jsapi::HandleObjectVector as RawHandleObjectVector;
use crate::jsapi::HandleValue as RawHandleValue;
use crate::jsapi::JS_AddExtraGCRootsTracer;
use crate::jsapi::MutableHandleIdVector as RawMutableHandleIdVector;
use crate::jsapi::{already_AddRefed, jsid};
use crate::jsapi::{BuildStackString, CaptureCurrentStack, StackFormat};
use crate::jsapi::{Evaluate2, HandleValueArray, StencilRelease};
use crate::jsapi::{InitSelfHostedCode, IsWindowSlow};
use crate::jsapi::{
JSAutoRealm, JS_SetGCParameter, JS_SetNativeStackQuota, JS_WrapObject, JS_WrapValue,
};
use crate::jsapi::{JSClass, JSClassOps, JSContext, Realm, JSCLASS_RESERVED_SLOTS_SHIFT};
use crate::jsapi::{JSErrorReport, JSFunctionSpec, JSGCParamKey};
use crate::jsapi::{JSObject, JSPropertySpec, JSRuntime};
use crate::jsapi::{JSString, Object, PersistentRootedIdVector};
use crate::jsapi::{JS_DefineFunctions, JS_DefineProperties, JS_DestroyContext, JS_ShutDown};
use crate::jsapi::{JS_EnumerateStandardClasses, JS_GetRuntime, JS_GlobalObjectTraceHook};
use crate::jsapi::{JS_MayResolveStandardClass, JS_NewContext, JS_ResolveStandardClass};
use crate::jsapi::{JS_RequestInterruptCallback, JS_RequestInterruptCallbackCanWait};
use crate::jsapi::{JS_StackCapture_AllFrames, JS_StackCapture_MaxFrames};
use crate::jsapi::{PersistentRootedObjectVector, ReadOnlyCompileOptions, RootingContext};
use crate::jsapi::{SetWarningReporter, SourceText, ToBooleanSlow};
use crate::jsapi::{ToInt32Slow, ToInt64Slow, ToNumberSlow, ToStringSlow, ToUint16Slow};
use crate::jsapi::{ToUint32Slow, ToUint64Slow, ToWindowProxyIfWindowSlow};
use crate::jsval::ObjectValue;
use crate::panic::maybe_resume_unwind;
use lazy_static::lazy_static;
use log::{debug, warn};
pub use mozjs_sys::jsgc::{GCMethods, IntoHandle, IntoMutableHandle};
use crate::rooted;
const STACK_QUOTA: usize = 128 * 8 * 1024;
const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
const TRUSTED_SCRIPT_BUFFER: usize = 8 * 12800;
trait ToResult {
fn to_result(self) -> Result<(), ()>;
}
impl ToResult for bool {
fn to_result(self) -> Result<(), ()> {
if self {
Ok(())
} else {
Err(())
}
}
}
pub struct RealmOptions(*mut jsapi::RealmOptions);
impl Deref for RealmOptions {
type Target = jsapi::RealmOptions;
fn deref(&self) -> &Self::Target {
unsafe { &*self.0 }
}
}
impl DerefMut for RealmOptions {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *self.0 }
}
}
impl Default for RealmOptions {
fn default() -> RealmOptions {
RealmOptions(unsafe { JS_NewRealmOptions() })
}
}
impl Drop for RealmOptions {
fn drop(&mut self) {
unsafe { DeleteRealmOptions(self.0) }
}
}
thread_local!(static CONTEXT: Cell<*mut JSContext> = Cell::new(ptr::null_mut()));
#[derive(PartialEq)]
enum EngineState {
Uninitialized,
InitFailed,
Initialized,
ShutDown,
}
lazy_static! {
static ref ENGINE_STATE: Mutex<EngineState> = Mutex::new(EngineState::Uninitialized);
}
#[derive(Debug)]
pub enum JSEngineError {
AlreadyInitialized,
AlreadyShutDown,
InitFailed,
}
pub struct JSEngine {
outstanding_handles: Arc<AtomicU32>,
marker: PhantomData<*mut ()>,
}
pub struct JSEngineHandle(Arc<AtomicU32>);
impl Clone for JSEngineHandle {
fn clone(&self) -> JSEngineHandle {
self.0.fetch_add(1, Ordering::SeqCst);
JSEngineHandle(self.0.clone())
}
}
impl Drop for JSEngineHandle {
fn drop(&mut self) {
self.0.fetch_sub(1, Ordering::SeqCst);
}
}
impl JSEngine {
pub fn init() -> Result<JSEngine, JSEngineError> {
let mut state = ENGINE_STATE.lock().unwrap();
match *state {
EngineState::Initialized => return Err(JSEngineError::AlreadyInitialized),
EngineState::InitFailed => return Err(JSEngineError::InitFailed),
EngineState::ShutDown => return Err(JSEngineError::AlreadyShutDown),
EngineState::Uninitialized => (),
}
if unsafe { !JS_Init() } {
*state = EngineState::InitFailed;
Err(JSEngineError::InitFailed)
} else {
*state = EngineState::Initialized;
Ok(JSEngine {
outstanding_handles: Arc::new(AtomicU32::new(0)),
marker: PhantomData,
})
}
}
pub fn can_shutdown(&self) -> bool {
self.outstanding_handles.load(Ordering::SeqCst) == 0
}
pub fn handle(&self) -> JSEngineHandle {
self.outstanding_handles.fetch_add(1, Ordering::SeqCst);
JSEngineHandle(self.outstanding_handles.clone())
}
}
impl Drop for JSEngine {
fn drop(&mut self) {
let mut state = ENGINE_STATE.lock().unwrap();
if *state == EngineState::Initialized {
assert_eq!(
self.outstanding_handles.load(Ordering::SeqCst),
0,
"There are outstanding JS engine handles"
);
*state = EngineState::ShutDown;
unsafe {
JS_ShutDown();
}
}
}
}
pub fn transform_str_to_source_text(source: &str) -> SourceText<Utf8Unit> {
SourceText {
units_: source.as_ptr() as *const _,
length_: source.len() as u32,
ownsUnits_: false,
_phantom_0: PhantomData,
}
}
pub fn transform_u16_to_source_text(source: &[u16]) -> SourceText<u16> {
SourceText {
units_: source.as_ptr() as *const _,
length_: source.len() as u32,
ownsUnits_: false,
_phantom_0: PhantomData,
}
}
pub struct ParentRuntime {
parent: *mut JSRuntime,
engine: JSEngineHandle,
children_of_parent: Arc<()>,
}
unsafe impl Send for ParentRuntime {}
pub struct Runtime {
cx: *mut JSContext,
engine: JSEngineHandle,
_parent_child_count: Option<Arc<()>>,
outstanding_children: Arc<()>,
thread_safe_handle: Arc<RwLock<Option<*mut JSContext>>>,
}
impl Runtime {
pub fn get() -> *mut JSContext {
let cx = CONTEXT.with(|context| context.get());
assert!(!cx.is_null());
cx
}
pub fn thread_safe_js_context(&self) -> ThreadSafeJSContext {
ThreadSafeJSContext(self.thread_safe_handle.clone())
}
pub fn new(engine: JSEngineHandle) -> Runtime {
unsafe { Self::create(engine, None) }
}
pub fn prepare_for_new_child(&self) -> ParentRuntime {
ParentRuntime {
parent: self.rt(),
engine: self.engine.clone(),
children_of_parent: self.outstanding_children.clone(),
}
}
pub unsafe fn create_with_parent(parent: ParentRuntime) -> Runtime {
Self::create(parent.engine.clone(), Some(parent))
}
unsafe fn create(engine: JSEngineHandle, parent: Option<ParentRuntime>) -> Runtime {
let parent_runtime = parent.as_ref().map_or(ptr::null_mut(), |r| r.parent);
let js_context = JS_NewContext(default_heapsize + (ChunkSize as u32), parent_runtime);
assert!(!js_context.is_null());
JS_SetGCParameter(js_context, JSGCParamKey::JSGC_MAX_BYTES, u32::MAX);
JS_AddExtraGCRootsTracer(js_context, Some(trace_traceables), ptr::null_mut());
JS_SetNativeStackQuota(
js_context,
STACK_QUOTA,
STACK_QUOTA - SYSTEM_CODE_BUFFER,
STACK_QUOTA - SYSTEM_CODE_BUFFER - TRUSTED_SCRIPT_BUFFER,
);
CONTEXT.with(|context| {
assert!(context.get().is_null());
context.set(js_context);
});
#[cfg(target_pointer_width = "64")]
InitSelfHostedCode(js_context, [0u64; 2], None);
#[cfg(target_pointer_width = "32")]
InitSelfHostedCode(js_context, [0u32; 2], None);
SetWarningReporter(js_context, Some(report_warning));
Runtime {
engine,
_parent_child_count: parent.map(|p| p.children_of_parent),
cx: js_context,
outstanding_children: Arc::new(()),
thread_safe_handle: Arc::new(RwLock::new(Some(js_context))),
}
}
pub fn rt(&self) -> *mut JSRuntime {
unsafe { JS_GetRuntime(self.cx) }
}
pub fn cx(&self) -> *mut JSContext {
self.cx
}
pub fn evaluate_script(
&self,
glob: HandleObject,
script: &str,
filename: &str,
line_num: u32,
rval: MutableHandleValue,
) -> Result<(), ()> {
debug!(
"Evaluating script from {} with content {}",
filename, script
);
let _ac = JSAutoRealm::new(self.cx(), glob.get());
let options = unsafe { CompileOptionsWrapper::new(self.cx(), filename, line_num) };
unsafe {
let mut source = transform_str_to_source_text(&script);
if !Evaluate2(self.cx(), options.ptr, &mut source, rval.into()) {
debug!("...err!");
maybe_resume_unwind();
Err(())
} else {
debug!("...ok!");
Ok(())
}
}
}
}
impl Drop for Runtime {
fn drop(&mut self) {
self.thread_safe_handle.write().unwrap().take();
assert_eq!(
Arc::strong_count(&self.outstanding_children),
1,
"This runtime still has live children."
);
unsafe {
JS_DestroyContext(self.cx);
CONTEXT.with(|context| {
assert_eq!(context.get(), self.cx);
context.set(ptr::null_mut());
});
}
}
}
#[derive(Clone)]
pub struct ThreadSafeJSContext(Arc<RwLock<Option<*mut JSContext>>>);
unsafe impl Send for ThreadSafeJSContext {}
unsafe impl Sync for ThreadSafeJSContext {}
impl ThreadSafeJSContext {
pub fn request_interrupt_callback(&self) {
if let Some(&cx) = self.0.read().unwrap().as_ref() {
unsafe {
JS_RequestInterruptCallback(cx);
}
}
}
pub fn request_interrupt_callback_can_wait(&self) {
if let Some(&cx) = self.0.read().unwrap().as_ref() {
unsafe {
JS_RequestInterruptCallbackCanWait(cx);
}
}
}
}
const ChunkShift: usize = 20;
const ChunkSize: usize = 1 << ChunkShift;
#[cfg(target_pointer_width = "32")]
const ChunkLocationOffset: usize = ChunkSize - 2 * 4 - 8;
pub struct RootedObjectVectorWrapper {
pub ptr: *mut PersistentRootedObjectVector,
}
impl RootedObjectVectorWrapper {
pub fn new(cx: *mut JSContext) -> RootedObjectVectorWrapper {
RootedObjectVectorWrapper {
ptr: unsafe { CreateRootedObjectVector(cx) },
}
}
pub fn append(&self, obj: *mut JSObject) -> bool {
unsafe { AppendToRootedObjectVector(self.ptr, obj) }
}
pub fn handle(&self) -> RawHandleObjectVector {
RawHandleObjectVector {
ptr: unsafe { GetObjectVectorAddress(self.ptr) },
}
}
}
impl Drop for RootedObjectVectorWrapper {
fn drop(&mut self) {
unsafe { DeleteRootedObjectVector(self.ptr) }
}
}
pub struct CompileOptionsWrapper {
pub ptr: *mut ReadOnlyCompileOptions,
}
impl CompileOptionsWrapper {
pub unsafe fn new(cx: *mut JSContext, filename: &str, line: u32) -> Self {
let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
let ptr = NewCompileOptions(cx, filename_cstr.as_ptr(), line);
assert!(!ptr.is_null());
Self { ptr }
}
}
impl Drop for CompileOptionsWrapper {
fn drop(&mut self) {
unsafe { DeleteCompileOptions(self.ptr) }
}
}
pub struct Stencil {
inner: already_AddRefed<CompilationStencil>,
}
impl Drop for Stencil {
fn drop(&mut self) {
if self.is_null() {
return;
}
unsafe {
StencilRelease(self.inner.mRawPtr);
}
}
}
impl Deref for Stencil {
type Target = *mut CompilationStencil;
fn deref(&self) -> &Self::Target {
&self.inner.mRawPtr
}
}
impl Stencil {
pub fn is_null(&self) -> bool {
self.inner.mRawPtr.is_null()
}
}
#[inline]
pub unsafe fn ToBoolean(v: HandleValue) -> bool {
let val = *v.ptr;
if val.is_boolean() {
return val.to_boolean();
}
if val.is_int32() {
return val.to_int32() != 0;
}
if val.is_null_or_undefined() {
return false;
}
if val.is_double() {
let d = val.to_double();
return !d.is_nan() && d != 0f64;
}
if val.is_symbol() {
return true;
}
ToBooleanSlow(v.into())
}
#[inline]
pub unsafe fn ToNumber(cx: *mut JSContext, v: HandleValue) -> Result<f64, ()> {
let val = *v.ptr;
if val.is_number() {
return Ok(val.to_number());
}
let mut out = Default::default();
if ToNumberSlow(cx, v.into_handle(), &mut out) {
Ok(out)
} else {
Err(())
}
}
#[inline]
unsafe fn convert_from_int32<T: Default + Copy>(
cx: *mut JSContext,
v: HandleValue,
conv_fn: unsafe extern "C" fn(*mut JSContext, RawHandleValue, *mut T) -> bool,
) -> Result<T, ()> {
let val = *v.ptr;
if val.is_int32() {
let intval: i64 = val.to_int32() as i64;
let intval = *(&intval as *const i64 as *const T);
return Ok(intval);
}
let mut out = Default::default();
if conv_fn(cx, v.into(), &mut out) {
Ok(out)
} else {
Err(())
}
}
#[inline]
pub unsafe fn ToInt32(cx: *mut JSContext, v: HandleValue) -> Result<i32, ()> {
convert_from_int32::<i32>(cx, v, ToInt32Slow)
}
#[inline]
pub unsafe fn ToUint32(cx: *mut JSContext, v: HandleValue) -> Result<u32, ()> {
convert_from_int32::<u32>(cx, v, ToUint32Slow)
}
#[inline]
pub unsafe fn ToUint16(cx: *mut JSContext, v: HandleValue) -> Result<u16, ()> {
convert_from_int32::<u16>(cx, v, ToUint16Slow)
}
#[inline]
pub unsafe fn ToInt64(cx: *mut JSContext, v: HandleValue) -> Result<i64, ()> {
convert_from_int32::<i64>(cx, v, ToInt64Slow)
}
#[inline]
pub unsafe fn ToUint64(cx: *mut JSContext, v: HandleValue) -> Result<u64, ()> {
convert_from_int32::<u64>(cx, v, ToUint64Slow)
}
#[inline]
pub unsafe fn ToString(cx: *mut JSContext, v: HandleValue) -> *mut JSString {
let val = *v.ptr;
if val.is_string() {
return val.to_string();
}
ToStringSlow(cx, v.into())
}
pub unsafe fn ToWindowProxyIfWindow(obj: *mut JSObject) -> *mut JSObject {
if is_window(obj) {
ToWindowProxyIfWindowSlow(obj)
} else {
obj
}
}
pub unsafe extern "C" fn report_warning(_cx: *mut JSContext, report: *mut JSErrorReport) {
fn latin1_to_string(bytes: &[u8]) -> String {
bytes
.iter()
.map(|c| char::from_u32(*c as u32).unwrap())
.collect()
}
let fnptr = (*report)._base.filename.data_;
let fname = if !fnptr.is_null() {
let c_str = CStr::from_ptr(fnptr);
latin1_to_string(c_str.to_bytes())
} else {
"none".to_string()
};
let lineno = (*report)._base.lineno;
let column = (*report)._base.column._base;
let msg_ptr = (*report)._base.message_.data_ as *const u8;
let msg_len = (0usize..)
.find(|&i| *msg_ptr.offset(i as isize) == 0)
.unwrap();
let msg_slice = slice::from_raw_parts(msg_ptr, msg_len);
let msg = str::from_utf8_unchecked(msg_slice);
warn!("Warning at {}:{}:{}: {}\n", fname, lineno, column, msg);
}
pub struct IdVector(*mut PersistentRootedIdVector);
impl IdVector {
pub unsafe fn new(cx: *mut JSContext) -> IdVector {
let vector = CreateRootedIdVector(cx);
assert!(!vector.is_null());
IdVector(vector)
}
pub fn handle_mut(&mut self) -> RawMutableHandleIdVector {
RawMutableHandleIdVector {
ptr: unsafe { GetIdVectorAddress(self.0) },
}
}
}
impl Drop for IdVector {
fn drop(&mut self) {
unsafe { DestroyRootedIdVector(self.0) }
}
}
impl Deref for IdVector {
type Target = [jsid];
fn deref(&self) -> &[jsid] {
unsafe {
let mut length = 0;
let pointer = SliceRootedIdVector(self.0, &mut length);
slice::from_raw_parts(pointer, length)
}
}
}
pub unsafe fn define_methods(
cx: *mut JSContext,
obj: HandleObject,
methods: &'static [JSFunctionSpec],
) -> Result<(), ()> {
assert!({
match methods.last() {
Some(&JSFunctionSpec {
name,
call,
nargs,
flags,
selfHostedName,
}) => {
name.string_.is_null()
&& call.is_zeroed()
&& nargs == 0
&& flags == 0
&& selfHostedName.is_null()
}
None => false,
}
});
JS_DefineFunctions(cx, obj.into(), methods.as_ptr()).to_result()
}
pub unsafe fn define_properties(
cx: *mut JSContext,
obj: HandleObject,
properties: &'static [JSPropertySpec],
) -> Result<(), ()> {
assert!({
match properties.last() {
Some(spec) => spec.is_zeroed(),
None => false,
}
});
JS_DefineProperties(cx, obj.into(), properties.as_ptr()).to_result()
}
static SIMPLE_GLOBAL_CLASS_OPS: JSClassOps = JSClassOps {
addProperty: None,
delProperty: None,
enumerate: Some(JS_EnumerateStandardClasses),
newEnumerate: None,
resolve: Some(JS_ResolveStandardClass),
mayResolve: Some(JS_MayResolveStandardClass),
finalize: None,
call: None,
construct: None,
trace: Some(JS_GlobalObjectTraceHook),
};
pub static SIMPLE_GLOBAL_CLASS: JSClass = JSClass {
name: b"Global\0" as *const u8 as *const _,
flags: JSCLASS_IS_GLOBAL
| ((JSCLASS_GLOBAL_SLOT_COUNT & JSCLASS_RESERVED_SLOTS_MASK)
<< JSCLASS_RESERVED_SLOTS_SHIFT),
cOps: &SIMPLE_GLOBAL_CLASS_OPS as *const JSClassOps,
spec: ptr::null(),
ext: ptr::null(),
oOps: ptr::null(),
};
#[inline]
unsafe fn get_object_group(obj: *mut JSObject) -> *mut BaseShape {
assert!(!obj.is_null());
let obj = obj as *mut Object;
(*(*obj).shape).base
}
#[inline]
pub unsafe fn get_object_class(obj: *mut JSObject) -> *const JSClass {
(*get_object_group(obj)).clasp as *const _
}
#[inline]
pub unsafe fn get_object_realm(obj: *mut JSObject) -> *mut Realm {
(*get_object_group(obj)).realm
}
#[inline]
pub unsafe fn get_context_realm(cx: *mut JSContext) -> *mut Realm {
let cx = cx as *mut RootingContext;
(*cx).realm_
}
#[inline]
pub fn is_dom_class(class: &JSClass) -> bool {
class.flags & JSCLASS_IS_DOMJSCLASS != 0
}
#[inline]
pub unsafe fn is_dom_object(obj: *mut JSObject) -> bool {
is_dom_class(&*get_object_class(obj))
}
#[inline]
pub unsafe fn is_window(obj: *mut JSObject) -> bool {
(*get_object_class(obj)).flags & JSCLASS_IS_GLOBAL != 0 && IsWindowSlow(obj)
}
#[inline]
pub unsafe fn try_to_outerize(mut rval: MutableHandleValue) {
let obj = rval.to_object();
if is_window(obj) {
let obj = ToWindowProxyIfWindowSlow(obj);
assert!(!obj.is_null());
rval.set(ObjectValue(&mut *obj));
}
}
#[inline]
pub unsafe fn try_to_outerize_object(mut rval: MutableHandleObject) {
if is_window(*rval) {
let obj = ToWindowProxyIfWindowSlow(*rval);
assert!(!obj.is_null());
rval.set(obj);
}
}
#[inline]
pub unsafe fn maybe_wrap_object(cx: *mut JSContext, obj: MutableHandleObject) {
if get_object_realm(*obj) != get_context_realm(cx) {
assert!(JS_WrapObject(cx, obj.into()));
}
try_to_outerize_object(obj);
}
#[inline]
pub unsafe fn maybe_wrap_object_value(cx: *mut JSContext, rval: MutableHandleValue) {
assert!(rval.is_object());
let obj = rval.to_object();
if get_object_realm(obj) != get_context_realm(cx) {
assert!(JS_WrapValue(cx, rval.into()));
} else if is_dom_object(obj) {
try_to_outerize(rval);
}
}
#[inline]
pub unsafe fn maybe_wrap_object_or_null_value(cx: *mut JSContext, rval: MutableHandleValue) {
assert!(rval.is_object_or_null());
if !rval.is_null() {
maybe_wrap_object_value(cx, rval);
}
}
#[inline]
pub unsafe fn maybe_wrap_value(cx: *mut JSContext, rval: MutableHandleValue) {
if rval.is_string() {
assert!(JS_WrapValue(cx, rval.into()));
} else if rval.is_object() {
maybe_wrap_object_value(cx, rval);
}
}
#[macro_export]
macro_rules! new_jsjitinfo_bitfield_1 {
(
$type_: expr,
$aliasSet_: expr,
$returnType_: expr,
$isInfallible: expr,
$isMovable: expr,
$isEliminatable: expr,
$isAlwaysInSlot: expr,
$isLazilyCachedInSlot: expr,
$isTypedMethod: expr,
$slotIndex: expr,
) => {
0 | (($type_ as u32) << 0u32)
| (($aliasSet_ as u32) << 4u32)
| (($returnType_ as u32) << 8u32)
| (($isInfallible as u32) << 16u32)
| (($isMovable as u32) << 17u32)
| (($isEliminatable as u32) << 18u32)
| (($isAlwaysInSlot as u32) << 19u32)
| (($isLazilyCachedInSlot as u32) << 20u32)
| (($isTypedMethod as u32) << 21u32)
| (($slotIndex as u32) << 22u32)
};
}
#[derive(Debug, Default)]
pub struct ScriptedCaller {
pub filename: String,
pub line: u32,
pub col: u32,
}
pub unsafe fn describe_scripted_caller(cx: *mut JSContext) -> Result<ScriptedCaller, ()> {
let mut buf = [0; 1024];
let mut line = 0;
let mut col = 0;
if !DescribeScriptedCaller(cx, buf.as_mut_ptr(), buf.len(), &mut line, &mut col) {
return Err(());
}
let filename = CStr::from_ptr((&buf) as *const _ as *const _);
Ok(ScriptedCaller {
filename: String::from_utf8_lossy(filename.to_bytes()).into_owned(),
line,
col,
})
}
pub struct CapturedJSStack<'a> {
cx: *mut JSContext,
stack: RootedGuard<'a, *mut JSObject>,
}
impl<'a> CapturedJSStack<'a> {
pub unsafe fn new(
cx: *mut JSContext,
mut guard: RootedGuard<'a, *mut JSObject>,
max_frame_count: Option<u32>,
) -> Option<Self> {
let ref mut stack_capture = MaybeUninit::uninit();
match max_frame_count {
None => JS_StackCapture_AllFrames(stack_capture.as_mut_ptr()),
Some(count) => JS_StackCapture_MaxFrames(count, stack_capture.as_mut_ptr()),
};
let ref mut stack_capture = stack_capture.assume_init();
if !CaptureCurrentStack(cx, guard.handle_mut().raw(), stack_capture) {
None
} else {
Some(CapturedJSStack { cx, stack: guard })
}
}
pub fn as_string(&self, indent: Option<usize>, format: StackFormat) -> Option<String> {
unsafe {
let stack_handle = self.stack.handle();
rooted!(in(self.cx) let mut js_string = ptr::null_mut::<JSString>());
let mut string_handle = js_string.handle_mut();
if !BuildStackString(
self.cx,
ptr::null_mut(),
stack_handle.into(),
string_handle.raw(),
indent.unwrap_or(0),
format,
) {
return None;
}
Some(jsstr_to_string(self.cx, string_handle.get()))
}
}
}
#[macro_export]
macro_rules! capture_stack {
(in($cx:expr) let $name:ident = with max depth($max_frame_count:expr)) => {
rooted!(in($cx) let mut __obj = ::std::ptr::null_mut());
let $name = $crate::rust::CapturedJSStack::new($cx, __obj, Some($max_frame_count));
};
(in($cx:expr) let $name:ident ) => {
rooted!(in($cx) let mut __obj = ::std::ptr::null_mut());
let $name = $crate::rust::CapturedJSStack::new($cx, __obj, None);
}
}
pub mod wrappers {
macro_rules! wrap {
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: Handle<$gentype:ty>, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandle<$gentype:ty>, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: Handle, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandle, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleFunction , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleId , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleObject , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleScript , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleString , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleSymbol , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: HandleValue , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleFunction , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleId , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleObject , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleScript , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleString , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleSymbol , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: MutableHandleValue , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($acc:expr,)*) <> $arg:ident: $type:ty, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($acc,)* $arg,) <> $($rest)*);
};
(@inner ($module:tt: $func_name:ident ($($args:tt)*) -> $outtype:ty) <> ($($argexprs:expr,)*) <> ) => {
#[inline]
pub unsafe fn $func_name($($args)*) -> $outtype {
$module::$func_name($($argexprs),*)
}
};
($module:tt: pub fn $func_name:ident($($args:tt)*) -> $outtype:ty) => {
wrap!(@inner ($module: $func_name ($($args)*) -> $outtype) <> () <> $($args)* ,);
};
($module:tt: pub fn $func_name:ident($($args:tt)*)) => {
wrap!($module: pub fn $func_name($($args)*) -> ());
}
}
use super::*;
use crate::glue;
use crate::glue::EncodedStringCallback;
use crate::jsapi;
use crate::jsapi::jsid;
use crate::jsapi::mozilla::Utf8Unit;
use crate::jsapi::BigInt;
use crate::jsapi::CallArgs;
use crate::jsapi::CloneDataPolicy;
use crate::jsapi::ColumnNumberOneOrigin;
use crate::jsapi::CompartmentTransplantCallback;
use crate::jsapi::JSONParseHandler;
use crate::jsapi::Latin1Char;
use crate::jsapi::PropertyKey;
use crate::jsapi::TaggedColumnNumberOneOrigin;
use crate::jsapi::ESClass;
use crate::jsapi::ExceptionStackBehavior;
use crate::jsapi::ForOfIterator;
use crate::jsapi::ForOfIterator_NonIterableBehavior;
use crate::jsapi::HandleObjectVector;
use crate::jsapi::InstantiateOptions;
use crate::jsapi::JSClass;
use crate::jsapi::JSErrorReport;
use crate::jsapi::JSExnType;
use crate::jsapi::JSFunctionSpecWithHelp;
use crate::jsapi::JSJitInfo;
use crate::jsapi::JSONWriteCallback;
use crate::jsapi::JSPrincipals;
use crate::jsapi::JSPropertySpec;
use crate::jsapi::JSPropertySpec_Name;
use crate::jsapi::JSProtoKey;
use crate::jsapi::JSScript;
use crate::jsapi::JSStructuredCloneData;
use crate::jsapi::JSType;
use crate::jsapi::ModuleErrorBehaviour;
use crate::jsapi::MutableHandleIdVector;
use crate::jsapi::PromiseState;
use crate::jsapi::PromiseUserInputEventHandlingState;
use crate::jsapi::ReadOnlyCompileOptions;
#[cfg(feature = "streams")]
use crate::jsapi::ReadableStreamMode;
#[cfg(feature = "streams")]
use crate::jsapi::ReadableStreamReaderMode;
#[cfg(feature = "streams")]
use crate::jsapi::ReadableStreamUnderlyingSource;
use crate::jsapi::Realm;
use crate::jsapi::RefPtr;
use crate::jsapi::RegExpFlags;
use crate::jsapi::ScriptEnvironmentPreparer_Closure;
use crate::jsapi::SourceText;
use crate::jsapi::StackCapture;
use crate::jsapi::StructuredCloneScope;
use crate::jsapi::Symbol;
use crate::jsapi::SymbolCode;
use crate::jsapi::TwoByteChars;
use crate::jsapi::UniqueChars;
use crate::jsapi::Value;
use crate::jsapi::WasmModule;
use crate::jsapi::{ElementAdder, IsArrayAnswer, PropertyDescriptor};
use crate::jsapi::{JSContext, JSFunction, JSNative, JSObject, JSString};
use crate::jsapi::{
JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
};
use crate::jsapi::{MallocSizeOf, ObjectOpResult, ObjectPrivateVisitor, TabSizes};
use crate::jsapi::{SavedFrameResult, SavedFrameSelfHosted};
include!("jsapi_wrappers.in");
include!("glue_wrappers.in");
}
pub mod jsapi_wrapped {
macro_rules! wrap {
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: Handle<$gentype:ty>, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: Handle<$gentype> , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandle<$gentype:ty>, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandle<$gentype> , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: Handle, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: Handle , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandle, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandle , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleFunction , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleFunction , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleId , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleId , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleObject , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleObject , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleScript , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleScript , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleString , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleString , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleSymbol , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleSymbol , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: HandleValue , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: HandleValue , ) <> ($($acc,)* $arg.into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleFunction , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleFunction , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleId , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleId , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleObject , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleObject , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleScript , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleScript , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleString , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleString , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleSymbol , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleSymbol , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: MutableHandleValue , $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: &mut MutableHandleValue , ) <> ($($acc,)* (*$arg).into(),) <> $($rest)*);
};
(@inner $saved:tt <> ($($declargs:tt)*) <> ($($acc:expr,)*) <> $arg:ident: $type:ty, $($rest:tt)*) => {
wrap!(@inner $saved <> ($($declargs)* $arg: $type,) <> ($($acc,)* $arg,) <> $($rest)*);
};
(@inner ($module:tt: $func_name:ident ($($args:tt)*) -> $outtype:ty) <> ($($declargs:tt)*) <> ($($argexprs:expr,)*) <> ) => {
#[inline]
pub unsafe fn $func_name($($declargs)*) -> $outtype {
$module::$func_name($($argexprs),*)
}
};
($module:tt: pub fn $func_name:ident($($args:tt)*) -> $outtype:ty) => {
wrap!(@inner ($module: $func_name ($($args)*) -> $outtype) <> () <> () <> $($args)* ,);
};
($module:tt: pub fn $func_name:ident($($args:tt)*)) => {
wrap!($module: pub fn $func_name($($args)*) -> ());
}
}
use super::*;
use crate::glue;
use crate::glue::EncodedStringCallback;
use crate::jsapi;
use crate::jsapi::mozilla::Utf8Unit;
use crate::jsapi::BigInt;
use crate::jsapi::CallArgs;
use crate::jsapi::CloneDataPolicy;
use crate::jsapi::ColumnNumberOneOrigin;
use crate::jsapi::CompartmentTransplantCallback;
use crate::jsapi::ESClass;
use crate::jsapi::ExceptionStackBehavior;
use crate::jsapi::ForOfIterator;
use crate::jsapi::ForOfIterator_NonIterableBehavior;
use crate::jsapi::HandleObjectVector;
use crate::jsapi::InstantiateOptions;
use crate::jsapi::JSClass;
use crate::jsapi::JSErrorReport;
use crate::jsapi::JSExnType;
use crate::jsapi::JSFunctionSpec;
use crate::jsapi::JSFunctionSpecWithHelp;
use crate::jsapi::JSJitInfo;
use crate::jsapi::JSONParseHandler;
use crate::jsapi::JSONWriteCallback;
use crate::jsapi::JSPrincipals;
use crate::jsapi::JSPropertySpec;
use crate::jsapi::JSPropertySpec_Name;
use crate::jsapi::JSProtoKey;
use crate::jsapi::JSScript;
use crate::jsapi::JSStructuredCloneData;
use crate::jsapi::JSType;
use crate::jsapi::Latin1Char;
use crate::jsapi::ModuleErrorBehaviour;
use crate::jsapi::MutableHandleIdVector;
use crate::jsapi::PromiseState;
use crate::jsapi::PromiseUserInputEventHandlingState;
use crate::jsapi::PropertyKey;
use crate::jsapi::ReadOnlyCompileOptions;
#[cfg(feature = "streams")]
use crate::jsapi::ReadableStreamMode;
#[cfg(feature = "streams")]
use crate::jsapi::ReadableStreamReaderMode;
#[cfg(feature = "streams")]
use crate::jsapi::ReadableStreamUnderlyingSource;
use crate::jsapi::Realm;
use crate::jsapi::RefPtr;
use crate::jsapi::RegExpFlags;
use crate::jsapi::ScriptEnvironmentPreparer_Closure;
use crate::jsapi::SourceText;
use crate::jsapi::StackCapture;
use crate::jsapi::StructuredCloneScope;
use crate::jsapi::Symbol;
use crate::jsapi::SymbolCode;
use crate::jsapi::TaggedColumnNumberOneOrigin;
use crate::jsapi::TwoByteChars;
use crate::jsapi::UniqueChars;
use crate::jsapi::Value;
use crate::jsapi::WasmModule;
use crate::jsapi::{ElementAdder, IsArrayAnswer, PropertyDescriptor};
use crate::jsapi::{JSContext, JSFunction, JSNative, JSObject, JSString};
use crate::jsapi::{
JSStructuredCloneCallbacks, JSStructuredCloneReader, JSStructuredCloneWriter,
};
use crate::jsapi::{MallocSizeOf, ObjectOpResult, ObjectPrivateVisitor, TabSizes};
use crate::jsapi::{SavedFrameResult, SavedFrameSelfHosted};
include!("jsapi_wrappers.in");
include!("glue_wrappers.in");
}