diff --git a/js/rust/build.rs b/js/rust/build.rs index d5ae2efc572f..2c1b5365711c 100644 --- a/js/rust/build.rs +++ b/js/rust/build.rs @@ -163,6 +163,7 @@ const WHITELIST_TYPES: &'static [&'static str] = &[ "js::ESClass", "JS::ForOfIterator", "JS::Handle", + "JS::HandleFunction", "JS::HandleId", "JS::HandleObject", "JS::HandleString", @@ -226,6 +227,7 @@ const WHITELIST_TYPES: &'static [&'static str] = &[ "js::shadow::Object", "js::shadow::ObjectGroup", "JS::SourceBufferHolder", + "js::StackFormat", "JSStructuredCloneCallbacks", "JS::Symbol", "JS::SymbolCode", @@ -254,6 +256,7 @@ const WHITELIST_VARS: &'static [&'static str] = &[ /// Functions we want to generate bindings to. const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "INTERNED_STRING_TO_JSID", + "ExceptionStackOrNull", "JS_AddExtraGCRootsTracer", "JS_AddInterruptCallback", "JS::AddPromiseReactions", @@ -261,7 +264,9 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "JS_AlreadyHasOwnPropertyById", "JS_AtomizeAndPinString", "js::AssertSameCompartment", + "JS::BuildStackString", "JS::Call", + "JS_CallFunctionName", "JS_CallFunctionValue", "JS::CallOriginalPromiseThen", "JS::CallOriginalPromiseResolve", @@ -288,6 +293,7 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "JS_GetObjectPrototype", "JS_GetObjectRuntime", "JS_GetOwnPropertyDescriptorById", + "JS::GetPromiseResult", "JS::GetPromiseState", "JS_GetPropertyDescriptorById", "js::GetPropertyKeys", @@ -322,6 +328,7 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "JS_EnumerateStandardClasses", "JS_ErrorFromException", "JS_FireOnNewGlobalObject", + "JS_free", "JS_GC", "JS_GetArrayBufferData", "JS_GetArrayBufferViewType", @@ -386,11 +393,13 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "JS_ReadBytes", "JS_ReadStructuredClone", "JS_ReadUint32Pair", + "JS_RemoveExtraGCRootsTracer", "js::RemoveRawValueRoot", "JS_ReportErrorASCII", "JS_ReportErrorNumberUTF8", "JS_RequestInterruptCallback", "JS_ResolveStandardClass", + "js::RunJobs", "JS_SameValue", "js::SetDOMCallbacks", "js::SetDOMProxyInformation", @@ -408,6 +417,7 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "JS_SetParallelParsingEnabled", "JS_SetPendingException", "js::SetPreserveWrapperCallback", + "JS::SetPromiseRejectionTrackerCallback", "JS_SetPrototype", "js::SetWindowProxy", "js::SetWindowProxyClass", @@ -416,6 +426,7 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "JS_SetWrapObjectCallbacks", "JS_ShutDown", "JS_SplicePrototype", + "js::StopDrainingJobQueue", "JS_StrictPropertyStub", "JS_StringEqualsAscii", "JS_StringHasLatin1Chars", @@ -449,6 +460,8 @@ const WHITELIST_FUNCTIONS: &'static [&'static str] = &[ "js::UnwrapUint32Array", "js::UnwrapUint8Array", "js::UnwrapUint8ClampedArray", + "js::UseInternalJobQueues", + "JS_ValueToFunction", ]; /// Types that should be treated as an opaque blob of bytes whenever they show diff --git a/js/rust/src/conversions.rs b/js/rust/src/conversions.rs index 05fd4394a81d..59eb43252ff1 100644 --- a/js/rust/src/conversions.rs +++ b/js/rust/src/conversions.rs @@ -102,6 +102,17 @@ pub enum ConversionResult { } impl ConversionResult { + /// Map a function over the `Success` value. + pub fn map(self, mut f: F) -> ConversionResult + where + F: FnMut(T) -> U + { + match self { + ConversionResult::Success(t) => ConversionResult::Success(f(t)), + ConversionResult::Failure(e) => ConversionResult::Failure(e), + } + } + /// Returns Some(value) if it is `ConversionResult::Success`. pub fn get_success_value(&self) -> Option<&T> { match *self { @@ -137,6 +148,53 @@ pub enum ConversionBehavior { Clamp, } +/// Use `T` with `ConversionBehavior::Default` but without requiring any +/// `Config` associated type. +pub struct Default(pub T); + +impl FromJSValConvertible for Default +where + T: FromJSValConvertible +{ + type Config = (); + unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _: ()) + -> Result, ()> { + T::from_jsval(cx, val, ConversionBehavior::Default).map(|conv| conv.map(Default)) + } +} + +/// Use `T` with `ConversionBehavior::EnforceRange` but without requiring any +/// `Config` associated type. +pub struct EnforceRange(pub T); + +impl FromJSValConvertible for EnforceRange + where + T: FromJSValConvertible +{ + type Config = (); + unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _: ()) + -> Result, ()> { + T::from_jsval(cx, val, ConversionBehavior::EnforceRange) + .map(|conv| conv.map(EnforceRange)) + } +} + +/// Use `T` with `ConversionBehavior::Clamp` but without requiring any `Config` +/// associated type. +pub struct Clamp(pub T); + +impl FromJSValConvertible for Clamp + where + T: FromJSValConvertible +{ + type Config = (); + unsafe fn from_jsval(cx: *mut JSContext, val: JS::HandleValue, _: ()) + -> Result, ()> { + T::from_jsval(cx, val, ConversionBehavior::Clamp) + .map(|conv| conv.map(Clamp)) + } +} + /// Try to cast the number to a smaller type, but /// if it doesn't fit, it will return an error. unsafe fn enforce_range(cx: *mut JSContext, d: f64) -> Result, ()> @@ -677,3 +735,55 @@ impl ToJSValConvertible for Heap<*mut JSObject> { maybe_wrap_object_or_null_value(cx, rval); } } + +// JSFunction + +impl ToJSValConvertible for *mut JSFunction { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) { + rval.set(ObjectOrNullValue(*self as *mut JSObject)); + maybe_wrap_object_or_null_value(cx, rval); + } +} + +#[cfg(feature = "nonzero")] +impl ToJSValConvertible for NonZero<*mut JSFunction> { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) { + use rust::maybe_wrap_object_value; + rval.set(ObjectValue(self.get() as *mut JSObject)); + maybe_wrap_object_value(cx, rval); + } +} + +impl ToJSValConvertible for Heap<*mut JSFunction> { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) { + rval.set(ObjectOrNullValue(self.get() as *mut JSObject)); + maybe_wrap_object_or_null_value(cx, rval); + } +} + +impl ToJSValConvertible for JS::Handle<*mut JSFunction> { + #[inline] + unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) { + rval.set(ObjectOrNullValue(self.get() as *mut JSObject)); + maybe_wrap_object_or_null_value(cx, rval); + } +} + +impl FromJSValConvertible for *mut JSFunction { + type Config = (); + + unsafe fn from_jsval(cx: *mut JSContext, + val: JS::HandleValue, + _: ()) + -> Result, ()> { + let func = JS_ValueToFunction(cx, val); + if func.is_null() { + Ok(ConversionResult::Failure("value is not a function".into())) + } else { + Ok(ConversionResult::Success(func)) + } + } +} diff --git a/js/rust/src/rust.rs b/js/rust/src/rust.rs index 5e1415f699d1..0274b1886194 100644 --- a/js/rust/src/rust.rs +++ b/js/rust/src/rust.rs @@ -84,7 +84,11 @@ impl Runtime { } /// Creates a new `JSContext`. - pub fn new() -> Result { + /// + /// * `use_internal_job_queue`: If `true`, then SpiderMonkey's internal + /// micro-task job queue is used. If `false`, then it is up to you to + /// implement micro-tasks yourself. + pub fn new(use_internal_job_queue: bool) -> Result { if SHUT_DOWN.load(Ordering::SeqCst) { return Err(()); } @@ -177,6 +181,10 @@ impl Runtime { context.set(js_context); }); + if use_internal_job_queue { + assert!(js::UseInternalJobQueues(js_context, false)); + } + JS::InitSelfHostedCode(js_context); JS::SetWarningReporter(js_context, Some(report_warning)); @@ -1098,3 +1106,126 @@ pub unsafe fn maybe_wrap_value(cx: *mut JSContext, rval: JS::MutableHandleValue) maybe_wrap_object_value(cx, rval); } } + +/// Equivalents of the JS_FN* macros. +impl JSFunctionSpec { + pub fn js_fs(name: *const ::std::os::raw::c_char, + func: JSNative, + nargs: u16, + flags: u16) -> JSFunctionSpec { + JSFunctionSpec { + name: name, + call: JSNativeWrapper { + op: func, + info: ptr::null(), + }, + nargs: nargs, + flags: flags, + selfHostedName: 0 as *const _, + } + } + + pub fn js_fn(name: *const ::std::os::raw::c_char, + func: JSNative, + nargs: u16, + flags: u16) -> JSFunctionSpec { + JSFunctionSpec { + name: name, + call: JSNativeWrapper { + op: func, + info: ptr::null(), + }, + nargs: nargs, + flags: flags, + selfHostedName: 0 as *const _, + } + } + + pub const NULL: JSFunctionSpec = JSFunctionSpec { + name: 0 as *const _, + call: JSNativeWrapper { + op: None, + info: 0 as *const _, + }, + nargs: 0, + flags: 0, + selfHostedName: 0 as *const _, + }; +} + +/// Equivalents of the JS_PS* macros. +impl JSPropertySpec { + pub fn getter(name: *const ::std::os::raw::c_char, flags: u8, func: JSNative) + -> JSPropertySpec { + debug_assert_eq!(flags & !(JSPROP_ENUMERATE | JSPROP_PERMANENT), 0); + JSPropertySpec { + name: name, + flags: flags, + __bindgen_anon_1: JSPropertySpec__bindgen_ty_1 { + accessors: JSPropertySpec__bindgen_ty_1__bindgen_ty_1 { + getter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 { + native: JSNativeWrapper { + op: func, + info: ptr::null(), + }, + }, + setter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 { + native: JSNativeWrapper { + op: None, + info: ptr::null(), + }, + } + } + } + } + } + + pub fn getter_setter(name: *const ::std::os::raw::c_char, + flags: u8, + g_f: JSNative, + s_f: JSNative) + -> JSPropertySpec { + debug_assert_eq!(flags & !(JSPROP_ENUMERATE | JSPROP_PERMANENT), 0); + JSPropertySpec { + name: name, + flags: flags, + __bindgen_anon_1: JSPropertySpec__bindgen_ty_1 { + accessors: JSPropertySpec__bindgen_ty_1__bindgen_ty_1 { + getter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 { + native: JSNativeWrapper { + op: g_f, + info: ptr::null(), + }, + }, + setter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 { + native: JSNativeWrapper { + op: s_f, + info: ptr::null(), + }, + } + } + } + } + } + + pub const NULL: JSPropertySpec = JSPropertySpec { + name: 0 as *const _, + flags: 0, + __bindgen_anon_1: JSPropertySpec__bindgen_ty_1{ + accessors: JSPropertySpec__bindgen_ty_1__bindgen_ty_1 { + getter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_1 { + native: JSNativeWrapper { + op: None, + info: 0 as *const _, + }, + }, + setter: JSPropertySpec__bindgen_ty_1__bindgen_ty_1__bindgen_ty_2 { + native: JSNativeWrapper { + op: None, + info: 0 as *const _, + }, + } + } + } + }; +} diff --git a/js/rust/tests/callback.rs b/js/rust/tests/callback.rs index 1beae8ae30b2..10b800ed24e4 100644 --- a/js/rust/tests/callback.rs +++ b/js/rust/tests/callback.rs @@ -25,7 +25,7 @@ use std::str; #[test] fn callback() { - let runtime = Runtime::new().unwrap(); + let runtime = Runtime::new(false).unwrap(); let context = runtime.cx(); let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook; let c_option = CompartmentOptions::default(); diff --git a/js/rust/tests/enumerate.rs b/js/rust/tests/enumerate.rs index 4ad82297fd02..1c1563c11f80 100644 --- a/js/rust/tests/enumerate.rs +++ b/js/rust/tests/enumerate.rs @@ -21,7 +21,7 @@ use std::ptr; #[test] fn enumerate() { - let rt = Runtime::new().unwrap(); + let rt = Runtime::new(false).unwrap(); let cx = rt.cx(); unsafe { diff --git a/js/rust/tests/evaluate.rs b/js/rust/tests/evaluate.rs index b208bc984760..738cc5475e22 100644 --- a/js/rust/tests/evaluate.rs +++ b/js/rust/tests/evaluate.rs @@ -15,7 +15,7 @@ use std::ptr; #[test] fn evaluate() { - let rt = Runtime::new().unwrap(); + let rt = Runtime::new(false).unwrap(); let cx = rt.cx(); unsafe { diff --git a/js/rust/tests/panic.rs b/js/rust/tests/panic.rs index 63ac73ad2e99..079e27bb1b9e 100644 --- a/js/rust/tests/panic.rs +++ b/js/rust/tests/panic.rs @@ -15,7 +15,7 @@ use std::str; #[test] #[should_panic] fn panic() { - let runtime = Runtime::new().unwrap(); + let runtime = Runtime::new(false).unwrap(); let context = runtime.cx(); let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook; let c_option = JS::CompartmentOptions::default(); diff --git a/js/rust/tests/rooting.rs b/js/rust/tests/rooting.rs index 39410f914ae4..1d7846623716 100644 --- a/js/rust/tests/rooting.rs +++ b/js/rust/tests/rooting.rs @@ -17,7 +17,7 @@ use std::ptr; #[test] fn rooting() { unsafe { - let runtime = Runtime::new().unwrap(); + let runtime = Runtime::new(false).unwrap(); JS_SetGCZeal(runtime.cx(), 2, 1); let cx = runtime.cx(); diff --git a/js/rust/tests/runtime.rs b/js/rust/tests/runtime.rs index a04cf5665d20..06cf39055bfd 100644 --- a/js/rust/tests/runtime.rs +++ b/js/rust/tests/runtime.rs @@ -13,7 +13,7 @@ use std::ptr; #[test] fn runtime() { unsafe { - let runtime = Runtime::new().unwrap(); + let runtime = Runtime::new(false).unwrap(); let cx = runtime.cx(); let h_option = JS::OnNewGlobalHookOption::FireOnNewGlobalHook; @@ -28,7 +28,7 @@ fn runtime() { rooted!(in(cx) let _object = JS_NewObject(cx, &CLASS as *const _)); } - assert!(Runtime::new().is_err()); + assert!(Runtime::new(false).is_err()); } unsafe extern fn finalize(_fop: *mut JSFreeOp, _object: *mut JSObject) { diff --git a/js/rust/tests/stack_limit.rs b/js/rust/tests/stack_limit.rs index 9abbc35035dd..6312f352e33f 100644 --- a/js/rust/tests/stack_limit.rs +++ b/js/rust/tests/stack_limit.rs @@ -15,7 +15,7 @@ use std::ptr; #[test] fn stack_limit() { - let rt = Runtime::new().unwrap(); + let rt = Runtime::new(false).unwrap(); let cx = rt.cx(); unsafe { diff --git a/js/rust/tests/typedarray.rs b/js/rust/tests/typedarray.rs index 8633528b7631..e384dc9c4873 100644 --- a/js/rust/tests/typedarray.rs +++ b/js/rust/tests/typedarray.rs @@ -14,7 +14,7 @@ use std::ptr; #[test] fn typedarray() { - let rt = Runtime_::new().unwrap(); + let rt = Runtime_::new(false).unwrap(); let cx = rt.cx(); unsafe { @@ -72,7 +72,7 @@ fn typedarray() { #[test] #[should_panic] fn typedarray_update_panic() { - let rt = Runtime_::new().unwrap(); + let rt = Runtime_::new(false).unwrap(); let cx = rt.cx(); unsafe { diff --git a/js/rust/tests/vec_conversion.rs b/js/rust/tests/vec_conversion.rs index 50fe06816582..c5a4f122846b 100644 --- a/js/rust/tests/vec_conversion.rs +++ b/js/rust/tests/vec_conversion.rs @@ -30,7 +30,7 @@ fn assert_is_array(cx: *mut js::jsapi::root::JSContext, #[test] fn vec_conversion() { - let rt = Runtime::new().unwrap(); + let rt = Runtime::new(false).unwrap(); let cx = rt.cx(); let h_option = OnNewGlobalHookOption::FireOnNewGlobalHook;