use ::std::ptr::NonNull;
use mozjs::conversions::unsafe_jsstr_to_string;
use mozjs::glue::JS_GetReservedSlot;
use mozjs::jsapi::*;
use mozjs::jsval::{JSVal, UndefinedValue};
use mozjs::rooted;
use mozjs::rust::wrappers2 as w2;
use crate::error::JsError;
use crate::value::{self, JsValue};
const HOST_OBJECT_SLOT: u32 = 0;
pub type JsResult<T> = ::std::result::Result<T, JsError>;
pub type JSHostFn = unsafe extern "C" fn(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HostFunctionType {
Free,
Method,
Getter,
Setter,
Constructor,
StaticMethod,
}
pub const fn to_js_host_call(f: JSHostFn) -> JSHostFn {
f
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn new_runtime_function(
cx: &mut mozjs::context::JSContext,
parent: mozjs::rust::Handle<*mut JSObject>,
name: &str,
nargs: u32,
host_fn: JSHostFn,
flags: u32,
) -> *mut JSObject {
let c_name = ::std::ffi::CString::new(name).unwrap_or_default();
let func = w2::JS_DefineFunction(cx, parent, c_name.as_ptr(), Some(host_fn), nargs, flags);
JS_GetFunctionObject(func)
}
pub trait JsClassOps: HostObject {
unsafe fn construct(cx: *mut JSContext, args: &CallArgs) -> JsResult<JsValue>;
}
pub fn install_console(
cx: &mut mozjs::context::JSContext,
global: mozjs::rust::Handle<*mut JSObject>,
) {
rooted!(&in(cx) let console_obj = unsafe { w2::JS_NewPlainObject(cx) });
if console_obj.get().is_null() {
return;
}
unsafe {
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"log".as_ptr(),
Some(console_log),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"error".as_ptr(),
Some(console_error),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"warn".as_ptr(),
Some(console_warn),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"info".as_ptr(),
Some(console_info),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"debug".as_ptr(),
Some(console_debug),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"dir".as_ptr(),
Some(console_dir),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"time".as_ptr(),
Some(console_time),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"timeEnd".as_ptr(),
Some(console_time_end),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"trace".as_ptr(),
Some(console_trace),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"assert".as_ptr(),
Some(console_assert),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"clear".as_ptr(),
Some(console_clear),
0,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"count".as_ptr(),
Some(console_count),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"countReset".as_ptr(),
Some(console_count_reset),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineFunction(
cx,
console_obj.handle(),
c"table".as_ptr(),
Some(console_table),
1,
JSPROP_ENUMERATE as u32,
);
w2::JS_DefineProperty3(
cx,
global,
c"console".as_ptr(),
console_obj.handle(),
JSPROP_ENUMERATE as u32,
);
}
}
pub trait HostObject: Sized {
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn from_private(_cx: *mut JSContext, thisv: JSVal) -> *mut Self {
if !thisv.is_object() {
return ::std::ptr::null_mut();
}
let obj = thisv.to_object();
let mut slot = UndefinedValue();
JS_GetReservedSlot(obj, HOST_OBJECT_SLOT, &mut slot);
if !(slot.is_double() && (slot.asBits_ & 0xFFFF000000000000) == 0) {
return ::std::ptr::null_mut();
}
let ptr = slot.to_private() as *mut Self;
if ptr.is_null() {
::std::ptr::null_mut()
} else {
ptr
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn to_private(&self, obj: *mut JSObject) {
let val = mozjs::jsval::PrivateValue(self as *const Self as *const ::std::os::raw::c_void);
JS_SetReservedSlot(obj, HOST_OBJECT_SLOT, &val);
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn call_function(
cx: *mut JSContext,
func: JSVal,
this_obj: *mut JSObject,
args: &[JSVal],
) -> JsResult<JsValue> {
if !func.is_object() {
return Err(JsError {
message: "value is not a function".into(),
filename: String::new(),
line: 0,
column: 0,
stack: None,
});
}
let wrapped_cx = mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(wrapped_cx) let rooted_func = func);
rooted!(&in(wrapped_cx) let rooted_this = this_obj);
let mut rooted_args: Vec<JSVal> = args.to_vec();
let handle_array = HandleValueArray {
length_: rooted_args.len(),
elements_: rooted_args.as_mut_ptr(),
};
let mut rval = UndefinedValue();
let ok = JS_CallFunctionValue(
cx,
rooted_this.handle().into(),
rooted_func.handle().into(),
&handle_array,
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut rval,
},
);
if !ok {
Err(take_exception(cx))
} else {
Ok(value::jsval_to_jsvalue(cx, rval))
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn call_method(
cx: *mut JSContext,
obj: *mut JSObject,
name: &str,
args: &[JSVal],
) -> JsResult<JsValue> {
let c_name = ::std::ffi::CString::new(name).unwrap_or_default();
let cx_ref = &mut mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(cx_ref) let obj_root = obj);
let mut func_val = UndefinedValue();
JS_GetProperty(
cx,
obj_root.handle().into(),
c_name.as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut func_val,
},
);
call_function(cx, func_val, obj, args)
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn take_exception(cx: *mut JSContext) -> JsError {
if !JS_IsExceptionPending(cx) {
return JsError {
message: "unknown error".into(),
filename: String::new(),
line: 0,
column: 0,
stack: None,
};
}
let mut exc = UndefinedValue();
JS_GetPendingException(
cx,
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut exc,
},
);
JS_ClearPendingException(cx);
if !exc.is_object() {
return JsError {
message: "non-object exception".into(),
filename: String::new(),
line: 0,
column: 0,
stack: None,
};
}
let obj = exc.to_object();
let cx_ref = &mut mozjs::context::JSContext::from_ptr(NonNull::new_unchecked(cx));
rooted!(&in(cx_ref) let obj_root = obj);
let obj_h = obj_root.handle().into();
let message = get_string_property(cx, obj_h, "message").unwrap_or_else(|| "error".into());
let filename = get_string_property(cx, obj_h, "fileName").unwrap_or_else(|| "<unknown>".into());
let line = get_int_property(cx, obj_h, "lineNumber").unwrap_or(0);
let column = get_int_property(cx, obj_h, "columnNumber").unwrap_or(0);
let stack = get_string_property(cx, obj_h, "stack");
JsError {
message,
filename,
line,
column,
stack,
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn check_exception(cx: *mut JSContext) -> JsResult<()> {
if JS_IsExceptionPending(cx) {
Err(take_exception(cx))
} else {
Ok(())
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn extract_setter_value(cx: *mut JSContext, args: &CallArgs) -> JsValue {
if args.argc_ > 0 {
crate::value::jsval_to_jsvalue(cx, *args.get(0).ptr)
} else {
JsValue::Undefined
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_string_property(
cx: *mut JSContext,
obj_h: Handle<*mut JSObject>,
name: &str,
) -> ::std::option::Option<String> {
let c_name = ::std::ffi::CString::new(name).unwrap_or_default();
let mut val = UndefinedValue();
JS_GetProperty(
cx,
obj_h,
c_name.as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut val,
},
);
if val.is_string() {
let s = val.to_string();
if !s.is_null() {
Some(unsafe_jsstr_to_string(cx, NonNull::new(s)?))
} else {
None
}
} else {
None
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_int_property(
cx: *mut JSContext,
obj_h: Handle<*mut JSObject>,
name: &str,
) -> ::std::option::Option<u32> {
let c_name = ::std::ffi::CString::new(name).unwrap_or_default();
let mut val = UndefinedValue();
JS_GetProperty(
cx,
obj_h,
c_name.as_ptr(),
MutableHandle::<Value> {
_phantom_0: ::std::marker::PhantomData,
ptr: &mut val,
},
);
if val.is_int32() {
Some(val.to_int32() as u32)
} else if val.is_double() {
Some(val.to_double() as u32)
} else {
None
}
}
impl JsError {
pub unsafe fn throw_on(&self, cx: *mut JSContext) {
let msg = ::std::ffi::CString::new(self.message.as_str())
.unwrap_or_else(|_| ::std::ffi::CString::new("error").unwrap());
unsafe {
JS_ReportErrorUTF8(cx, c"%s".as_ptr(), msg.as_ptr());
}
}
}
impl JsValue {
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn set_as_rval(self, cx: *mut JSContext, args: &mut CallArgs) {
args.rval().set(self.to_jsval(cx));
}
}
use ::std::cell::RefCell;
use ::std::collections::HashMap;
use ::std::time::Instant;
thread_local! {
static CONSOLE_TIMERS: RefCell<HashMap<String, Instant>> = RefCell::new(HashMap::new());
static CONSOLE_COUNTERS: RefCell<HashMap<String, u32>> = RefCell::new(HashMap::new());
}
fn ensure_output_source() {
bun_core::output::Source::ensure_thread_source();
}
fn console_out(line: &str) {
ensure_output_source();
let mut bytes = Vec::with_capacity(line.len() + 1);
bytes.extend_from_slice(line.as_bytes());
bytes.push(b'\n');
bun_core::output::write_bytes(bun_core::output::Destination::Stdout, &bytes);
}
fn console_err(line: &str) {
ensure_output_source();
let mut bytes = Vec::with_capacity(line.len() + 1);
bytes.extend_from_slice(line.as_bytes());
bytes.push(b'\n');
bun_core::output::write_bytes(bun_core::output::Destination::Stderr, &bytes);
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_log(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let args_slice = ::std::slice::from_raw_parts(args.argv_, argc as usize);
let line = format_args_line(cx, args_slice);
console_out(&line);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn format_args_line(cx: *mut JSContext, args_slice: &[JSVal]) -> String {
let mut parts = Vec::with_capacity(args_slice.len());
for val in args_slice {
parts.push(format_value(cx, *val));
}
parts.join(" ")
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn format_value(cx: *mut JSContext, val: JSVal) -> String {
if val.is_undefined() {
"undefined".into()
} else if val.is_null() {
"null".into()
} else if val.is_boolean() {
format!("{}", val.to_boolean())
} else if val.is_int32() {
format!("{}", val.to_int32())
} else if val.is_double() {
let d = val.to_double();
if d.is_nan() {
"NaN".into()
} else if d.is_infinite() {
format!("{}", if d > 0.0 { "Infinity" } else { "-Infinity" })
} else {
format!("{}", d)
}
} else if val.is_string() {
let s = val.to_string();
if !s.is_null() {
unsafe_jsstr_to_string(cx, NonNull::new(s).expect("null-checked JSString"))
} else {
String::new()
}
} else if val.is_object() {
"[object Object]".into()
} else {
String::new()
}
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_error(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let args_slice = ::std::slice::from_raw_parts(args.argv_, argc as usize);
let line = format_args_line(cx, args_slice);
console_err(&line);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_warn(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let args_slice = ::std::slice::from_raw_parts(args.argv_, argc as usize);
let line = format_args_line(cx, args_slice);
console_err(&line);
args.rval().set(UndefinedValue());
true
}
unsafe extern "C" fn console_info(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
unsafe { console_log(cx, argc, vp) }
}
unsafe extern "C" fn console_debug(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
unsafe { console_log(cx, argc, vp) }
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_dir(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc > 0 {
let s = format_value(cx, *args.get(0).ptr);
console_out(&s);
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_time(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let label = extract_label(cx, argc, &args);
CONSOLE_TIMERS.with(|t| t.borrow_mut().insert(label, Instant::now()));
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_time_end(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let label = extract_label(cx, argc, &args);
let elapsed = CONSOLE_TIMERS
.with(|t| t.borrow_mut().remove(&label))
.map(|start| start.elapsed());
if let Some(d) = elapsed {
console_out(&format!("{}: {:.3}ms", label, d.as_secs_f64() * 1000.0));
} else {
console_err(&format!(
"Warning: No such label '{}' for console.timeEnd()",
label
));
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_trace(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let args_slice = ::std::slice::from_raw_parts(args.argv_, argc as usize);
let label = if args_slice.is_empty() {
String::new()
} else {
format!(" {}", format_args_line(cx, args_slice))
};
console_err(&format!("Trace{}:", label));
console_err(" at <anonymous>");
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_assert(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
if argc > 0 {
let cond = *args.get(0).ptr;
if !cond.is_boolean() || !cond.to_boolean() {
let args_slice = ::std::slice::from_raw_parts(args.argv_, argc as usize);
let mut msg = String::from("Assertion failed");
let extra: Vec<String> = args_slice
.iter()
.skip(1)
.map(|val| format_value(cx, *val))
.collect();
if !extra.is_empty() {
msg.push_str(": ");
msg.push_str(&extra.join(" "));
}
console_err(&msg);
}
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_clear(_cx: *mut JSContext, _argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, _argc);
ensure_output_source();
bun_core::output::write_bytes(
bun_core::output::Destination::Stdout,
b"\x1b[2J\x1b[H",
);
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_count(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let label = extract_label(cx, argc, &args);
let count = CONSOLE_COUNTERS.with(|c| {
let mut map = c.borrow_mut();
let entry = map.entry(label.clone()).or_insert(0);
*entry += 1;
*entry
});
console_out(&format!("{}: {}", label, count));
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_count_reset(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
let args = CallArgs::from_vp(vp, argc);
let label = extract_label(cx, argc, &args);
let existed = CONSOLE_COUNTERS.with(|c| {
let mut map = c.borrow_mut();
if map.contains_key(&label) {
map.insert(label.clone(), 0);
true
} else {
false
}
});
if !existed {
console_err(&format!("Warning: Count for '{}' does not exist", label));
}
args.rval().set(UndefinedValue());
true
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe extern "C" fn console_table(cx: *mut JSContext, argc: u32, vp: *mut JSVal) -> bool {
console_log(cx, argc, vp)
}
#[allow(unsafe_op_in_unsafe_fn)]
unsafe fn extract_label(cx: *mut JSContext, argc: u32, args: &CallArgs) -> String {
if argc > 0 && (*args.get(0).ptr).is_string() {
let s = (*args.get(0).ptr).to_string();
if !s.is_null() {
unsafe_jsstr_to_string(cx, NonNull::new(s).expect("null-checked JSString"))
} else {
"default".into()
}
} else {
"default".into()
}
}
pub struct ArgReader<'a> {
cx: *mut JSContext,
args: &'a CallArgs,
}
impl<'a> ArgReader<'a> {
pub unsafe fn new(cx: *mut JSContext, args: &'a CallArgs) -> Self {
ArgReader { cx, args }
}
pub fn argc(&self) -> u32 {
self.args.argc_
}
pub fn has(&self, index: u32) -> bool {
index < self.args.argc_
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_raw(&self, index: u32) -> JSVal {
if index < self.args.argc_ {
*self.args.get(index).ptr
} else {
UndefinedValue()
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_string(&self, index: u32) -> ::std::string::String {
let val = self.get_raw(index);
if val.is_string() {
let s = val.to_string();
if !s.is_null() {
unsafe_jsstr_to_string(self.cx, NonNull::new(s).expect("null-checked"))
} else {
::std::string::String::new()
}
} else {
::std::string::String::new()
}
}
pub unsafe fn get_optional_string(
&self,
index: u32,
) -> ::std::option::Option<::std::string::String> {
if !self.has(index) {
return None;
}
let s = unsafe { self.get_string(index) };
if s.is_empty() { None } else { Some(s) }
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_int(&self, index: u32) -> i32 {
let val = self.get_raw(index);
if val.is_int32() {
val.to_int32()
} else if val.is_double() {
val.to_double() as i32
} else {
0
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_f64(&self, index: u32) -> f64 {
let val = self.get_raw(index);
if val.is_int32() {
val.to_int32() as f64
} else if val.is_double() {
val.to_double()
} else {
0.0
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_bool(&self, index: u32) -> bool {
let val = self.get_raw(index);
if val.is_boolean() {
val.to_boolean()
} else {
false
}
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn get_object(&self, index: u32) -> *mut JSObject {
let val = self.get_raw(index);
if val.is_object() {
val.to_object()
} else {
::std::ptr::null_mut()
}
}
pub unsafe fn get_value(&self, index: u32) -> JsValue {
unsafe { value::jsval_to_jsvalue(self.cx, self.get_raw(index)) }
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn return_undefined(&self) {
self.args.rval().set(UndefinedValue());
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn return_value(&self, val: JsValue) {
self.args.rval().set(val.to_jsval(self.cx));
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn return_bool(&self, v: bool) {
self.args.rval().set(mozjs::jsval::BooleanValue(v));
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn return_int(&self, v: i32) {
self.args.rval().set(mozjs::jsval::Int32Value(v));
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn return_f64(&self, v: f64) {
self.args.rval().set(mozjs::jsval::DoubleValue(v));
}
#[allow(unsafe_op_in_unsafe_fn)]
pub unsafe fn return_string(&self, s: &str) {
let c_str = ::std::ffi::CString::new(s).unwrap_or_default();
let js_str = JS_NewStringCopyZ(self.cx, c_str.as_ptr());
if !js_str.is_null() {
self.args.rval().set(mozjs::jsval::StringValue(&*js_str));
} else {
self.return_undefined();
}
}
pub unsafe fn throw(&self, msg: &str) -> bool {
let c_msg = ::std::ffi::CString::new(msg)
.unwrap_or_else(|_| ::std::ffi::CString::new("error").unwrap());
unsafe {
JS_ReportErrorUTF8(self.cx, c"%s".as_ptr(), c_msg.as_ptr());
}
false
}
}
#[macro_export]
macro_rules! define_host_fn {
($cx:expr, $obj:expr, $name:expr, $nargs:expr, $handler:expr) => {
unsafe {
static mut __HANDLER: ::std::option::Option<
for<'a> fn(
*mut mozjs::jsapi::JSContext,
&'a $crate::host_fn::ArgReader<'a>,
) -> bool,
> = None;
::std::ptr::write_volatile(&mut __HANDLER, Some($handler));
unsafe extern "C" fn __trampoline(
cx: *mut mozjs::jsapi::JSContext,
argc: u32,
vp: *mut mozjs::jsval::JSVal,
) -> bool {
let args = mozjs::jsapi::CallArgs::from_vp(vp, argc);
let reader = $crate::host_fn::ArgReader::new(cx, &args);
match ::std::ptr::read_volatile(&__HANDLER) {
Some(h) => h(cx, &reader),
None => {
args.rval().set(mozjs::jsval::UndefinedValue());
true
}
}
}
mozjs::rust::wrappers2::JS_DefineFunction(
$cx,
$obj,
$name.as_ptr(),
Some(__trampoline),
$nargs,
mozjs::jsapi::JSPROP_ENUMERATE as u32,
);
}
};
}