summaryrefslogtreecommitdiffstats
path: root/js/rust/src
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 14:29:10 +0000
commit2aa4a82499d4becd2284cdb482213d541b8804dd (patch)
treeb80bf8bf13c3766139fbacc530efd0dd9d54394c /js/rust/src
parentInitial commit. (diff)
downloadfirefox-2aa4a82499d4becd2284cdb482213d541b8804dd.tar.xz
firefox-2aa4a82499d4becd2284cdb482213d541b8804dd.zip
Adding upstream version 86.0.1.upstream/86.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/rust/src')
-rw-r--r--js/rust/src/ar.rs18
-rw-r--r--js/rust/src/conversions.rs848
-rw-r--r--js/rust/src/error.rs78
-rw-r--r--js/rust/src/glue.rs474
-rw-r--r--js/rust/src/heap.rs179
-rw-r--r--js/rust/src/jsapi.rs9
-rw-r--r--js/rust/src/jsglue.cpp746
-rw-r--r--js/rust/src/jsval.rs532
-rw-r--r--js/rust/src/lib.rs53
-rw-r--r--js/rust/src/panic.rs34
-rw-r--r--js/rust/src/rust.rs1312
-rw-r--r--js/rust/src/sc.rs102
-rw-r--r--js/rust/src/typedarray.rs325
13 files changed, 4710 insertions, 0 deletions
diff --git a/js/rust/src/ar.rs b/js/rust/src/ar.rs
new file mode 100644
index 0000000000..59e26ee57d
--- /dev/null
+++ b/js/rust/src/ar.rs
@@ -0,0 +1,18 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use jsapi::root::*;
+
+#[derive(Debug)]
+pub struct AutoRealm(JSAutoRealm);
+
+impl AutoRealm {
+ pub unsafe fn with_obj(cx: *mut JSContext, target: *mut JSObject) -> AutoRealm {
+ AutoRealm(JSAutoRealm::new(cx, target))
+ }
+
+ pub unsafe fn with_script(cx: *mut JSContext, target: *mut JSScript) -> AutoRealm {
+ AutoRealm(JSAutoRealm::new1(cx, target))
+ }
+}
diff --git a/js/rust/src/conversions.rs b/js/rust/src/conversions.rs
new file mode 100644
index 0000000000..199ff376ab
--- /dev/null
+++ b/js/rust/src/conversions.rs
@@ -0,0 +1,848 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Conversions of Rust values to and from `JSVal`.
+//!
+//! | IDL type | Type |
+//! |-------------------------|----------------------------------|
+//! | any | `JSVal` |
+//! | boolean | `bool` |
+//! | byte | `i8` |
+//! | octet | `u8` |
+//! | short | `i16` |
+//! | unsigned short | `u16` |
+//! | long | `i32` |
+//! | unsigned long | `u32` |
+//! | long long | `i64` |
+//! | unsigned long long | `u64` |
+//! | unrestricted float | `f32` |
+//! | float | `Finite<f32>` |
+//! | unrestricted double | `f64` |
+//! | double | `Finite<f64>` |
+//! | USVString | `String` |
+//! | object | `*mut JSObject` |
+//! | nullable types | `Option<T>` |
+//! | sequences | `Vec<T>` |
+
+#![deny(missing_docs)]
+
+#[cfg(feature = "nonzero")]
+use core::nonzero::NonZero;
+
+use error::throw_type_error;
+use glue::RUST_JS_NumberValue;
+use heap::Heap;
+use jsapi::root::*;
+use jsval::{BooleanValue, Int32Value, NullValue, UInt32Value, UndefinedValue};
+use jsval::{ObjectOrNullValue, ObjectValue, StringValue};
+use libc;
+use num_traits::{Bounded, Zero};
+use rust::{maybe_wrap_object_or_null_value, maybe_wrap_value, ToString};
+use rust::{ToBoolean, ToInt32, ToInt64, ToNumber, ToUint16, ToUint32, ToUint64};
+use std::borrow::Cow;
+use std::rc::Rc;
+use std::{ptr, slice};
+
+trait As<O>: Copy {
+ fn cast(self) -> O;
+}
+
+macro_rules! impl_as {
+ ($I:ty, $O:ty) => {
+ impl As<$O> for $I {
+ fn cast(self) -> $O {
+ self as $O
+ }
+ }
+ };
+}
+
+impl_as!(f64, u8);
+impl_as!(f64, u16);
+impl_as!(f64, u32);
+impl_as!(f64, u64);
+impl_as!(f64, i8);
+impl_as!(f64, i16);
+impl_as!(f64, i32);
+impl_as!(f64, i64);
+
+impl_as!(u8, f64);
+impl_as!(u16, f64);
+impl_as!(u32, f64);
+impl_as!(u64, f64);
+impl_as!(i8, f64);
+impl_as!(i16, f64);
+impl_as!(i32, f64);
+impl_as!(i64, f64);
+
+impl_as!(i32, i8);
+impl_as!(i32, u8);
+impl_as!(i32, i16);
+impl_as!(u16, u16);
+impl_as!(i32, i32);
+impl_as!(u32, u32);
+impl_as!(i64, i64);
+impl_as!(u64, u64);
+
+/// A trait to convert Rust types to `JSVal`s.
+pub trait ToJSValConvertible {
+ /// Convert `self` to a `JSVal`. JSAPI failure causes a panic.
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue);
+}
+
+/// An enum to better support enums through FromJSValConvertible::from_jsval.
+#[derive(PartialEq, Eq, Clone, Debug)]
+pub enum ConversionResult<T> {
+ /// Everything went fine.
+ Success(T),
+ /// Conversion failed, without a pending exception.
+ Failure(Cow<'static, str>),
+}
+
+impl<T> ConversionResult<T> {
+ /// Map a function over the `Success` value.
+ pub fn map<F, U>(self, mut f: F) -> ConversionResult<U>
+ 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 {
+ ConversionResult::Success(ref v) => Some(v),
+ _ => None,
+ }
+ }
+}
+
+/// A trait to convert `JSVal`s to Rust types.
+pub trait FromJSValConvertible: Sized {
+ /// Optional configurable behaviour switch; use () for no configuration.
+ type Config;
+ /// Convert `val` to type `Self`.
+ /// Optional configuration of type `T` can be passed as the `option`
+ /// argument.
+ /// If it returns `Err(())`, a JSAPI exception is pending.
+ /// If it returns `Ok(Failure(reason))`, there is no pending JSAPI exception.
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: Self::Config,
+ ) -> Result<ConversionResult<Self>, ()>;
+}
+
+/// Behavior for converting out-of-range integers.
+#[derive(PartialEq, Eq, Clone)]
+pub enum ConversionBehavior {
+ /// Wrap into the integer's range.
+ Default,
+ /// Throw an exception.
+ EnforceRange,
+ /// Clamp into the integer's range.
+ Clamp,
+}
+
+/// Use `T` with `ConversionBehavior::Default` but without requiring any
+/// `Config` associated type.
+pub struct Default<T>(pub T);
+
+impl<T> FromJSValConvertible for Default<T>
+where
+ T: FromJSValConvertible<Config = ConversionBehavior>,
+{
+ type Config = ();
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ _: (),
+ ) -> Result<ConversionResult<Self>, ()> {
+ 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<T>(pub T);
+
+impl<T> FromJSValConvertible for EnforceRange<T>
+where
+ T: FromJSValConvertible<Config = ConversionBehavior>,
+{
+ type Config = ();
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ _: (),
+ ) -> Result<ConversionResult<Self>, ()> {
+ 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<T>(pub T);
+
+impl<T> FromJSValConvertible for Clamp<T>
+where
+ T: FromJSValConvertible<Config = ConversionBehavior>,
+{
+ type Config = ();
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ _: (),
+ ) -> Result<ConversionResult<Self>, ()> {
+ 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<D>(cx: *mut JSContext, d: f64) -> Result<ConversionResult<D>, ()>
+where
+ D: Bounded + As<f64>,
+ f64: As<D>,
+{
+ if d.is_infinite() {
+ throw_type_error(cx, "value out of range in an EnforceRange argument");
+ return Err(());
+ }
+
+ let rounded = d.round();
+ if D::min_value().cast() <= rounded && rounded <= D::max_value().cast() {
+ Ok(ConversionResult::Success(rounded.cast()))
+ } else {
+ throw_type_error(cx, "value out of range in an EnforceRange argument");
+ Err(())
+ }
+}
+
+/// Try to cast the number to a smaller type, but if it doesn't fit,
+/// round it to the MAX or MIN of the source type before casting it to
+/// the destination type.
+fn clamp_to<D>(d: f64) -> D
+where
+ D: Bounded + As<f64> + Zero,
+ f64: As<D>,
+{
+ if d.is_nan() {
+ D::zero()
+ } else if d > D::max_value().cast() {
+ D::max_value()
+ } else if d < D::min_value().cast() {
+ D::min_value()
+ } else {
+ d.cast()
+ }
+}
+
+// https://heycam.github.io/webidl/#es-void
+impl ToJSValConvertible for () {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(UndefinedValue());
+ }
+}
+
+impl FromJSValConvertible for JS::HandleValue {
+ type Config = ();
+ #[inline]
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ value: JS::HandleValue,
+ _option: (),
+ ) -> Result<ConversionResult<JS::HandleValue>, ()> {
+ if value.is_object() {
+ js::AssertSameCompartment(cx, value.to_object());
+ }
+ Ok(ConversionResult::Success(value))
+ }
+}
+
+impl FromJSValConvertible for JS::Value {
+ type Config = ();
+ unsafe fn from_jsval(
+ _cx: *mut JSContext,
+ value: JS::HandleValue,
+ _option: (),
+ ) -> Result<ConversionResult<JS::Value>, ()> {
+ Ok(ConversionResult::Success(value.get()))
+ }
+}
+
+impl FromJSValConvertible for Heap<JS::Value> {
+ type Config = ();
+ unsafe fn from_jsval(
+ _cx: *mut JSContext,
+ value: JS::HandleValue,
+ _option: (),
+ ) -> Result<ConversionResult<Self>, ()> {
+ Ok(ConversionResult::Success(Heap::<JS::Value>::new(
+ value.get(),
+ )))
+ }
+}
+
+impl ToJSValConvertible for JS::Value {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(*self);
+ maybe_wrap_value(cx, rval);
+ }
+}
+
+impl ToJSValConvertible for JS::HandleValue {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(self.get());
+ maybe_wrap_value(cx, rval);
+ }
+}
+
+impl ToJSValConvertible for Heap<JS::Value> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(self.get());
+ maybe_wrap_value(cx, rval);
+ }
+}
+
+#[inline]
+unsafe fn convert_int_from_jsval<T, M>(
+ cx: *mut JSContext,
+ value: JS::HandleValue,
+ option: ConversionBehavior,
+ convert_fn: unsafe fn(*mut JSContext, JS::HandleValue) -> Result<M, ()>,
+) -> Result<ConversionResult<T>, ()>
+where
+ T: Bounded + Zero + As<f64>,
+ M: Zero + As<T>,
+ f64: As<T>,
+{
+ match option {
+ ConversionBehavior::Default => Ok(ConversionResult::Success(
+ try!(convert_fn(cx, value)).cast(),
+ )),
+ ConversionBehavior::EnforceRange => enforce_range(cx, try!(ToNumber(cx, value))),
+ ConversionBehavior::Clamp => Ok(ConversionResult::Success(clamp_to(try!(ToNumber(
+ cx, value
+ ))))),
+ }
+}
+
+// https://heycam.github.io/webidl/#es-boolean
+impl ToJSValConvertible for bool {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(BooleanValue(*self));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-boolean
+impl FromJSValConvertible for bool {
+ type Config = ();
+ unsafe fn from_jsval(
+ _cx: *mut JSContext,
+ val: JS::HandleValue,
+ _option: (),
+ ) -> Result<ConversionResult<bool>, ()> {
+ Ok(ToBoolean(val)).map(ConversionResult::Success)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-byte
+impl ToJSValConvertible for i8 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(Int32Value(*self as i32));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-byte
+impl FromJSValConvertible for i8 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<i8>, ()> {
+ convert_int_from_jsval(cx, val, option, ToInt32)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-octet
+impl ToJSValConvertible for u8 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(Int32Value(*self as i32));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-octet
+impl FromJSValConvertible for u8 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<u8>, ()> {
+ convert_int_from_jsval(cx, val, option, ToInt32)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-short
+impl ToJSValConvertible for i16 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(Int32Value(*self as i32));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-short
+impl FromJSValConvertible for i16 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<i16>, ()> {
+ convert_int_from_jsval(cx, val, option, ToInt32)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-short
+impl ToJSValConvertible for u16 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(Int32Value(*self as i32));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-short
+impl FromJSValConvertible for u16 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<u16>, ()> {
+ convert_int_from_jsval(cx, val, option, ToUint16)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-long
+impl ToJSValConvertible for i32 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(Int32Value(*self));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-long
+impl FromJSValConvertible for i32 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<i32>, ()> {
+ convert_int_from_jsval(cx, val, option, ToInt32)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long
+impl ToJSValConvertible for u32 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(UInt32Value(*self));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long
+impl FromJSValConvertible for u32 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<u32>, ()> {
+ convert_int_from_jsval(cx, val, option, ToUint32)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-long-long
+impl ToJSValConvertible for i64 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(RUST_JS_NumberValue(*self as f64));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-long-long
+impl FromJSValConvertible for i64 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<i64>, ()> {
+ convert_int_from_jsval(cx, val, option, ToInt64)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long-long
+impl ToJSValConvertible for u64 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(RUST_JS_NumberValue(*self as f64));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-unsigned-long-long
+impl FromJSValConvertible for u64 {
+ type Config = ConversionBehavior;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ option: ConversionBehavior,
+ ) -> Result<ConversionResult<u64>, ()> {
+ convert_int_from_jsval(cx, val, option, ToUint64)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-float
+impl ToJSValConvertible for f32 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(RUST_JS_NumberValue(*self as f64));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-float
+impl FromJSValConvertible for f32 {
+ type Config = ();
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ _option: (),
+ ) -> Result<ConversionResult<f32>, ()> {
+ let result = ToNumber(cx, val);
+ result.map(|f| f as f32).map(ConversionResult::Success)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-double
+impl ToJSValConvertible for f64 {
+ #[inline]
+ unsafe fn to_jsval(&self, _cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(RUST_JS_NumberValue(*self));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-double
+impl FromJSValConvertible for f64 {
+ type Config = ();
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ val: JS::HandleValue,
+ _option: (),
+ ) -> Result<ConversionResult<f64>, ()> {
+ ToNumber(cx, val).map(ConversionResult::Success)
+ }
+}
+
+/// Converts a `JSString`, encoded in "Latin1" (i.e. U+0000-U+00FF encoded as 0x00-0xFF) into a
+/// `String`.
+pub unsafe fn latin1_to_string(cx: *mut JSContext, s: *mut JSString) -> String {
+ assert!(JS_DeprecatedStringHasLatin1Chars(s));
+
+ let mut length = 0;
+ let chars = JS_GetLatin1StringCharsAndLength(cx, ptr::null(), s, &mut length);
+ assert!(!chars.is_null());
+
+ let chars = slice::from_raw_parts(chars, length as usize);
+ let mut s = String::with_capacity(length as usize);
+ s.extend(chars.iter().map(|&c| c as char));
+ s
+}
+
+// https://heycam.github.io/webidl/#es-USVString
+impl ToJSValConvertible for str {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ let mut string_utf16: Vec<u16> = Vec::with_capacity(self.len());
+ string_utf16.extend(self.encode_utf16());
+ let jsstr = JS_NewUCStringCopyN(
+ cx,
+ string_utf16.as_ptr(),
+ string_utf16.len() as libc::size_t,
+ );
+ if jsstr.is_null() {
+ panic!("JS_NewUCStringCopyN failed");
+ }
+ rval.set(StringValue(&*jsstr));
+ }
+}
+
+// https://heycam.github.io/webidl/#es-USVString
+impl ToJSValConvertible for String {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ (**self).to_jsval(cx, rval);
+ }
+}
+
+// https://heycam.github.io/webidl/#es-USVString
+impl FromJSValConvertible for String {
+ type Config = ();
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ value: JS::HandleValue,
+ _: (),
+ ) -> Result<ConversionResult<String>, ()> {
+ let jsstr = ToString(cx, value);
+ if jsstr.is_null() {
+ debug!("ToString failed");
+ return Err(());
+ }
+ if JS_DeprecatedStringHasLatin1Chars(jsstr) {
+ return Ok(latin1_to_string(cx, jsstr)).map(ConversionResult::Success);
+ }
+
+ let mut length = 0;
+ let chars = JS_GetTwoByteStringCharsAndLength(cx, ptr::null(), jsstr, &mut length);
+ assert!(!chars.is_null());
+ let char_vec = slice::from_raw_parts(chars, length as usize);
+ Ok(String::from_utf16_lossy(char_vec)).map(ConversionResult::Success)
+ }
+}
+
+impl<T: ToJSValConvertible> ToJSValConvertible for Option<T> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ match self {
+ &Some(ref value) => value.to_jsval(cx, rval),
+ &None => rval.set(NullValue()),
+ }
+ }
+}
+
+impl<T: ToJSValConvertible> ToJSValConvertible for Rc<T> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ (**self).to_jsval(cx, rval)
+ }
+}
+
+impl<T: FromJSValConvertible> FromJSValConvertible for Option<T> {
+ type Config = T::Config;
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ value: JS::HandleValue,
+ option: T::Config,
+ ) -> Result<ConversionResult<Option<T>>, ()> {
+ if value.get().is_null_or_undefined() {
+ Ok(ConversionResult::Success(None))
+ } else {
+ Ok(
+ match try!(FromJSValConvertible::from_jsval(cx, value, option)) {
+ ConversionResult::Success(v) => ConversionResult::Success(Some(v)),
+ ConversionResult::Failure(v) => ConversionResult::Failure(v),
+ },
+ )
+ }
+ }
+}
+
+// https://heycam.github.io/webidl/#es-sequence
+impl<T: ToJSValConvertible> ToJSValConvertible for Vec<T> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rooted!(in(cx) let js_array = JS::NewArrayObject1(cx, self.len() as libc::size_t));
+ assert!(!js_array.handle().is_null());
+
+ rooted!(in(cx) let mut val = UndefinedValue());
+ for (index, obj) in self.iter().enumerate() {
+ obj.to_jsval(cx, val.handle_mut());
+
+ assert!(JS_DefineElement(
+ cx,
+ js_array.handle(),
+ index as u32,
+ val.handle(),
+ JSPROP_ENUMERATE as _
+ ));
+ }
+
+ rval.set(ObjectValue(js_array.handle().get()));
+ }
+}
+
+/// Rooting guard for the iterator and nextMethod fields of ForOfIterator.
+/// Behaves like RootedGuard (roots on creation, unroots on drop),
+/// but borrows and allows access to the whole ForOfIterator, so
+/// that methods on ForOfIterator can still be used through it.
+struct ForOfIteratorGuard<'a> {
+ root: &'a mut JS::ForOfIterator,
+}
+
+impl<'a> ForOfIteratorGuard<'a> {
+ fn new(cx: *mut JSContext, root: &'a mut JS::ForOfIterator) -> Self {
+ unsafe {
+ root.iterator.register_with_root_lists(cx);
+ root.nextMethod.register_with_root_lists(cx);
+ }
+ ForOfIteratorGuard { root: root }
+ }
+}
+
+impl<'a> Drop for ForOfIteratorGuard<'a> {
+ fn drop(&mut self) {
+ unsafe {
+ self.root.nextMethod.remove_from_root_stack();
+ self.root.iterator.remove_from_root_stack();
+ }
+ }
+}
+
+impl<C: Clone, T: FromJSValConvertible<Config = C>> FromJSValConvertible for Vec<T> {
+ type Config = C;
+
+ unsafe fn from_jsval(
+ cx: *mut JSContext,
+ value: JS::HandleValue,
+ option: C,
+ ) -> Result<ConversionResult<Vec<T>>, ()> {
+ let mut iterator = JS::ForOfIterator {
+ cx_: cx,
+ iterator: JS::RootedObject::new_unrooted(),
+ nextMethod: JS::RootedValue::new_unrooted(),
+ index: ::std::u32::MAX, // NOT_ARRAY
+ };
+ let iterator = ForOfIteratorGuard::new(cx, &mut iterator);
+ let iterator = &mut *iterator.root;
+
+ if !iterator.init(
+ value,
+ JS::ForOfIterator_NonIterableBehavior::AllowNonIterable,
+ ) {
+ return Err(());
+ }
+
+ if iterator.iterator.ptr.is_null() {
+ return Ok(ConversionResult::Failure("Value is not iterable".into()));
+ }
+
+ let mut ret = vec![];
+
+ loop {
+ let mut done = false;
+ rooted!(in(cx) let mut val = UndefinedValue());
+ if !iterator.next(val.handle_mut(), &mut done) {
+ return Err(());
+ }
+
+ if done {
+ break;
+ }
+
+ ret.push(
+ match try!(T::from_jsval(cx, val.handle(), option.clone())) {
+ ConversionResult::Success(v) => v,
+ ConversionResult::Failure(e) => return Ok(ConversionResult::Failure(e)),
+ },
+ );
+ }
+
+ Ok(ret).map(ConversionResult::Success)
+ }
+}
+
+// https://heycam.github.io/webidl/#es-object
+impl ToJSValConvertible for *mut JSObject {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(ObjectOrNullValue(*self));
+ maybe_wrap_object_or_null_value(cx, rval);
+ }
+}
+
+// https://heycam.github.io/webidl/#es-object
+#[cfg(feature = "nonzero")]
+impl ToJSValConvertible for NonZero<*mut JSObject> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ use rust::maybe_wrap_object_value;
+ rval.set(ObjectValue(self.get()));
+ maybe_wrap_object_value(cx, rval);
+ }
+}
+
+// https://heycam.github.io/webidl/#es-object
+impl ToJSValConvertible for Heap<*mut JSObject> {
+ #[inline]
+ unsafe fn to_jsval(&self, cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ rval.set(ObjectOrNullValue(self.get()));
+ 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<ConversionResult<Self>, ()> {
+ 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/error.rs b/js/rust/src/error.rs
new file mode 100644
index 0000000000..34a5fa828a
--- /dev/null
+++ b/js/rust/src/error.rs
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Functions to throw JavaScript exceptions from Rust.
+
+#![deny(missing_docs)]
+
+use jsapi::root::*;
+use libc;
+use std::ffi::CString;
+use std::{mem, os, ptr};
+
+/// Format string used to throw javascript errors.
+static ERROR_FORMAT_STRING_STRING: [libc::c_char; 4] = [
+ '{' as libc::c_char,
+ '0' as libc::c_char,
+ '}' as libc::c_char,
+ 0 as libc::c_char,
+];
+
+/// Format string struct used to throw `TypeError`s.
+static mut TYPE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
+ name: b"RUSTMSG_TYPE_ERROR\0" as *const _ as *const libc::c_char,
+ format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
+ argCount: 1,
+ exnType: JSExnType::JSEXN_TYPEERR as i16,
+};
+
+/// Format string struct used to throw `RangeError`s.
+static mut RANGE_ERROR_FORMAT_STRING: JSErrorFormatString = JSErrorFormatString {
+ name: b"RUSTMSG_RANGE_ERROR\0" as *const _ as *const libc::c_char,
+ format: &ERROR_FORMAT_STRING_STRING as *const libc::c_char,
+ argCount: 1,
+ exnType: JSExnType::JSEXN_RANGEERR as i16,
+};
+
+/// Callback used to throw javascript errors.
+/// See throw_js_error for info about error_number.
+unsafe extern "C" fn get_error_message(
+ _user_ref: *mut os::raw::c_void,
+ error_number: libc::c_uint,
+) -> *const JSErrorFormatString {
+ let num: JSExnType = mem::transmute(error_number);
+ match num {
+ JSExnType::JSEXN_TYPEERR => &TYPE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
+ JSExnType::JSEXN_RANGEERR => &RANGE_ERROR_FORMAT_STRING as *const JSErrorFormatString,
+ _ => panic!(
+ "Bad js error number given to get_error_message: {}",
+ error_number
+ ),
+ }
+}
+
+/// Helper fn to throw a javascript error with the given message and number.
+/// Reuse the jsapi error codes to distinguish the error_number
+/// passed back to the get_error_message callback.
+/// c_uint is u32, so this cast is safe, as is casting to/from i32 from there.
+unsafe fn throw_js_error(cx: *mut JSContext, error: &str, error_number: u32) {
+ let error = CString::new(error).unwrap();
+ JS_ReportErrorNumberUTF8(
+ cx,
+ Some(get_error_message),
+ ptr::null_mut(),
+ error_number,
+ error.as_ptr(),
+ );
+}
+
+/// Throw a `TypeError` with the given message.
+pub unsafe fn throw_type_error(cx: *mut JSContext, error: &str) {
+ throw_js_error(cx, error, JSExnType::JSEXN_TYPEERR as u32);
+}
+
+/// Throw a `RangeError` with the given message.
+pub unsafe fn throw_range_error(cx: *mut JSContext, error: &str) {
+ throw_js_error(cx, error, JSExnType::JSEXN_RANGEERR as u32);
+}
diff --git a/js/rust/src/glue.rs b/js/rust/src/glue.rs
new file mode 100644
index 0000000000..9a0fd861f4
--- /dev/null
+++ b/js/rust/src/glue.rs
@@ -0,0 +1,474 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use heap::Heap;
+use jsapi::root::*;
+use std::os::raw::c_void;
+
+pub enum Action {}
+unsafe impl Sync for ProxyTraps {}
+
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ProxyTraps {
+ pub enter: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ action: Action,
+ bp: *mut bool,
+ ) -> bool,
+ >,
+ pub getOwnPropertyDescriptor: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ desc: JS::MutableHandle<JS::PropertyDescriptor>,
+ ) -> bool,
+ >,
+ pub defineProperty: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ desc: JS::Handle<JS::PropertyDescriptor>,
+ result: *mut JS::ObjectOpResult,
+ ) -> bool,
+ >,
+ pub ownPropertyKeys: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ props: JS::MutableHandleIdVector,
+ ) -> bool,
+ >,
+ pub delete_: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ result: *mut JS::ObjectOpResult,
+ ) -> bool,
+ >,
+ pub enumerate: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ objp: JS::MutableHandleObject,
+ ) -> bool,
+ >,
+ pub getPrototypeIfOrdinary: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ isOrdinary: *mut bool,
+ protop: JS::MutableHandleObject,
+ ) -> bool,
+ >,
+ pub preventExtensions: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ result: *mut JS::ObjectOpResult,
+ ) -> bool,
+ >,
+ pub isExtensible: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ succeeded: *mut bool,
+ ) -> bool,
+ >,
+ pub has: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ bp: *mut bool,
+ ) -> bool,
+ >,
+ pub get: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ receiver: JS::HandleValue,
+ id: JS::HandleId,
+ vp: JS::MutableHandleValue,
+ ) -> bool,
+ >,
+ pub set: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ v: JS::HandleValue,
+ receiver: JS::HandleValue,
+ result: *mut JS::ObjectOpResult,
+ ) -> bool,
+ >,
+ pub call: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ args: *const JS::CallArgs,
+ ) -> bool,
+ >,
+ pub construct: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ args: *const JS::CallArgs,
+ ) -> bool,
+ >,
+ pub hasOwn: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ bp: *mut bool,
+ ) -> bool,
+ >,
+ pub getOwnEnumerablePropertyKeys: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ props: JS::MutableHandleIdVector,
+ ) -> bool,
+ >,
+ pub nativeCall: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ test: JS::IsAcceptableThis,
+ _impl: JS::NativeImpl,
+ args: JS::CallArgs,
+ ) -> bool,
+ >,
+ pub hasInstance: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ v: JS::MutableHandleValue,
+ bp: *mut bool,
+ ) -> bool,
+ >,
+ pub objectClassIs: ::std::option::Option<
+ unsafe extern "C" fn(
+ obj: JS::HandleObject,
+ classValue: js::ESClass,
+ cx: *mut JSContext,
+ ) -> bool,
+ >,
+ pub className: ::std::option::Option<
+ unsafe extern "C" fn(cx: *mut JSContext, proxy: JS::HandleObject) -> *const i8,
+ >,
+ pub fun_toString: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ indent: u32,
+ ) -> *mut JSString,
+ >,
+ pub boxedValue_unbox: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ vp: JS::MutableHandleValue,
+ ) -> bool,
+ >,
+ pub defaultValue: ::std::option::Option<
+ unsafe extern "C" fn(
+ cx: *mut JSContext,
+ obj: JS::HandleObject,
+ hint: JSType,
+ vp: JS::MutableHandleValue,
+ ) -> bool,
+ >,
+ pub trace:
+ ::std::option::Option<unsafe extern "C" fn(trc: *mut JSTracer, proxy: *mut JSObject)>,
+ pub finalize:
+ ::std::option::Option<unsafe extern "C" fn(fop: *mut JSFreeOp, proxy: *mut JSObject)>,
+ pub objectMoved: ::std::option::Option<
+ unsafe extern "C" fn(proxy: *mut JSObject, old: *mut JSObject) -> usize,
+ >,
+ pub isCallable: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
+ pub isConstructor: ::std::option::Option<unsafe extern "C" fn(obj: *mut JSObject) -> bool>,
+}
+impl ::std::default::Default for ProxyTraps {
+ fn default() -> ProxyTraps {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct WrapperProxyHandler {
+ pub mTraps: ProxyTraps,
+}
+impl ::std::default::Default for WrapperProxyHandler {
+ fn default() -> WrapperProxyHandler {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+#[repr(C)]
+#[derive(Copy, Clone)]
+pub struct ForwardingProxyHandler {
+ pub mTraps: ProxyTraps,
+ pub mExtra: *const ::libc::c_void,
+}
+impl ::std::default::Default for ForwardingProxyHandler {
+ fn default() -> ForwardingProxyHandler {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+
+extern "C" {
+ pub fn InvokeGetOwnPropertyDescriptor(
+ handler: *const ::libc::c_void,
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ desc: JS::MutableHandle<JS::PropertyDescriptor>,
+ ) -> bool;
+ pub fn InvokeHasOwn(
+ handler: *const ::libc::c_void,
+ cx: *mut JSContext,
+ proxy: JS::HandleObject,
+ id: JS::HandleId,
+ bp: *mut bool,
+ ) -> bool;
+ pub fn RUST_JS_NumberValue(d: f64) -> JS::Value;
+ pub fn RUST_FUNCTION_VALUE_TO_JITINFO(v: JS::Value) -> *const JSJitInfo;
+ pub fn CreateCallArgsFromVp(argc: u32, v: *mut JS::Value) -> JS::CallArgs;
+ pub fn CallJitGetterOp(
+ info: *const JSJitInfo,
+ cx: *mut JSContext,
+ thisObj: JS::HandleObject,
+ specializedThis: *mut ::libc::c_void,
+ argc: u32,
+ vp: *mut JS::Value,
+ ) -> bool;
+ pub fn CallJitSetterOp(
+ info: *const JSJitInfo,
+ cx: *mut JSContext,
+ thisObj: JS::HandleObject,
+ specializedThis: *mut ::libc::c_void,
+ argc: u32,
+ vp: *mut JS::Value,
+ ) -> bool;
+ pub fn CallJitMethodOp(
+ info: *const JSJitInfo,
+ cx: *mut JSContext,
+ thisObj: JS::HandleObject,
+ specializedThis: *mut ::libc::c_void,
+ argc: u32,
+ vp: *mut JS::Value,
+ ) -> bool;
+ pub fn CreateProxyHandler(
+ aTraps: *const ProxyTraps,
+ aExtra: *const ::libc::c_void,
+ ) -> *const ::libc::c_void;
+ pub fn CreateWrapperProxyHandler(aTraps: *const ProxyTraps) -> *const ::libc::c_void;
+ pub fn CreateRustJSPrincipal(
+ origin: *const ::libc::c_void,
+ destroy: Option<unsafe extern "C" fn(principal: *mut JSPrincipals)>,
+ write: Option<
+ unsafe extern "C" fn(cx: *mut JSContext, writer: *mut JSStructuredCloneWriter) -> bool,
+ >,
+ ) -> *mut JSPrincipals;
+ pub fn GetPrincipalOrigin(principal: *const JSPrincipals) -> *const ::libc::c_void;
+ pub fn GetCrossCompartmentWrapper() -> *const ::libc::c_void;
+ pub fn GetSecurityWrapper() -> *const ::libc::c_void;
+ pub fn NewCompileOptions(
+ aCx: *mut JSContext,
+ aFile: *const ::libc::c_char,
+ aLine: u32,
+ ) -> *mut JS::ReadOnlyCompileOptions;
+ pub fn DeleteCompileOptions(aOpts: *mut JS::ReadOnlyCompileOptions);
+ pub fn NewProxyObject(
+ aCx: *mut JSContext,
+ aHandler: *const ::libc::c_void,
+ aPriv: JS::HandleValue,
+ proto: *mut JSObject,
+ parent: *mut JSObject,
+ call: *mut JSObject,
+ construct: *mut JSObject,
+ ) -> *mut JSObject;
+ pub fn WrapperNew(
+ aCx: *mut JSContext,
+ aObj: JS::HandleObject,
+ aHandler: *const ::libc::c_void,
+ aClass: *const JSClass,
+ ) -> *mut JSObject;
+ pub fn WrapperNewSingleton(
+ aCx: *mut JSContext,
+ aObj: JS::HandleObject,
+ aHandler: *const ::libc::c_void,
+ aClass: *const JSClass,
+ ) -> *mut JSObject;
+ pub fn NewWindowProxy(
+ aCx: *mut JSContext,
+ aObj: JS::HandleObject,
+ aHandler: *const ::libc::c_void,
+ ) -> *mut JSObject;
+ pub fn GetWindowProxyClass() -> *const JSClass;
+ pub fn GetProxyPrivate(obj: *mut JSObject) -> JS::Value;
+ pub fn SetProxyPrivate(obj: *mut JSObject, private: *const JS::Value);
+ pub fn GetProxyReservedSlot(obj: *mut JSObject, slot: u32) -> JS::Value;
+ pub fn SetProxyReservedSlot(obj: *mut JSObject, slot: u32, val: *const JS::Value);
+ pub fn RUST_JSID_IS_INT(id: JS::HandleId) -> bool;
+ pub fn RUST_JSID_TO_INT(id: JS::HandleId) -> i32;
+ pub fn int_to_jsid(i: i32) -> jsid;
+ pub fn RUST_JSID_IS_STRING(id: JS::HandleId) -> bool;
+ pub fn RUST_JSID_TO_STRING(id: JS::HandleId) -> *mut JSString;
+ pub fn RUST_SYMBOL_TO_JSID(sym: *mut JS::Symbol) -> jsid;
+ pub fn RUST_SET_JITINFO(func: *mut JSFunction, info: *const JSJitInfo);
+ pub fn RUST_INTERNED_STRING_TO_JSID(cx: *mut JSContext, str: *mut JSString) -> jsid;
+ pub fn RUST_js_GetErrorMessage(
+ userRef: *mut ::libc::c_void,
+ errorNumber: u32,
+ ) -> *const JSErrorFormatString;
+ pub fn IsProxyHandlerFamily(obj: *mut JSObject) -> u8;
+ pub fn GetProxyHandlerExtra(obj: *mut JSObject) -> *const ::libc::c_void;
+ pub fn GetProxyHandler(obj: *mut JSObject) -> *const ::libc::c_void;
+ pub fn ReportError(aCx: *mut JSContext, aError: *const i8);
+ pub fn IsWrapper(obj: *mut JSObject) -> bool;
+ pub fn UnwrapObjectStatic(obj: *mut JSObject) -> *mut JSObject;
+ pub fn UncheckedUnwrapObject(obj: *mut JSObject, stopAtOuter: u8) -> *mut JSObject;
+ pub fn CreateRootedIdVector(cx: *mut JSContext) -> *mut JS::PersistentRootedIdVector;
+ pub fn AppendToRootedIdVector(v: *mut JS::PersistentRootedIdVector, id: jsid) -> bool;
+ pub fn SliceRootedIdVector(
+ v: *const JS::PersistentRootedIdVector,
+ length: *mut usize,
+ ) -> *const jsid;
+ pub fn DestroyRootedIdVector(v: *mut JS::PersistentRootedIdVector);
+ pub fn GetMutableHandleIdVector(
+ v: *mut JS::PersistentRootedIdVector,
+ ) -> JS::MutableHandleIdVector;
+ pub fn CreateRootedObjectVector(aCx: *mut JSContext) -> *mut JS::PersistentRootedObjectVector;
+ pub fn AppendToRootedObjectVector(
+ v: *mut JS::PersistentRootedObjectVector,
+ obj: *mut JSObject,
+ ) -> bool;
+ pub fn DeleteRootedObjectVector(v: *mut JS::PersistentRootedObjectVector);
+ pub fn CollectServoSizes(rt: *mut JSRuntime, sizes: *mut JS::ServoSizes) -> bool;
+ pub fn CallIdTracer(trc: *mut JSTracer, idp: *mut Heap<jsid>, name: *const ::libc::c_char);
+ pub fn CallValueTracer(
+ trc: *mut JSTracer,
+ valuep: *mut Heap<JS::Value>,
+ name: *const ::libc::c_char,
+ );
+ pub fn CallObjectTracer(
+ trc: *mut JSTracer,
+ objp: *mut Heap<*mut JSObject>,
+ name: *const ::libc::c_char,
+ );
+ pub fn CallStringTracer(
+ trc: *mut JSTracer,
+ strp: *mut Heap<*mut JSString>,
+ name: *const ::libc::c_char,
+ );
+ #[cfg(feature = "bigint")]
+ pub fn CallBigIntTracer(
+ trc: *mut JSTracer,
+ bip: *mut Heap<*mut JS::BigInt>,
+ name: *const ::libc::c_char,
+ );
+ pub fn CallScriptTracer(
+ trc: *mut JSTracer,
+ scriptp: *mut Heap<*mut JSScript>,
+ name: *const ::libc::c_char,
+ );
+ pub fn CallFunctionTracer(
+ trc: *mut JSTracer,
+ funp: *mut Heap<*mut JSFunction>,
+ name: *const ::libc::c_char,
+ );
+ pub fn CallUnbarrieredObjectTracer(
+ trc: *mut JSTracer,
+ objp: *mut *mut JSObject,
+ name: *const ::libc::c_char,
+ );
+ pub fn GetProxyHandlerFamily() -> *const c_void;
+
+ pub fn GetInt8ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut i8,
+ );
+ pub fn GetUint8ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut u8,
+ );
+ pub fn GetUint8ClampedArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut u8,
+ );
+ pub fn GetInt16ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut i16,
+ );
+ pub fn GetUint16ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut u16,
+ );
+ pub fn GetInt32ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut i32,
+ );
+ pub fn GetUint32ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut u32,
+ );
+ pub fn GetFloat32ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut f32,
+ );
+ pub fn GetFloat64ArrayLengthAndData(
+ obj: *mut JSObject,
+ length: *mut u32,
+ isSharedMemory: *mut bool,
+ data: *mut *mut f64,
+ );
+
+ pub fn NewJSAutoStructuredCloneBuffer(
+ scope: JS::StructuredCloneScope,
+ callbacks: *const JSStructuredCloneCallbacks,
+ ) -> *mut JSAutoStructuredCloneBuffer;
+ pub fn DeleteJSAutoStructuredCloneBuffer(buf: *mut JSAutoStructuredCloneBuffer);
+ pub fn GetLengthOfJSStructuredCloneData(data: *mut JSStructuredCloneData) -> usize;
+ pub fn CopyJSStructuredCloneData(src: *mut JSStructuredCloneData, dest: *mut u8);
+ pub fn WriteBytesToJSStructuredCloneData(
+ src: *const u8,
+ len: usize,
+ dest: *mut JSStructuredCloneData,
+ ) -> bool;
+
+ pub fn JSEncodeStringToUTF8(
+ cx: *mut JSContext,
+ string: JS::HandleString,
+ ) -> *mut ::libc::c_char;
+
+ pub fn IsDebugBuild() -> bool;
+}
+
+#[test]
+fn jsglue_cpp_configured_correctly() {
+ assert_eq!(cfg!(feature = "debugmozjs"), unsafe { IsDebugBuild() });
+}
diff --git a/js/rust/src/heap.rs b/js/rust/src/heap.rs
new file mode 100644
index 0000000000..7c39feaf27
--- /dev/null
+++ b/js/rust/src/heap.rs
@@ -0,0 +1,179 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use glue;
+use jsapi::root::*;
+use rust::GCMethods;
+use std::cell::UnsafeCell;
+use std::ptr;
+
+/// Types that can be traced.
+///
+/// This trait is unsafe; if it is implemented incorrectly, the GC may end up
+/// collecting objects that are still reachable.
+pub unsafe trait Trace {
+ unsafe fn trace(&self, trc: *mut JSTracer);
+}
+
+/**
+ * The Heap<T> class is a heap-stored reference to a JS GC thing. All members of
+ * heap classes that refer to GC things should use Heap<T> (or possibly
+ * TenuredHeap<T>, described below).
+ *
+ * Heap<T> is an abstraction that hides some of the complexity required to
+ * maintain GC invariants for the contained reference. It uses operator
+ * overloading to provide a normal pointer interface, but notifies the GC every
+ * time the value it contains is updated. This is necessary for generational GC,
+ * which keeps track of all pointers into the nursery.
+ *
+ * Heap<T> instances must be traced when their containing object is traced to
+ * keep the pointed-to GC thing alive.
+ *
+ * Heap<T> objects should only be used on the heap. GC references stored on the
+ * C/C++ stack must use Rooted/Handle/MutableHandle instead.
+ *
+ * Type T must be a public GC pointer type.
+ *
+ * Note that the rust version of Heap<T> implements different barriers to the
+ * C++ version, which also provides features to help integration with
+ * cycle-collected C++ objects. That version has a read barrier which performs
+ * gray unmarking and also marks the contents during an incremental GC. This
+ * version has a pre-write barrier instead, and this enforces the
+ * snapshot-at-the-beginning invariant which is necessary for incremental GC in
+ * the absence of the read barrier.
+ */
+#[repr(C)]
+#[derive(Debug)]
+pub struct Heap<T: GCMethods + Copy> {
+ ptr: UnsafeCell<T>,
+}
+
+impl<T: GCMethods + Copy> Heap<T> {
+ pub fn new(v: T) -> Heap<T>
+ where
+ Heap<T>: Default,
+ {
+ let ptr = Heap::default();
+ ptr.set(v);
+ ptr
+ }
+
+ pub fn set(&self, v: T) {
+ unsafe {
+ let ptr = self.ptr.get();
+ let prev = *ptr;
+ *ptr = v;
+ T::write_barriers(ptr, prev, v);
+ }
+ }
+
+ pub fn get(&self) -> T {
+ unsafe { *self.ptr.get() }
+ }
+
+ pub unsafe fn get_unsafe(&self) -> *mut T {
+ self.ptr.get()
+ }
+
+ pub fn handle(&self) -> JS::Handle<T> {
+ unsafe { JS::Handle::from_marked_location(self.ptr.get() as *const _) }
+ }
+
+ pub fn handle_mut(&self) -> JS::MutableHandle<T> {
+ unsafe { JS::MutableHandle::from_marked_location(self.ptr.get()) }
+ }
+}
+
+impl<T: GCMethods + Copy> Clone for Heap<T>
+where
+ Heap<T>: Default,
+{
+ fn clone(&self) -> Self {
+ Heap::new(self.get())
+ }
+}
+
+impl<T: GCMethods + Copy + PartialEq> PartialEq for Heap<T> {
+ fn eq(&self, other: &Self) -> bool {
+ self.get() == other.get()
+ }
+}
+
+impl<T> Default for Heap<*mut T>
+where
+ *mut T: GCMethods + Copy,
+{
+ fn default() -> Heap<*mut T> {
+ Heap {
+ ptr: UnsafeCell::new(ptr::null_mut()),
+ }
+ }
+}
+
+impl Default for Heap<JS::Value> {
+ fn default() -> Heap<JS::Value> {
+ Heap {
+ ptr: UnsafeCell::new(JS::Value::default()),
+ }
+ }
+}
+
+impl<T: GCMethods + Copy> Drop for Heap<T> {
+ fn drop(&mut self) {
+ unsafe {
+ let prev = self.ptr.get();
+ T::write_barriers(prev, *prev, T::initial());
+ }
+ }
+}
+
+// Creates a C string literal `$str`.
+macro_rules! c_str {
+ ($str:expr) => {
+ concat!($str, "\0").as_ptr() as *const ::std::os::raw::c_char
+ };
+}
+
+unsafe impl Trace for Heap<*mut JSFunction> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallFunctionTracer(trc, self as *const _ as *mut Self, c_str!("function"));
+ }
+}
+
+unsafe impl Trace for Heap<*mut JSObject> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallObjectTracer(trc, self as *const _ as *mut Self, c_str!("object"));
+ }
+}
+
+unsafe impl Trace for Heap<*mut JSScript> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallScriptTracer(trc, self as *const _ as *mut Self, c_str!("script"));
+ }
+}
+
+unsafe impl Trace for Heap<*mut JSString> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallStringTracer(trc, self as *const _ as *mut Self, c_str!("string"));
+ }
+}
+
+#[cfg(feature = "bigint")]
+unsafe impl Trace for Heap<*mut JS::BigInt> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallBigIntTracer(trc, self as *const _ as *mut Self, c_str!("bigint"));
+ }
+}
+
+unsafe impl Trace for Heap<JS::Value> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallValueTracer(trc, self as *const _ as *mut Self, c_str!("value"));
+ }
+}
+
+unsafe impl Trace for Heap<jsid> {
+ unsafe fn trace(&self, trc: *mut JSTracer) {
+ glue::CallIdTracer(trc, self as *const _ as *mut Self, c_str!("id"));
+ }
+}
diff --git a/js/rust/src/jsapi.rs b/js/rust/src/jsapi.rs
new file mode 100644
index 0000000000..a0a57d546e
--- /dev/null
+++ b/js/rust/src/jsapi.rs
@@ -0,0 +1,9 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#[cfg(feature = "debugmozjs")]
+include!(concat!(env!("OUT_DIR"), "/jsapi_debug.rs"));
+
+#[cfg(not(feature = "debugmozjs"))]
+include!(concat!(env!("OUT_DIR"), "/jsapi.rs"));
diff --git a/js/rust/src/jsglue.cpp b/js/rust/src/jsglue.cpp
new file mode 100644
index 0000000000..69ed3bbca1
--- /dev/null
+++ b/js/rust/src/jsglue.cpp
@@ -0,0 +1,746 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#define __STDC_LIMIT_MACROS
+#include <stdint.h>
+
+#include "js-config.h"
+
+#ifdef JS_DEBUG
+// A hack for MFBT. Guard objects need this to work.
+# define DEBUG 1
+#endif
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "js/Proxy.h"
+#include "js/CharacterEncoding.h"
+#include "js/Class.h"
+#include "js/experimental/JitInfo.h"
+#include "js/experimental/TypedData.h"
+#include "js/friend/ErrorMessages.h" // js::GetErrorMessage
+#include "js/MemoryMetrics.h"
+#include "js/Principals.h"
+#include "js/StructuredClone.h"
+#include "js/Wrapper.h"
+#include "assert.h"
+
+struct ProxyTraps {
+ bool (*enter)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ js::BaseProxyHandler::Action action, bool* bp);
+
+ bool (*getOwnPropertyDescriptor)(
+ JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc);
+ bool (*defineProperty)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result);
+ bool (*ownPropertyKeys)(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props);
+ bool (*delete_)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::ObjectOpResult& result);
+
+ bool (*enumerate)(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props);
+
+ bool (*getPrototypeIfOrdinary)(JSContext* cx, JS::HandleObject proxy,
+ bool* isOrdinary,
+ JS::MutableHandleObject protop);
+ // getPrototype
+ // setPrototype
+ // setImmutablePrototype
+
+ bool (*preventExtensions)(JSContext* cx, JS::HandleObject proxy,
+ JS::ObjectOpResult& result);
+
+ bool (*isExtensible)(JSContext* cx, JS::HandleObject proxy, bool* succeeded);
+
+ bool (*has)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, bool* bp);
+ bool (*get)(JSContext* cx, JS::HandleObject proxy, JS::HandleValue receiver,
+ JS::HandleId id, JS::MutableHandleValue vp);
+ bool (*set)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::HandleValue v, JS::HandleValue receiver,
+ JS::ObjectOpResult& result);
+
+ bool (*call)(JSContext* cx, JS::HandleObject proxy, const JS::CallArgs& args);
+ bool (*construct)(JSContext* cx, JS::HandleObject proxy,
+ const JS::CallArgs& args);
+
+ bool (*hasOwn)(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ bool* bp);
+ bool (*getOwnEnumerablePropertyKeys)(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props);
+ bool (*nativeCall)(JSContext* cx, JS::IsAcceptableThis test,
+ JS::NativeImpl impl, JS::CallArgs args);
+ bool (*hasInstance)(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue v, bool* bp);
+ bool (*objectClassIs)(JS::HandleObject obj, js::ESClass classValue,
+ JSContext* cx);
+ const char* (*className)(JSContext* cx, JS::HandleObject proxy);
+ JSString* (*fun_toString)(JSContext* cx, JS::HandleObject proxy,
+ bool isToString);
+ // bool (*regexp_toShared)(JSContext *cx, JS::HandleObject proxy, RegExpGuard
+ // *g);
+ bool (*boxedValue_unbox)(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleValue vp);
+ bool (*defaultValue)(JSContext* cx, JS::HandleObject obj, JSType hint,
+ JS::MutableHandleValue vp);
+ void (*trace)(JSTracer* trc, JSObject* proxy);
+ void (*finalize)(JSFreeOp* fop, JSObject* proxy);
+ size_t (*objectMoved)(JSObject* proxy, JSObject* old);
+
+ bool (*isCallable)(JSObject* obj);
+ bool (*isConstructor)(JSObject* obj);
+
+ // getElements
+
+ // isScripted
+};
+
+static int HandlerFamily;
+
+#define DEFER_TO_TRAP_OR_BASE_CLASS(_base) \
+ \
+ /* Standard internal methods. */ \
+ virtual bool enumerate(JSContext* cx, JS::HandleObject proxy, \
+ JS::MutableHandleIdVector props) const override { \
+ return mTraps.enumerate ? mTraps.enumerate(cx, proxy, props) \
+ : _base::enumerate(cx, proxy, props); \
+ } \
+ \
+ virtual bool has(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, \
+ bool* bp) const override { \
+ return mTraps.has ? mTraps.has(cx, proxy, id, bp) \
+ : _base::has(cx, proxy, id, bp); \
+ } \
+ \
+ virtual bool get(JSContext* cx, JS::HandleObject proxy, \
+ JS::HandleValue receiver, JS::HandleId id, \
+ JS::MutableHandleValue vp) const override { \
+ return mTraps.get ? mTraps.get(cx, proxy, receiver, id, vp) \
+ : _base::get(cx, proxy, receiver, id, vp); \
+ } \
+ \
+ virtual bool set(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, \
+ JS::HandleValue v, JS::HandleValue receiver, \
+ JS::ObjectOpResult& result) const override { \
+ return mTraps.set ? mTraps.set(cx, proxy, id, v, receiver, result) \
+ : _base::set(cx, proxy, id, v, receiver, result); \
+ } \
+ \
+ virtual bool call(JSContext* cx, JS::HandleObject proxy, \
+ const JS::CallArgs& args) const override { \
+ return mTraps.call ? mTraps.call(cx, proxy, args) \
+ : _base::call(cx, proxy, args); \
+ } \
+ \
+ virtual bool construct(JSContext* cx, JS::HandleObject proxy, \
+ const JS::CallArgs& args) const override { \
+ return mTraps.construct ? mTraps.construct(cx, proxy, args) \
+ : _base::construct(cx, proxy, args); \
+ } \
+ \
+ /* Spidermonkey extensions. */ \
+ virtual bool hasOwn(JSContext* cx, JS::HandleObject proxy, JS::HandleId id, \
+ bool* bp) const override { \
+ return mTraps.hasOwn ? mTraps.hasOwn(cx, proxy, id, bp) \
+ : _base::hasOwn(cx, proxy, id, bp); \
+ } \
+ \
+ virtual bool getOwnEnumerablePropertyKeys( \
+ JSContext* cx, JS::HandleObject proxy, JS::MutableHandleIdVector props) \
+ const override { \
+ return mTraps.getOwnEnumerablePropertyKeys \
+ ? mTraps.getOwnEnumerablePropertyKeys(cx, proxy, props) \
+ : _base::getOwnEnumerablePropertyKeys(cx, proxy, props); \
+ } \
+ \
+ virtual bool nativeCall(JSContext* cx, JS::IsAcceptableThis test, \
+ JS::NativeImpl impl, const JS::CallArgs& args) \
+ const override { \
+ return mTraps.nativeCall ? mTraps.nativeCall(cx, test, impl, args) \
+ : _base::nativeCall(cx, test, impl, args); \
+ } \
+ \
+ virtual bool hasInstance(JSContext* cx, JS::HandleObject proxy, \
+ JS::MutableHandleValue v, bool* bp) \
+ const override { \
+ return mTraps.hasInstance ? mTraps.hasInstance(cx, proxy, v, bp) \
+ : _base::hasInstance(cx, proxy, v, bp); \
+ } \
+ \
+ virtual const char* className(JSContext* cx, JS::HandleObject proxy) \
+ const override { \
+ return mTraps.className ? mTraps.className(cx, proxy) \
+ : _base::className(cx, proxy); \
+ } \
+ \
+ virtual JSString* fun_toString(JSContext* cx, JS::HandleObject proxy, \
+ bool isToString) const override { \
+ return mTraps.fun_toString ? mTraps.fun_toString(cx, proxy, isToString) \
+ : _base::fun_toString(cx, proxy, isToString); \
+ } \
+ \
+ virtual bool boxedValue_unbox(JSContext* cx, JS::HandleObject proxy, \
+ JS::MutableHandleValue vp) const override { \
+ return mTraps.boxedValue_unbox ? mTraps.boxedValue_unbox(cx, proxy, vp) \
+ : _base::boxedValue_unbox(cx, proxy, vp); \
+ } \
+ \
+ virtual void trace(JSTracer* trc, JSObject* proxy) const override { \
+ mTraps.trace ? mTraps.trace(trc, proxy) : _base::trace(trc, proxy); \
+ } \
+ \
+ virtual void finalize(JSFreeOp* fop, JSObject* proxy) const override { \
+ mTraps.finalize ? mTraps.finalize(fop, proxy) \
+ : _base::finalize(fop, proxy); \
+ } \
+ \
+ virtual size_t objectMoved(JSObject* proxy, JSObject* old) const override { \
+ return mTraps.objectMoved ? mTraps.objectMoved(proxy, old) \
+ : _base::objectMoved(proxy, old); \
+ } \
+ \
+ virtual bool isCallable(JSObject* obj) const override { \
+ return mTraps.isCallable ? mTraps.isCallable(obj) \
+ : _base::isCallable(obj); \
+ } \
+ \
+ virtual bool isConstructor(JSObject* obj) const override { \
+ return mTraps.isConstructor ? mTraps.isConstructor(obj) \
+ : _base::isConstructor(obj); \
+ }
+
+class WrapperProxyHandler : public js::Wrapper {
+ ProxyTraps mTraps;
+
+ public:
+ WrapperProxyHandler(const ProxyTraps& aTraps)
+ : js::Wrapper(0), mTraps(aTraps) {}
+
+ virtual bool finalizeInBackground(const JS::Value& priv) const override {
+ return false;
+ }
+
+ DEFER_TO_TRAP_OR_BASE_CLASS(js::Wrapper)
+
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override {
+ return mTraps.getOwnPropertyDescriptor
+ ? mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc)
+ : js::Wrapper::getOwnPropertyDescriptor(cx, proxy, id, desc);
+ }
+
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override {
+ return mTraps.defineProperty
+ ? mTraps.defineProperty(cx, proxy, id, desc, result)
+ : js::Wrapper::defineProperty(cx, proxy, id, desc, result);
+ }
+
+ virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override {
+ return mTraps.ownPropertyKeys
+ ? mTraps.ownPropertyKeys(cx, proxy, props)
+ : js::Wrapper::ownPropertyKeys(cx, proxy, props);
+ }
+
+ virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::ObjectOpResult& result) const override {
+ return mTraps.delete_ ? mTraps.delete_(cx, proxy, id, result)
+ : js::Wrapper::delete_(cx, proxy, id, result);
+ }
+
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy,
+ JS::ObjectOpResult& result) const override {
+ return mTraps.preventExtensions
+ ? mTraps.preventExtensions(cx, proxy, result)
+ : js::Wrapper::preventExtensions(cx, proxy, result);
+ }
+
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy,
+ bool* succeeded) const override {
+ return mTraps.isExtensible
+ ? mTraps.isExtensible(cx, proxy, succeeded)
+ : js::Wrapper::isExtensible(cx, proxy, succeeded);
+ }
+};
+
+class RustJSPrincipal : public JSPrincipals {
+ const void* origin; // box with origin in it
+ void (*destroyCallback)(JSPrincipals* principal);
+ bool (*writeCallback)(JSContext* cx, JSStructuredCloneWriter* writer);
+
+ public:
+ RustJSPrincipal(const void* origin, void (*destroy)(JSPrincipals* principal),
+ bool (*write)(JSContext* cx, JSStructuredCloneWriter* writer))
+ : JSPrincipals() {
+ this->origin = origin;
+ this->destroyCallback = destroy;
+ this->writeCallback = write;
+ }
+
+ virtual const void* getOrigin() { return origin; }
+
+ virtual void destroy() {
+ if (this->destroyCallback) this->destroyCallback(this);
+ }
+
+ bool isSystemOrAddonPrincipal() { return false; }
+
+ bool write(JSContext* cx, JSStructuredCloneWriter* writer) {
+ return this->writeCallback ? this->writeCallback(cx, writer) : false;
+ }
+};
+
+class ForwardingProxyHandler : public js::BaseProxyHandler {
+ ProxyTraps mTraps;
+ const void* mExtra;
+
+ public:
+ ForwardingProxyHandler(const ProxyTraps& aTraps, const void* aExtra)
+ : js::BaseProxyHandler(&HandlerFamily), mTraps(aTraps), mExtra(aExtra) {}
+
+ const void* getExtra() const { return mExtra; }
+
+ virtual bool finalizeInBackground(const JS::Value& priv) const override {
+ return false;
+ }
+
+ DEFER_TO_TRAP_OR_BASE_CLASS(BaseProxyHandler)
+
+ virtual bool getOwnPropertyDescriptor(
+ JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) const override {
+ return mTraps.getOwnPropertyDescriptor(cx, proxy, id, desc);
+ }
+
+ virtual bool defineProperty(JSContext* cx, JS::HandleObject proxy,
+ JS::HandleId id,
+ JS::Handle<JS::PropertyDescriptor> desc,
+ JS::ObjectOpResult& result) const override {
+ return mTraps.defineProperty(cx, proxy, id, desc, result);
+ }
+
+ virtual bool ownPropertyKeys(JSContext* cx, JS::HandleObject proxy,
+ JS::MutableHandleIdVector props) const override {
+ return mTraps.ownPropertyKeys(cx, proxy, props);
+ }
+
+ virtual bool delete_(JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::ObjectOpResult& result) const override {
+ return mTraps.delete_(cx, proxy, id, result);
+ }
+
+ virtual bool getPrototypeIfOrdinary(
+ JSContext* cx, JS::HandleObject proxy, bool* isOrdinary,
+ JS::MutableHandleObject protop) const override {
+ return mTraps.getPrototypeIfOrdinary(cx, proxy, isOrdinary, protop);
+ }
+
+ virtual bool preventExtensions(JSContext* cx, JS::HandleObject proxy,
+ JS::ObjectOpResult& result) const override {
+ return mTraps.preventExtensions(cx, proxy, result);
+ }
+
+ virtual bool isExtensible(JSContext* cx, JS::HandleObject proxy,
+ bool* succeeded) const override {
+ return mTraps.isExtensible(cx, proxy, succeeded);
+ }
+};
+
+extern "C" {
+
+JSPrincipals* CreateRustJSPrincipal(
+ const void* origin, void (*destroy)(JSPrincipals* principal),
+ bool (*write)(JSContext* cx, JSStructuredCloneWriter* writer)) {
+ return new RustJSPrincipal(origin, destroy, write);
+}
+
+const void* GetPrincipalOrigin(JSPrincipals* principal) {
+ return static_cast<RustJSPrincipal*>(principal)->getOrigin();
+}
+
+bool InvokeGetOwnPropertyDescriptor(
+ const void* handler, JSContext* cx, JS::HandleObject proxy, JS::HandleId id,
+ JS::MutableHandle<JS::PropertyDescriptor> desc) {
+ return static_cast<const ForwardingProxyHandler*>(handler)
+ ->getOwnPropertyDescriptor(cx, proxy, id, desc);
+}
+
+bool InvokeHasOwn(const void* handler, JSContext* cx, JS::HandleObject proxy,
+ JS::HandleId id, bool* bp) {
+ return static_cast<const js::BaseProxyHandler*>(handler)->hasOwn(cx, proxy,
+ id, bp);
+}
+
+JS::Value RUST_JS_NumberValue(double d) { return JS_NumberValue(d); }
+
+const JSJitInfo* RUST_FUNCTION_VALUE_TO_JITINFO(JS::Value v) {
+ return FUNCTION_VALUE_TO_JITINFO(v);
+}
+
+JS::CallArgs CreateCallArgsFromVp(unsigned argc, JS::Value* vp) {
+ return JS::CallArgsFromVp(argc, vp);
+}
+
+bool CallJitGetterOp(const JSJitInfo* info, JSContext* cx,
+ JS::HandleObject thisObj, void* specializedThis,
+ unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ return info->getter(cx, thisObj, specializedThis, JSJitGetterCallArgs(args));
+}
+
+bool CallJitSetterOp(const JSJitInfo* info, JSContext* cx,
+ JS::HandleObject thisObj, void* specializedThis,
+ unsigned argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ return info->setter(cx, thisObj, specializedThis, JSJitSetterCallArgs(args));
+}
+
+bool CallJitMethodOp(const JSJitInfo* info, JSContext* cx,
+ JS::HandleObject thisObj, void* specializedThis,
+ uint32_t argc, JS::Value* vp) {
+ JS::CallArgs args = JS::CallArgsFromVp(argc, vp);
+ return info->method(cx, thisObj, specializedThis, JSJitMethodCallArgs(args));
+}
+
+const void* CreateProxyHandler(const ProxyTraps* aTraps, const void* aExtra) {
+ return new ForwardingProxyHandler(*aTraps, aExtra);
+}
+
+const void* CreateWrapperProxyHandler(const ProxyTraps* aTraps) {
+ return new WrapperProxyHandler(*aTraps);
+}
+
+const void* GetCrossCompartmentWrapper() {
+ return &js::CrossCompartmentWrapper::singleton;
+}
+
+const void* GetSecurityWrapper() {
+ return &js::CrossCompartmentSecurityWrapper::singleton;
+}
+
+void DeleteCompileOptions(JS::ReadOnlyCompileOptions* aOpts) {
+ delete static_cast<JS::OwningCompileOptions*>(aOpts);
+}
+
+JS::ReadOnlyCompileOptions* NewCompileOptions(JSContext* aCx, const char* aFile,
+ unsigned aLine) {
+ JS::CompileOptions opts(aCx);
+ opts.setFileAndLine(aFile, aLine);
+
+ JS::OwningCompileOptions* owned = new JS::OwningCompileOptions(aCx);
+ if (!owned) {
+ return nullptr;
+ }
+
+ if (!owned->copy(aCx, opts)) {
+ DeleteCompileOptions(owned);
+ return nullptr;
+ }
+
+ return owned;
+}
+
+JSObject* NewProxyObject(JSContext* aCx, const void* aHandler,
+ JS::HandleValue aPriv, JSObject* proto,
+ JSObject* parent, JSObject* call,
+ JSObject* construct) {
+ js::ProxyOptions options;
+ return js::NewProxyObject(aCx, (js::BaseProxyHandler*)aHandler, aPriv, proto,
+ options);
+}
+
+JSObject* WrapperNew(JSContext* aCx, JS::HandleObject aObj,
+ const void* aHandler, const JSClass* aClass) {
+ js::WrapperOptions options;
+ if (aClass) {
+ options.setClass(aClass);
+ }
+ return js::Wrapper::New(aCx, aObj, (const js::Wrapper*)aHandler, options);
+}
+
+JSObject* WrapperNewSingleton(JSContext* aCx, JS::HandleObject aObj,
+ const void* aHandler, const JSClass* aClass) {
+ js::WrapperOptions options;
+ if (aClass) {
+ options.setClass(aClass);
+ }
+ return js::Wrapper::NewSingleton(aCx, aObj, (const js::Wrapper*)aHandler,
+ options);
+}
+
+const JSClass WindowProxyClass = PROXY_CLASS_DEF(
+ "Proxy", JSCLASS_HAS_RESERVED_SLOTS(1)); /* additional class flags */
+
+const JSClass* GetWindowProxyClass() { return &WindowProxyClass; }
+
+JS::Value GetProxyReservedSlot(JSObject* obj, uint32_t slot) {
+ return js::GetProxyReservedSlot(obj, slot);
+}
+
+void SetProxyReservedSlot(JSObject* obj, uint32_t slot, const JS::Value* val) {
+ js::SetProxyReservedSlot(obj, slot, *val);
+}
+
+JSObject* NewWindowProxy(JSContext* aCx, JS::HandleObject aObj,
+ const void* aHandler) {
+ return WrapperNewSingleton(aCx, aObj, aHandler, &WindowProxyClass);
+}
+
+JS::Value GetProxyPrivate(JSObject* obj) { return js::GetProxyPrivate(obj); }
+
+void SetProxyPrivate(JSObject* obj, const JS::Value* expando) {
+ js::SetProxyPrivate(obj, *expando);
+}
+
+bool RUST_JSID_IS_INT(JS::HandleId id) { return JSID_IS_INT(id); }
+
+jsid int_to_jsid(int32_t i) { return INT_TO_JSID(i); }
+
+int32_t RUST_JSID_TO_INT(JS::HandleId id) { return JSID_TO_INT(id); }
+
+bool RUST_JSID_IS_STRING(JS::HandleId id) { return JSID_IS_STRING(id); }
+
+JSString* RUST_JSID_TO_STRING(JS::HandleId id) { return JSID_TO_STRING(id); }
+
+jsid RUST_SYMBOL_TO_JSID(JS::Symbol* sym) { return SYMBOL_TO_JSID(sym); }
+
+void RUST_SET_JITINFO(JSFunction* func, const JSJitInfo* info) {
+ SET_JITINFO(func, info);
+}
+
+jsid RUST_INTERNED_STRING_TO_JSID(JSContext* cx, JSString* str) {
+ return JS::PropertyKey::fromPinnedString(str);
+}
+
+const JSErrorFormatString* RUST_js_GetErrorMessage(void* userRef,
+ uint32_t errorNumber) {
+ return js::GetErrorMessage(userRef, errorNumber);
+}
+
+bool IsProxyHandlerFamily(JSObject* obj) {
+ auto family = js::GetProxyHandler(obj)->family();
+ return family == &HandlerFamily;
+}
+
+const void* GetProxyHandlerFamily() { return &HandlerFamily; }
+
+const void* GetProxyHandlerExtra(JSObject* obj) {
+ const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+ assert(handler->family() == &HandlerFamily);
+ return static_cast<const ForwardingProxyHandler*>(handler)->getExtra();
+}
+
+const void* GetProxyHandler(JSObject* obj) {
+ const js::BaseProxyHandler* handler = js::GetProxyHandler(obj);
+ assert(handler->family() == &HandlerFamily);
+ return handler;
+}
+
+void ReportError(JSContext* aCx, const char* aError) {
+#ifdef DEBUG
+ for (const char* p = aError; *p; ++p) {
+ assert(*p != '%');
+ }
+#endif
+ JS_ReportErrorASCII(aCx, "%s", aError);
+}
+
+bool IsWrapper(JSObject* obj) { return js::IsWrapper(obj); }
+
+JSObject* UnwrapObjectStatic(JSObject* obj) {
+ return js::CheckedUnwrapStatic(obj);
+}
+
+JSObject* UncheckedUnwrapObject(JSObject* obj, bool stopAtOuter) {
+ return js::UncheckedUnwrap(obj, stopAtOuter);
+}
+
+JS::PersistentRootedIdVector* CreateRootedIdVector(JSContext* cx) {
+ return new JS::PersistentRootedIdVector(cx);
+}
+
+bool AppendToRootedIdVector(JS::PersistentRootedIdVector* v, jsid id) {
+ return v->append(id);
+}
+
+const jsid* SliceRootedIdVector(const JS::PersistentRootedIdVector* v,
+ size_t* length) {
+ *length = v->length();
+ return v->begin();
+}
+
+void DestroyRootedIdVector(JS::PersistentRootedIdVector* v) { delete v; }
+
+JS::MutableHandleIdVector GetMutableHandleIdVector(
+ JS::PersistentRootedIdVector* v) {
+ return JS::MutableHandleIdVector(v);
+}
+
+JS::PersistentRootedObjectVector* CreateRootedObjectVector(JSContext* aCx) {
+ JS::PersistentRootedObjectVector* vec =
+ new JS::PersistentRootedObjectVector(aCx);
+ return vec;
+}
+
+bool AppendToRootedObjectVector(JS::PersistentRootedObjectVector* v,
+ JSObject* obj) {
+ return v->append(obj);
+}
+
+void DeleteRootedObjectVector(JS::PersistentRootedObjectVector* v) { delete v; }
+
+#if defined(__linux__)
+# include <malloc.h>
+#elif defined(__APPLE__)
+# include <malloc/malloc.h>
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+// nothing needed here
+#elif defined(_MSC_VER)
+// nothing needed here
+#else
+# error "unsupported platform"
+#endif
+
+// SpiderMonkey-in-Rust currently uses system malloc, not jemalloc.
+static size_t MallocSizeOf(const void* aPtr) {
+#if defined(__linux__)
+ return malloc_usable_size((void*)aPtr);
+#elif defined(__APPLE__)
+ return malloc_size((void*)aPtr);
+#elif defined(__MINGW32__) || defined(__MINGW64__)
+ return _msize((void*)aPtr);
+#elif defined(_MSC_VER)
+ return _msize((void*)aPtr);
+#else
+# error "unsupported platform"
+#endif
+}
+
+bool CollectServoSizes(JSContext* cx, JS::ServoSizes* sizes) {
+ mozilla::PodZero(sizes);
+ return JS::AddServoSizeOf(cx, MallocSizeOf,
+ /* ObjectPrivateVisitor = */ nullptr, sizes);
+}
+
+void CallValueTracer(JSTracer* trc, JS::Heap<JS::Value>* valuep,
+ const char* name) {
+ JS::TraceEdge(trc, valuep, name);
+}
+
+void CallIdTracer(JSTracer* trc, JS::Heap<jsid>* idp, const char* name) {
+ JS::TraceEdge(trc, idp, name);
+}
+
+void CallObjectTracer(JSTracer* trc, JS::Heap<JSObject*>* objp,
+ const char* name) {
+ JS::TraceEdge(trc, objp, name);
+}
+
+void CallStringTracer(JSTracer* trc, JS::Heap<JSString*>* strp,
+ const char* name) {
+ JS::TraceEdge(trc, strp, name);
+}
+
+void CallBigIntTracer(JSTracer* trc, JS::Heap<JS::BigInt*>* bip,
+ const char* name) {
+ JS::TraceEdge(trc, bip, name);
+}
+
+void CallScriptTracer(JSTracer* trc, JS::Heap<JSScript*>* scriptp,
+ const char* name) {
+ JS::TraceEdge(trc, scriptp, name);
+}
+
+void CallFunctionTracer(JSTracer* trc, JS::Heap<JSFunction*>* funp,
+ const char* name) {
+ JS::TraceEdge(trc, funp, name);
+}
+
+void CallUnbarrieredObjectTracer(JSTracer* trc, JSObject** objp,
+ const char* name) {
+ js::UnsafeTraceManuallyBarrieredEdge(trc, objp, name);
+}
+
+bool IsDebugBuild() {
+#ifdef JS_DEBUG
+ return true;
+#else
+ return false;
+#endif
+}
+
+#define JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Type, type) \
+ void Get##Type##ArrayLengthAndData(JSObject* obj, uint32_t* length, \
+ bool* isSharedMemory, type** data) { \
+ js::Get##Type##ArrayLengthAndData(obj, length, isSharedMemory, data); \
+ }
+
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int8, int8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint8Clamped, uint8_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int16, int16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint16, uint16_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Int32, int32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Uint32, uint32_t)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float32, float)
+JS_DEFINE_DATA_AND_LENGTH_ACCESSOR(Float64, double)
+
+#undef JS_DEFINE_DATA_AND_LENGTH_ACCESSOR
+
+JSAutoStructuredCloneBuffer* NewJSAutoStructuredCloneBuffer(
+ JS::StructuredCloneScope scope,
+ const JSStructuredCloneCallbacks* callbacks) {
+ return js_new<JSAutoStructuredCloneBuffer>(scope, callbacks, nullptr);
+}
+
+JSStructuredCloneData* DeleteJSAutoStructuredCloneBuffer(
+ JSAutoStructuredCloneBuffer* buf) {
+ js_delete(buf);
+}
+
+size_t GetLengthOfJSStructuredCloneData(JSStructuredCloneData* data) {
+ assert(data != nullptr);
+
+ size_t len = 0;
+
+ data->ForEachDataChunk([&](const char* bytes, size_t size) {
+ len += size;
+ return true;
+ });
+
+ return len;
+}
+
+void CopyJSStructuredCloneData(JSStructuredCloneData* src, uint8_t* dest) {
+ assert(src != nullptr);
+ assert(dest != nullptr);
+
+ size_t bytes_copied = 0;
+
+ src->ForEachDataChunk([&](const char* bytes, size_t size) {
+ memcpy(dest, bytes, size);
+ dest += size;
+ return true;
+ });
+}
+
+bool WriteBytesToJSStructuredCloneData(const uint8_t* src, size_t len,
+ JSStructuredCloneData* dest) {
+ assert(src != nullptr);
+ assert(dest != nullptr);
+
+ return dest->AppendBytes(reinterpret_cast<const char*>(src), len);
+}
+
+char* JSEncodeStringToUTF8(JSContext* cx, JS::HandleString string) {
+ return JS_EncodeStringToUTF8(cx, string).release();
+}
+
+} // extern "C"
diff --git a/js/rust/src/jsval.rs b/js/rust/src/jsval.rs
new file mode 100644
index 0000000000..f3a388605a
--- /dev/null
+++ b/js/rust/src/jsval.rs
@@ -0,0 +1,532 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use jsapi::root::*;
+use libc::c_void;
+use std::mem;
+
+#[cfg(target_pointer_width = "64")]
+const JSVAL_TAG_SHIFT: usize = 47;
+
+#[cfg(target_pointer_width = "64")]
+const JSVAL_TAG_MAX_DOUBLE: u32 = 0x1FFF0u32;
+
+#[cfg(target_pointer_width = "32")]
+const JSVAL_TAG_CLEAR: u32 = 0xFFFFFF80;
+
+#[cfg(target_pointer_width = "64")]
+#[repr(u32)]
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug)]
+enum ValueTag {
+ INT32 = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_INT32 as u32),
+ UNDEFINED = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
+ STRING = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_STRING as u32),
+ SYMBOL = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
+ #[cfg(feature = "bigint")]
+ BIGINT = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BIGINT as u32),
+ BOOLEAN = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
+ MAGIC = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_MAGIC as u32),
+ NULL = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_NULL as u32),
+ OBJECT = JSVAL_TAG_MAX_DOUBLE | (JSValueType::JSVAL_TYPE_OBJECT as u32),
+}
+
+#[cfg(target_pointer_width = "32")]
+#[repr(u32)]
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug)]
+enum ValueTag {
+ PRIVATE = 0,
+ INT32 = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_INT32 as u32),
+ UNDEFINED = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_UNDEFINED as u32),
+ STRING = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_STRING as u32),
+ SYMBOL = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_SYMBOL as u32),
+ #[cfg(feature = "bigint")]
+ BIGINT = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BIGINT as u32),
+ BOOLEAN = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_BOOLEAN as u32),
+ MAGIC = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_MAGIC as u32),
+ NULL = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_NULL as u32),
+ OBJECT = JSVAL_TAG_CLEAR as u32 | (JSValueType::JSVAL_TYPE_OBJECT as u32),
+}
+
+#[cfg(target_pointer_width = "64")]
+#[repr(u64)]
+#[allow(dead_code)]
+#[derive(Clone, Copy, Debug)]
+enum ValueShiftedTag {
+ MAX_DOUBLE = (((JSVAL_TAG_MAX_DOUBLE as u64) << JSVAL_TAG_SHIFT) | 0xFFFFFFFFu64),
+ INT32 = ((ValueTag::INT32 as u64) << JSVAL_TAG_SHIFT),
+ UNDEFINED = ((ValueTag::UNDEFINED as u64) << JSVAL_TAG_SHIFT),
+ STRING = ((ValueTag::STRING as u64) << JSVAL_TAG_SHIFT),
+ SYMBOL = ((ValueTag::SYMBOL as u64) << JSVAL_TAG_SHIFT),
+ #[cfg(feature = "bigint")]
+ BIGINT = ((ValueTag::BIGINT as u64) << JSVAL_TAG_SHIFT),
+ BOOLEAN = ((ValueTag::BOOLEAN as u64) << JSVAL_TAG_SHIFT),
+ MAGIC = ((ValueTag::MAGIC as u64) << JSVAL_TAG_SHIFT),
+ NULL = ((ValueTag::NULL as u64) << JSVAL_TAG_SHIFT),
+ OBJECT = ((ValueTag::OBJECT as u64) << JSVAL_TAG_SHIFT),
+}
+
+#[cfg(target_pointer_width = "64")]
+const JSVAL_PAYLOAD_MASK: u64 = 0x00007FFFFFFFFFFF;
+
+#[inline(always)]
+fn AsJSVal(val: u64) -> JS::Value {
+ JS::Value { asBits_: val }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+fn BuildJSVal(tag: ValueTag, payload: u64) -> JS::Value {
+ AsJSVal(((tag as u32 as u64) << JSVAL_TAG_SHIFT) | payload)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+fn BuildJSVal(tag: ValueTag, payload: u64) -> JS::Value {
+ AsJSVal(((tag as u32 as u64) << 32) | payload)
+}
+
+#[inline(always)]
+pub fn NullValue() -> JS::Value {
+ BuildJSVal(ValueTag::NULL, 0)
+}
+
+#[inline(always)]
+pub fn UndefinedValue() -> JS::Value {
+ BuildJSVal(ValueTag::UNDEFINED, 0)
+}
+
+#[inline(always)]
+pub fn Int32Value(i: i32) -> JS::Value {
+ BuildJSVal(ValueTag::INT32, i as u32 as u64)
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn DoubleValue(f: f64) -> JS::Value {
+ let bits: u64 = unsafe { mem::transmute(f) };
+ assert!(bits <= ValueShiftedTag::MAX_DOUBLE as u64);
+ AsJSVal(bits)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn DoubleValue(f: f64) -> JS::Value {
+ let bits: u64 = unsafe { mem::transmute(f) };
+ let val = AsJSVal(bits);
+ assert!(val.is_double());
+ val
+}
+
+#[inline(always)]
+pub fn UInt32Value(ui: u32) -> JS::Value {
+ if ui > 0x7fffffff {
+ DoubleValue(ui as f64)
+ } else {
+ Int32Value(ui as i32)
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn StringValue(s: &JSString) -> JS::Value {
+ let bits = s as *const JSString as usize as u64;
+ assert!((bits >> JSVAL_TAG_SHIFT) == 0);
+ BuildJSVal(ValueTag::STRING, bits)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn StringValue(s: &JSString) -> JS::Value {
+ let bits = s as *const JSString as usize as u64;
+ BuildJSVal(ValueTag::STRING, bits)
+}
+
+#[inline(always)]
+pub fn BooleanValue(b: bool) -> JS::Value {
+ BuildJSVal(ValueTag::BOOLEAN, b as u64)
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn ObjectValue(o: *mut JSObject) -> JS::Value {
+ let bits = o as usize as u64;
+ assert!((bits >> JSVAL_TAG_SHIFT) == 0);
+ BuildJSVal(ValueTag::OBJECT, bits)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn ObjectValue(o: *mut JSObject) -> JS::Value {
+ let bits = o as usize as u64;
+ BuildJSVal(ValueTag::OBJECT, bits)
+}
+
+#[inline(always)]
+pub fn ObjectOrNullValue(o: *mut JSObject) -> JS::Value {
+ if o.is_null() {
+ NullValue()
+ } else {
+ ObjectValue(o)
+ }
+}
+
+#[cfg(target_pointer_width = "64")]
+#[inline(always)]
+pub fn PrivateValue(o: *const c_void) -> JS::Value {
+ let ptrBits = o as usize as u64;
+ assert!((ptrBits & 1) == 0);
+ AsJSVal(ptrBits >> 1)
+}
+
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn PrivateValue(o: *const c_void) -> JS::Value {
+ let ptrBits = o as usize as u64;
+ assert!((ptrBits & 1) == 0);
+ BuildJSVal(ValueTag::PRIVATE, ptrBits)
+}
+
+#[inline(always)]
+#[cfg(feature = "bigint")]
+#[cfg(target_pointer_width = "64")]
+pub fn BigIntValue(b: &JS::BigInt) -> JS::Value {
+ let bits = b as *const JS::BigInt as usize as u64;
+ assert!((bits >> JSVAL_TAG_SHIFT) == 0);
+ BuildJSVal(ValueTag::BIGINT, bits)
+}
+
+#[inline(always)]
+#[cfg(target_pointer_width = "32")]
+#[inline(always)]
+pub fn BigIntValue(s: &JS::BigInt) -> JS::Value {
+ let bits = s as *const JS::BigInt as usize as u64;
+ BuildJSVal(ValueTag::BIGINT, bits)
+}
+
+impl JS::Value {
+ #[inline(always)]
+ unsafe fn asBits(&self) -> u64 {
+ self.asBits_
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_undefined(&self) -> bool {
+ unsafe { self.asBits() == ValueShiftedTag::UNDEFINED as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_undefined(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::UNDEFINED as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_null(&self) -> bool {
+ unsafe { self.asBits() == ValueShiftedTag::NULL as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_null(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::NULL as u64 }
+ }
+
+ #[inline(always)]
+ pub fn is_null_or_undefined(&self) -> bool {
+ self.is_null() || self.is_undefined()
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_boolean(&self) -> bool {
+ unsafe { (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BOOLEAN as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_boolean(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::BOOLEAN as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_int32(&self) -> bool {
+ unsafe { (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::INT32 as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_int32(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::INT32 as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_double(&self) -> bool {
+ unsafe { self.asBits() <= ValueShiftedTag::MAX_DOUBLE as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_double(&self) -> bool {
+ unsafe { (self.asBits() >> 32) <= JSVAL_TAG_CLEAR as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_number(&self) -> bool {
+ const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET: u64 = ValueShiftedTag::UNDEFINED as u64;
+ unsafe { self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_NUMBER_SET }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_number(&self) -> bool {
+ const JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET: u64 = ValueTag::INT32 as u64;
+ unsafe { (self.asBits() >> 32) <= JSVAL_UPPER_INCL_TAG_OF_NUMBER_SET }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_primitive(&self) -> bool {
+ const JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET: u64 = ValueShiftedTag::OBJECT as u64;
+ unsafe { self.asBits() < JSVAL_UPPER_EXCL_SHIFTED_TAG_OF_PRIMITIVE_SET }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_primitive(&self) -> bool {
+ const JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET: u64 = ValueTag::OBJECT as u64;
+ unsafe { (self.asBits() >> 32) < JSVAL_UPPER_EXCL_TAG_OF_PRIMITIVE_SET }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_string(&self) -> bool {
+ unsafe { (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::STRING as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_string(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::STRING as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_object(&self) -> bool {
+ unsafe {
+ assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
+ self.asBits() >= ValueShiftedTag::OBJECT as u64
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_object(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::OBJECT as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_symbol(&self) -> bool {
+ unsafe { (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::SYMBOL as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_symbol(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::SYMBOL as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(feature = "bigint")]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_bigint(&self) -> bool {
+ unsafe { (self.asBits() >> JSVAL_TAG_SHIFT) == ValueTag::BIGINT as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(feature = "bigint")]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_bigint(&self) -> bool {
+ unsafe { (self.asBits() >> 32) == ValueTag::BIGINT as u64 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn to_boolean(&self) -> bool {
+ assert!(self.is_boolean());
+ unsafe { (self.asBits() & JSVAL_PAYLOAD_MASK) != 0 }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn to_boolean(&self) -> bool {
+ assert!(self.is_boolean());
+ unsafe { (self.asBits() & 0x00000000FFFFFFFF) != 0 }
+ }
+
+ #[inline(always)]
+ pub fn to_int32(&self) -> i32 {
+ assert!(self.is_int32());
+ unsafe { (self.asBits() & 0x00000000FFFFFFFF) as i32 }
+ }
+
+ #[inline(always)]
+ pub fn to_double(&self) -> f64 {
+ assert!(self.is_double());
+ unsafe { mem::transmute(self.asBits()) }
+ }
+
+ #[inline(always)]
+ pub fn to_number(&self) -> f64 {
+ assert!(self.is_number());
+ if self.is_double() {
+ self.to_double()
+ } else {
+ self.to_int32() as f64
+ }
+ }
+
+ #[inline(always)]
+ pub fn to_object(&self) -> *mut JSObject {
+ assert!(self.is_object());
+ self.to_object_or_null()
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn to_string(&self) -> *mut JSString {
+ assert!(self.is_string());
+ unsafe {
+ let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
+ ptrBits as usize as *mut JSString
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn to_string(&self) -> *mut JSString {
+ assert!(self.is_string());
+ unsafe {
+ let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+ ptrBits as *mut JSString
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_object_or_null(&self) -> bool {
+ const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueShiftedTag::NULL as u64;
+ unsafe {
+ assert!((self.asBits() >> JSVAL_TAG_SHIFT) <= ValueTag::OBJECT as u64);
+ self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_OBJ_OR_NULL_SET
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_object_or_null(&self) -> bool {
+ const JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET: u64 = ValueTag::NULL as u64;
+ unsafe {
+ assert!((self.asBits() >> 32) <= ValueTag::OBJECT as u64);
+ (self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_OBJ_OR_NULL_SET
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn to_object_or_null(&self) -> *mut JSObject {
+ assert!(self.is_object_or_null());
+ unsafe {
+ let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
+ assert!((ptrBits & 0x7) == 0);
+ ptrBits as usize as *mut JSObject
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn to_object_or_null(&self) -> *mut JSObject {
+ assert!(self.is_object_or_null());
+ unsafe {
+ let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+ ptrBits as *mut JSObject
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn to_private(&self) -> *const c_void {
+ assert!(self.is_double());
+ unsafe {
+ assert!((self.asBits() & 0x8000000000000000u64) == 0);
+ (self.asBits() << 1) as usize as *const c_void
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn to_private(&self) -> *const c_void {
+ unsafe {
+ let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+ ptrBits as *const c_void
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn is_gcthing(&self) -> bool {
+ const JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET: u64 = ValueShiftedTag::STRING as u64;
+ unsafe { self.asBits() >= JSVAL_LOWER_INCL_SHIFTED_TAG_OF_GCTHING_SET }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn is_gcthing(&self) -> bool {
+ const JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET: u64 = ValueTag::STRING as u64;
+ unsafe { (self.asBits() >> 32) >= JSVAL_LOWER_INCL_TAG_OF_GCTHING_SET }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "64")]
+ pub fn to_gcthing(&self) -> *mut c_void {
+ assert!(self.is_gcthing());
+ unsafe {
+ let ptrBits = self.asBits() & JSVAL_PAYLOAD_MASK;
+ assert!((ptrBits & 0x7) == 0);
+ ptrBits as *mut c_void
+ }
+ }
+
+ #[inline(always)]
+ #[cfg(target_pointer_width = "32")]
+ pub fn to_gcthing(&self) -> *mut c_void {
+ assert!(self.is_gcthing());
+ unsafe {
+ let ptrBits: u32 = (self.asBits() & 0x00000000FFFFFFFF) as u32;
+ ptrBits as *mut c_void
+ }
+ }
+
+ #[inline(always)]
+ pub fn is_markable(&self) -> bool {
+ self.is_gcthing() && !self.is_null()
+ }
+
+ #[inline(always)]
+ pub fn trace_kind(&self) -> JS::TraceKind {
+ assert!(self.is_markable());
+ if self.is_object() {
+ JS::TraceKind::Object
+ } else {
+ JS::TraceKind::String
+ }
+ }
+}
diff --git a/js/rust/src/lib.rs b/js/rust/src/lib.rs
new file mode 100644
index 0000000000..c16d1e1702
--- /dev/null
+++ b/js/rust/src/lib.rs
@@ -0,0 +1,53 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#![crate_name = "js"]
+#![crate_type = "rlib"]
+#![cfg_attr(feature = "nonzero", feature(nonzero))]
+#![allow(
+ non_upper_case_globals,
+ non_camel_case_types,
+ non_snake_case,
+ improper_ctypes
+)]
+
+#[cfg(feature = "nonzero")]
+extern crate core;
+#[macro_use]
+extern crate lazy_static;
+extern crate libc;
+#[macro_use]
+extern crate log;
+#[allow(unused_extern_crates)]
+extern crate mozjs_sys;
+extern crate num_traits;
+
+#[macro_use]
+pub mod rust;
+
+pub mod ar;
+pub mod conversions;
+pub mod error;
+pub mod glue;
+pub mod heap;
+pub mod jsval;
+pub mod panic;
+pub mod sc;
+pub mod typedarray;
+
+pub mod jsapi;
+use self::jsapi::root::*;
+
+#[inline(always)]
+pub unsafe fn JS_ARGV(_cx: *mut JSContext, vp: *mut JS::Value) -> *mut JS::Value {
+ vp.offset(2)
+}
+
+impl JS::ObjectOpResult {
+ /// Set this ObjectOpResult to true and return true.
+ pub fn succeed(&mut self) -> bool {
+ self.code_ = JS::ObjectOpResult_SpecialCodes::OkCode as usize;
+ true
+ }
+}
diff --git a/js/rust/src/panic.rs b/js/rust/src/panic.rs
new file mode 100644
index 0000000000..496ed9b33d
--- /dev/null
+++ b/js/rust/src/panic.rs
@@ -0,0 +1,34 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+use std::any::Any;
+use std::cell::RefCell;
+use std::panic::{catch_unwind, resume_unwind, UnwindSafe};
+
+thread_local!(static PANIC_RESULT: RefCell<Option<Box<Any + Send>>> = RefCell::new(None));
+
+/// If there is a pending panic, resume unwinding.
+pub fn maybe_resume_unwind() {
+ if let Some(error) = PANIC_RESULT.with(|result| result.borrow_mut().take()) {
+ resume_unwind(error);
+ }
+}
+
+/// Generic wrapper for JS engine callbacks panic-catching
+pub fn wrap_panic<F, R>(function: F, generic_return_type: R) -> R
+where
+ F: FnOnce() -> R + UnwindSafe,
+{
+ let result = catch_unwind(function);
+ match result {
+ Ok(result) => result,
+ Err(error) => {
+ PANIC_RESULT.with(|result| {
+ assert!(result.borrow().is_none());
+ *result.borrow_mut() = Some(error);
+ });
+ generic_return_type
+ }
+ }
+}
diff --git a/js/rust/src/rust.rs b/js/rust/src/rust.rs
new file mode 100644
index 0000000000..c92554f91e
--- /dev/null
+++ b/js/rust/src/rust.rs
@@ -0,0 +1,1312 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Rust wrappers around the raw JS apis
+
+use ar::AutoRealm;
+use glue::{
+ AppendToRootedObjectVector, CreateCallArgsFromVp, CreateRootedObjectVector,
+ DeleteRootedObjectVector, IsDebugBuild,
+};
+use glue::{
+ CreateRootedIdVector, DestroyRootedIdVector, GetMutableHandleIdVector, SliceRootedIdVector,
+};
+use glue::{DeleteCompileOptions, NewCompileOptions};
+use jsapi::root::*;
+use jsval::{self, UndefinedValue};
+use libc::c_uint;
+use panic;
+use std::cell::{Cell, UnsafeCell};
+use std::char;
+use std::default::Default;
+use std::ffi;
+use std::marker;
+use std::mem;
+use std::ops::{Deref, DerefMut};
+use std::ptr;
+use std::slice;
+use std::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
+use std::sync::mpsc::{sync_channel, SyncSender};
+use std::sync::{Arc, Mutex, Once, ONCE_INIT};
+use std::thread;
+use std::u32;
+
+const DEFAULT_HEAPSIZE: u32 = 32_u32 * 1024_u32 * 1024_u32;
+
+// From Gecko:
+// Our "default" stack is what we use in configurations where we don't have a compelling reason to
+// do things differently. This is effectively 1MB on 64-bit platforms.
+const STACK_QUOTA: usize = 128 * 8 * 1024;
+
+// From Gecko:
+// (See js/xpconnect/src/XPCJSContext.cpp)
+// We tune the trusted/untrusted quotas for each configuration to achieve our
+// invariants while attempting to minimize overhead. In contrast, our buffer
+// between system code and trusted script is a very unscientific 10k.
+const SYSTEM_CODE_BUFFER: usize = 10 * 1024;
+
+// Gecko's value on 64-bit.
+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(())
+ }
+ }
+}
+
+// ___________________________________________________________________________
+// friendly Rustic API to runtimes
+
+thread_local!(static CONTEXT: Cell<*mut JSContext> = Cell::new(ptr::null_mut()));
+
+lazy_static! {
+ static ref OUTSTANDING_RUNTIMES: AtomicUsize = AtomicUsize::new(0);
+ static ref SHUT_DOWN: AtomicBool = AtomicBool::new(false);
+ static ref SHUT_DOWN_SIGNAL: Arc<Mutex<Option<SyncSender<()>>>> = Arc::new(Mutex::new(None));
+}
+
+/// A wrapper for the `JSContext` structure in SpiderMonkey.
+pub struct Runtime {
+ cx: *mut JSContext,
+}
+
+impl Runtime {
+ /// Get the `JSContext` for this thread.
+ pub fn get() -> *mut JSContext {
+ let cx = CONTEXT.with(|context| context.get());
+ assert!(!cx.is_null());
+ cx
+ }
+
+ /// Creates a new `JSContext`.
+ ///
+ /// * `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<Runtime, ()> {
+ if SHUT_DOWN.load(Ordering::SeqCst) {
+ return Err(());
+ }
+
+ OUTSTANDING_RUNTIMES.fetch_add(1, Ordering::SeqCst);
+
+ unsafe {
+ #[derive(Debug)]
+ struct Parent(UnsafeCell<*mut JSContext>);
+ unsafe impl ::std::marker::Sync for Parent {}
+
+ impl Parent {
+ fn set(&self, val: *mut JSContext) {
+ self.as_atomic().store(val, Ordering::SeqCst);
+ assert_eq!(self.get(), val);
+ }
+
+ fn get(&self) -> *mut JSContext {
+ self.as_atomic().load(Ordering::SeqCst)
+ }
+
+ fn as_atomic(&self) -> &AtomicPtr<JSContext> {
+ unsafe { mem::transmute(&self.0) }
+ }
+ }
+
+ lazy_static! {
+ static ref PARENT: Parent = Parent(UnsafeCell::new(0 as *mut _));
+ }
+ static ONCE: Once = ONCE_INIT;
+
+ ONCE.call_once(|| {
+ // There is a 1:1 relationship between threads and JSContexts,
+ // so we must spawn a new thread for the parent context.
+ let (tx, rx) = sync_channel(0);
+ *SHUT_DOWN_SIGNAL.lock().unwrap() = Some(tx);
+ let _ = thread::spawn(move || {
+ let is_debug_mozjs = cfg!(feature = "debugmozjs");
+ let diagnostic = JS::detail::InitWithFailureDiagnostic(is_debug_mozjs);
+ if !diagnostic.is_null() {
+ panic!(
+ "JS::detail::InitWithFailureDiagnostic failed: {}",
+ ffi::CStr::from_ptr(diagnostic).to_string_lossy()
+ );
+ }
+
+ let context = JS_NewContext(DEFAULT_HEAPSIZE, ptr::null_mut());
+ assert!(!context.is_null());
+ JS::InitSelfHostedCode(context);
+ PARENT.set(context);
+
+ // The last JSRuntime child died, resume the execution by destroying the parent.
+ rx.recv().unwrap();
+ let cx = PARENT.get();
+ JS_DestroyContext(cx);
+ JS_ShutDown();
+ PARENT.set(0 as *mut _);
+ // Unblock the last child thread, waiting for the JS_ShutDown.
+ rx.recv().unwrap();
+ });
+
+ while PARENT.get().is_null() {
+ thread::yield_now();
+ }
+ });
+
+ assert_eq!(IsDebugBuild(), cfg!(feature = "debugmozjs"));
+
+ let js_context = JS_NewContext(DEFAULT_HEAPSIZE, JS_GetParentRuntime(PARENT.get()));
+ assert!(!js_context.is_null());
+
+ // Unconstrain the runtime's threshold on nominal heap size, to avoid
+ // triggering GC too often if operating continuously near an arbitrary
+ // finite threshold. This leaves the maximum-JS_malloc-bytes threshold
+ // still in effect to cause periodical, and we hope hygienic,
+ // last-ditch GCs from within the GC's allocator.
+ JS_SetGCParameter(js_context, JSGCParamKey::JSGC_MAX_BYTES, u32::MAX);
+
+ 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);
+ });
+
+ if use_internal_job_queue {
+ assert!(js::UseInternalJobQueues(js_context));
+ }
+
+ JS::InitSelfHostedCode(js_context);
+
+ JS::SetWarningReporter(js_context, Some(report_warning));
+
+ Ok(Runtime { cx: js_context })
+ }
+ }
+
+ /// Returns the underlying `JSContext` object.
+ pub fn cx(&self) -> *mut JSContext {
+ self.cx
+ }
+
+ /// Returns the underlying `JSContext`'s `JSRuntime`.
+ pub fn rt(&self) -> *mut JSRuntime {
+ unsafe { JS_GetRuntime(self.cx) }
+ }
+
+ pub fn evaluate_script(
+ &self,
+ glob: JS::HandleObject,
+ script: &str,
+ filename: &str,
+ line_num: u32,
+ rval: JS::MutableHandleValue,
+ ) -> Result<(), ()> {
+ let script_utf16: Vec<u16> = script.encode_utf16().collect();
+ let filename_cstr = ffi::CString::new(filename.as_bytes()).unwrap();
+ debug!(
+ "Evaluating script from {} with content {}",
+ filename, script
+ );
+ // SpiderMonkey does not approve of null pointers.
+ let (ptr, len) = if script_utf16.len() == 0 {
+ static empty: &'static [u16] = &[];
+ (empty.as_ptr(), 0)
+ } else {
+ (script_utf16.as_ptr(), script_utf16.len() as c_uint)
+ };
+ assert!(!ptr.is_null());
+ unsafe {
+ let _ar = AutoRealm::with_obj(self.cx(), glob.get());
+ let options = CompileOptionsWrapper::new(self.cx(), filename_cstr.as_ptr(), line_num);
+
+ let mut srcBuf = JS::SourceText {
+ units_: ptr,
+ length_: len as _,
+ ownsUnits_: false,
+ _phantom_0: marker::PhantomData,
+ };
+ if !JS::Evaluate(self.cx(), options.ptr, &mut srcBuf, rval) {
+ debug!("...err!");
+ panic::maybe_resume_unwind();
+ Err(())
+ } else {
+ // we could return the script result but then we'd have
+ // to root it and so forth and, really, who cares?
+ debug!("...ok!");
+ Ok(())
+ }
+ }
+ }
+}
+
+impl Drop for Runtime {
+ fn drop(&mut self) {
+ unsafe {
+ JS_DestroyContext(self.cx);
+
+ CONTEXT.with(|context| {
+ assert_eq!(context.get(), self.cx);
+ context.set(ptr::null_mut());
+ });
+
+ if OUTSTANDING_RUNTIMES.fetch_sub(1, Ordering::SeqCst) == 1 {
+ SHUT_DOWN.store(true, Ordering::SeqCst);
+ let signal = &SHUT_DOWN_SIGNAL.lock().unwrap();
+ let signal = signal.as_ref().unwrap();
+ // Send a signal to shutdown the Parent runtime.
+ signal.send(()).unwrap();
+ // Wait for it to be destroyed before resuming the execution
+ // of static variable destructors.
+ signal.send(()).unwrap();
+ }
+ }
+ }
+}
+
+// ___________________________________________________________________________
+// Rooting API for standard JS things
+
+pub trait RootKind {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind;
+}
+
+impl RootKind for *mut JSObject {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::Object
+ }
+}
+
+impl RootKind for *mut JSLinearString {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::String
+ }
+}
+
+impl RootKind for *mut JSFunction {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::Object
+ }
+}
+
+impl RootKind for *mut JSString {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::String
+ }
+}
+
+impl RootKind for *mut JS::Symbol {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::Symbol
+ }
+}
+
+#[cfg(feature = "bigint")]
+impl RootKind for *mut JS::BigInt {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::BigInt
+ }
+}
+
+impl RootKind for *mut JSScript {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::Script
+ }
+}
+
+impl RootKind for jsid {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::Id
+ }
+}
+
+impl RootKind for JS::Value {
+ #[inline(always)]
+ fn rootKind() -> JS::RootKind {
+ JS::RootKind::Value
+ }
+}
+
+impl<T> JS::Rooted<T> {
+ pub fn new_unrooted() -> JS::Rooted<T>
+ where
+ T: GCMethods,
+ {
+ JS::Rooted {
+ stack: ptr::null_mut(),
+ prev: ptr::null_mut(),
+ ptr: unsafe { T::initial() },
+ _phantom_0: marker::PhantomData,
+ }
+ }
+
+ unsafe fn get_rooting_context(cx: *mut JSContext) -> *mut JS::RootingContext {
+ mem::transmute(cx)
+ }
+
+ unsafe fn get_root_stack(
+ cx: *mut JSContext,
+ ) -> *mut *mut JS::Rooted<*mut JS::detail::RootListEntry>
+ where
+ T: RootKind,
+ {
+ let kind = T::rootKind() as usize;
+ let rooting_cx = Self::get_rooting_context(cx);
+ &mut rooting_cx.as_mut().unwrap().stackRoots_[kind] as *mut _ as *mut _
+ }
+
+ pub unsafe fn register_with_root_lists(&mut self, cx: *mut JSContext)
+ where
+ T: RootKind,
+ {
+ self.stack = Self::get_root_stack(cx);
+ let stack = self.stack.as_mut().unwrap();
+ self.prev = *stack as *mut _;
+
+ *stack = self as *mut _ as usize as _;
+ }
+
+ pub unsafe fn remove_from_root_stack(&mut self) {
+ assert!(*self.stack == self as *mut _ as usize as _);
+ *self.stack = self.prev;
+ }
+}
+
+/// Rust API for keeping a JS::Rooted value in the context's root stack.
+/// Example usage: `rooted!(in(cx) let x = UndefinedValue());`.
+/// `RootedGuard::new` also works, but the macro is preferred.
+pub struct RootedGuard<'a, T: 'a + RootKind + GCMethods> {
+ root: &'a mut JS::Rooted<T>,
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> RootedGuard<'a, T> {
+ pub fn new(cx: *mut JSContext, root: &'a mut JS::Rooted<T>, initial: T) -> Self {
+ root.ptr = initial;
+ unsafe {
+ root.register_with_root_lists(cx);
+ }
+ RootedGuard { root: root }
+ }
+
+ pub fn handle(&self) -> JS::Handle<T> {
+ unsafe { JS::Handle::from_marked_location(&self.root.ptr) }
+ }
+
+ pub fn handle_mut(&mut self) -> JS::MutableHandle<T> {
+ unsafe { JS::MutableHandle::from_marked_location(&mut self.root.ptr) }
+ }
+
+ pub fn get(&self) -> T
+ where
+ T: Copy,
+ {
+ self.root.ptr
+ }
+
+ pub fn set(&mut self, v: T) {
+ self.root.ptr = v;
+ }
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> Deref for RootedGuard<'a, T> {
+ type Target = T;
+ fn deref(&self) -> &T {
+ &self.root.ptr
+ }
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> DerefMut for RootedGuard<'a, T> {
+ fn deref_mut(&mut self) -> &mut T {
+ &mut self.root.ptr
+ }
+}
+
+impl<'a, T: 'a + RootKind + GCMethods> Drop for RootedGuard<'a, T> {
+ fn drop(&mut self) {
+ unsafe {
+ self.root.ptr = T::initial();
+ self.root.remove_from_root_stack();
+ }
+ }
+}
+
+#[macro_export]
+macro_rules! rooted {
+ (in($cx:expr) let $name:ident = $init:expr) => {
+ let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+ let $name = $crate::rust::RootedGuard::new($cx, &mut __root, $init);
+ };
+ (in($cx:expr) let mut $name:ident = $init:expr) => {
+ let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+ let mut $name = $crate::rust::RootedGuard::new($cx, &mut __root, $init);
+ };
+}
+
+impl<T> JS::Handle<T> {
+ pub fn get(&self) -> T
+ where
+ T: Copy,
+ {
+ unsafe { *self.ptr }
+ }
+
+ pub unsafe fn from_marked_location(ptr: *const T) -> JS::Handle<T> {
+ JS::Handle {
+ ptr: mem::transmute(ptr),
+ _phantom_0: marker::PhantomData,
+ }
+ }
+}
+
+impl<T> Deref for JS::Handle<T> {
+ type Target = T;
+
+ fn deref<'a>(&'a self) -> &'a T {
+ unsafe { &*self.ptr }
+ }
+}
+
+impl<T> JS::MutableHandle<T> {
+ pub unsafe fn from_marked_location(ptr: *mut T) -> JS::MutableHandle<T> {
+ JS::MutableHandle {
+ ptr: ptr,
+ _phantom_0: marker::PhantomData,
+ }
+ }
+
+ pub fn handle(&self) -> JS::Handle<T> {
+ unsafe { JS::Handle::from_marked_location(self.ptr as *const _) }
+ }
+
+ pub fn get(&self) -> T
+ where
+ T: Copy,
+ {
+ unsafe { *self.ptr }
+ }
+
+ pub fn set(&self, v: T)
+ where
+ T: Copy,
+ {
+ unsafe { *self.ptr = v }
+ }
+}
+
+impl<T> Deref for JS::MutableHandle<T> {
+ type Target = T;
+
+ fn deref<'a>(&'a self) -> &'a T {
+ unsafe { &*self.ptr }
+ }
+}
+
+impl<T> DerefMut for JS::MutableHandle<T> {
+ fn deref_mut<'a>(&'a mut self) -> &'a mut T {
+ unsafe { &mut *self.ptr }
+ }
+}
+
+impl JS::HandleValue {
+ pub fn null() -> JS::HandleValue {
+ unsafe { JS::NullHandleValue }
+ }
+
+ pub fn undefined() -> JS::HandleValue {
+ unsafe { JS::UndefinedHandleValue }
+ }
+}
+
+impl JS::HandleValueArray {
+ pub fn new() -> JS::HandleValueArray {
+ JS::HandleValueArray {
+ length_: 0,
+ elements_: ptr::null(),
+ }
+ }
+
+ pub unsafe fn from_rooted_slice(values: &[JS::Value]) -> JS::HandleValueArray {
+ JS::HandleValueArray {
+ length_: values.len(),
+ elements_: values.as_ptr(),
+ }
+ }
+}
+
+const ConstNullValue: *mut JSObject = 0 as *mut JSObject;
+
+impl JS::HandleObject {
+ pub fn null() -> JS::HandleObject {
+ unsafe { JS::HandleObject::from_marked_location(&ConstNullValue) }
+ }
+}
+
+impl Default for jsid {
+ fn default() -> jsid {
+ jsid {
+ asBits: JSID_TYPE_VOID as usize,
+ }
+ }
+}
+
+impl Default for JS::Value {
+ fn default() -> JS::Value {
+ jsval::UndefinedValue()
+ }
+}
+
+impl Default for JS::RealmOptions {
+ fn default() -> Self {
+ unsafe { ::std::mem::zeroed() }
+ }
+}
+
+pub trait GCMethods {
+ unsafe fn initial() -> Self;
+ unsafe fn write_barriers(v: *mut Self, prev: Self, next: Self);
+}
+
+impl GCMethods for jsid {
+ unsafe fn initial() -> jsid {
+ Default::default()
+ }
+ unsafe fn write_barriers(_: *mut jsid, _: jsid, _: jsid) {}
+}
+
+#[cfg(feature = "bigint")]
+impl GCMethods for *mut JS::BigInt {
+ unsafe fn initial() -> *mut JS::BigInt {
+ ptr::null_mut()
+ }
+ unsafe fn write_barriers(
+ v: *mut *mut JS::BigInt,
+ prev: *mut JS::BigInt,
+ next: *mut JS::BigInt,
+ ) {
+ JS::HeapBigIntWriteBarriers(v, prev, next);
+ }
+}
+
+impl GCMethods for *mut JSObject {
+ unsafe fn initial() -> *mut JSObject {
+ ptr::null_mut()
+ }
+ unsafe fn write_barriers(v: *mut *mut JSObject, prev: *mut JSObject, next: *mut JSObject) {
+ JS::HeapObjectWriteBarriers(v, prev, next);
+ }
+}
+
+impl GCMethods for *mut JSString {
+ unsafe fn initial() -> *mut JSString {
+ ptr::null_mut()
+ }
+ unsafe fn write_barriers(v: *mut *mut JSString, prev: *mut JSString, next: *mut JSString) {
+ JS::HeapStringWriteBarriers(v, prev, next);
+ }
+}
+
+impl GCMethods for *mut JSScript {
+ unsafe fn initial() -> *mut JSScript {
+ ptr::null_mut()
+ }
+ unsafe fn write_barriers(v: *mut *mut JSScript, prev: *mut JSScript, next: *mut JSScript) {
+ JS::HeapScriptWriteBarriers(v, prev, next);
+ }
+}
+
+impl GCMethods for *mut JSFunction {
+ unsafe fn initial() -> *mut JSFunction {
+ ptr::null_mut()
+ }
+ unsafe fn write_barriers(
+ v: *mut *mut JSFunction,
+ prev: *mut JSFunction,
+ next: *mut JSFunction,
+ ) {
+ JS::HeapObjectWriteBarriers(
+ mem::transmute(v),
+ mem::transmute(prev),
+ mem::transmute(next),
+ );
+ }
+}
+
+impl GCMethods for JS::Value {
+ unsafe fn initial() -> JS::Value {
+ UndefinedValue()
+ }
+ unsafe fn write_barriers(v: *mut JS::Value, prev: JS::Value, next: JS::Value) {
+ JS::HeapValueWriteBarriers(v, &prev, &next);
+ }
+}
+
+// ___________________________________________________________________________
+// Implementations for various things in jsapi.rs
+
+impl Drop for JSAutoRealm {
+ fn drop(&mut self) {
+ unsafe {
+ JS::LeaveRealm(self.cx_, self.oldRealm_);
+ }
+ }
+}
+
+impl JSJitMethodCallArgs {
+ #[inline]
+ pub fn get(&self, i: u32) -> JS::HandleValue {
+ unsafe {
+ if i < self._base.argc_ {
+ JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+ } else {
+ JS::UndefinedHandleValue
+ }
+ }
+ }
+
+ #[inline]
+ pub fn index(&self, i: u32) -> JS::HandleValue {
+ assert!(i < self._base.argc_);
+ unsafe { JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize)) }
+ }
+
+ #[inline]
+ pub fn index_mut(&self, i: u32) -> JS::MutableHandleValue {
+ assert!(i < self._base.argc_);
+ unsafe { JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(i as isize)) }
+ }
+
+ #[inline]
+ pub fn rval(&self) -> JS::MutableHandleValue {
+ unsafe { JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(-2)) }
+ }
+}
+
+// XXX need to hack up bindgen to convert this better so we don't have
+// to duplicate so much code here
+impl JS::CallArgs {
+ #[inline]
+ pub unsafe fn from_vp(vp: *mut JS::Value, argc: u32) -> JS::CallArgs {
+ CreateCallArgsFromVp(argc, vp)
+ }
+
+ #[inline]
+ pub fn index(&self, i: u32) -> JS::HandleValue {
+ assert!(i < self._base.argc_);
+ unsafe { JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize)) }
+ }
+
+ #[inline]
+ pub fn index_mut(&self, i: u32) -> JS::MutableHandleValue {
+ assert!(i < self._base.argc_);
+ unsafe { JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(i as isize)) }
+ }
+
+ #[inline]
+ pub fn get(&self, i: u32) -> JS::HandleValue {
+ unsafe {
+ if i < self._base.argc_ {
+ JS::HandleValue::from_marked_location(self._base.argv_.offset(i as isize))
+ } else {
+ JS::UndefinedHandleValue
+ }
+ }
+ }
+
+ #[inline]
+ pub fn rval(&self) -> JS::MutableHandleValue {
+ unsafe { JS::MutableHandleValue::from_marked_location(self._base.argv_.offset(-2)) }
+ }
+
+ #[inline]
+ pub fn thisv(&self) -> JS::HandleValue {
+ unsafe { JS::HandleValue::from_marked_location(self._base.argv_.offset(-1)) }
+ }
+
+ #[inline]
+ pub fn calleev(&self) -> JS::HandleValue {
+ unsafe { JS::HandleValue::from_marked_location(self._base.argv_.offset(-2)) }
+ }
+
+ #[inline]
+ pub fn callee(&self) -> *mut JSObject {
+ self.calleev().to_object()
+ }
+
+ #[inline]
+ pub fn new_target(&self) -> JS::MutableHandleValue {
+ assert!(self._base.constructing_());
+ unsafe {
+ JS::MutableHandleValue::from_marked_location(
+ self._base.argv_.offset(self._base.argc_ as isize),
+ )
+ }
+ }
+}
+
+impl JSJitGetterCallArgs {
+ #[inline]
+ pub fn rval(&self) -> JS::MutableHandleValue {
+ self._base
+ }
+}
+
+impl JSJitSetterCallArgs {
+ #[inline]
+ pub fn get(&self, i: u32) -> JS::HandleValue {
+ assert!(i == 0);
+ self._base.handle()
+ }
+}
+
+// ___________________________________________________________________________
+// Wrappers around things in jsglue.cpp
+
+pub struct RootedObjectVectorWrapper {
+ pub ptr: *mut JS::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) }
+ }
+}
+
+impl Drop for RootedObjectVectorWrapper {
+ fn drop(&mut self) {
+ unsafe { DeleteRootedObjectVector(self.ptr) }
+ }
+}
+
+pub struct CompileOptionsWrapper {
+ pub ptr: *mut JS::ReadOnlyCompileOptions,
+}
+
+impl CompileOptionsWrapper {
+ pub fn new(
+ cx: *mut JSContext,
+ file: *const ::libc::c_char,
+ line: c_uint,
+ ) -> CompileOptionsWrapper {
+ CompileOptionsWrapper {
+ ptr: unsafe { NewCompileOptions(cx, file, line) },
+ }
+ }
+}
+
+impl Drop for CompileOptionsWrapper {
+ fn drop(&mut self) {
+ unsafe { DeleteCompileOptions(self.ptr) }
+ }
+}
+
+// ___________________________________________________________________________
+// Fast inline converters
+
+#[inline]
+pub unsafe fn ToBoolean(v: JS::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;
+ }
+
+ js::ToBooleanSlow(v)
+}
+
+#[inline]
+pub unsafe fn ToNumber(cx: *mut JSContext, v: JS::HandleValue) -> Result<f64, ()> {
+ let val = *v.ptr;
+ if val.is_number() {
+ return Ok(val.to_number());
+ }
+
+ let mut out = Default::default();
+ if js::ToNumberSlow(cx, v, &mut out) {
+ Ok(out)
+ } else {
+ Err(())
+ }
+}
+
+#[inline]
+unsafe fn convert_from_int32<T: Default + Copy>(
+ cx: *mut JSContext,
+ v: JS::HandleValue,
+ conv_fn: unsafe extern "C" fn(*mut JSContext, JS::HandleValue, *mut T) -> bool,
+) -> Result<T, ()> {
+ let val = *v.ptr;
+ if val.is_int32() {
+ let intval: i64 = val.to_int32() as i64;
+ // TODO: do something better here that works on big endian
+ let intval = *(&intval as *const i64 as *const T);
+ return Ok(intval);
+ }
+
+ let mut out = Default::default();
+ if conv_fn(cx, v, &mut out) {
+ Ok(out)
+ } else {
+ Err(())
+ }
+}
+
+#[inline]
+pub unsafe fn ToInt32(cx: *mut JSContext, v: JS::HandleValue) -> Result<i32, ()> {
+ convert_from_int32::<i32>(cx, v, js::ToInt32Slow)
+}
+
+#[inline]
+pub unsafe fn ToUint32(cx: *mut JSContext, v: JS::HandleValue) -> Result<u32, ()> {
+ convert_from_int32::<u32>(cx, v, js::ToUint32Slow)
+}
+
+#[inline]
+pub unsafe fn ToUint16(cx: *mut JSContext, v: JS::HandleValue) -> Result<u16, ()> {
+ convert_from_int32::<u16>(cx, v, js::ToUint16Slow)
+}
+
+#[inline]
+pub unsafe fn ToInt64(cx: *mut JSContext, v: JS::HandleValue) -> Result<i64, ()> {
+ convert_from_int32::<i64>(cx, v, js::ToInt64Slow)
+}
+
+#[inline]
+pub unsafe fn ToUint64(cx: *mut JSContext, v: JS::HandleValue) -> Result<u64, ()> {
+ convert_from_int32::<u64>(cx, v, js::ToUint64Slow)
+}
+
+#[inline]
+pub unsafe fn ToString(cx: *mut JSContext, v: JS::HandleValue) -> *mut JSString {
+ let val = *v.ptr;
+ if val.is_string() {
+ return val.to_string();
+ }
+
+ js::ToStringSlow(cx, v)
+}
+
+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;
+ let fname = if !fnptr.is_null() {
+ let c_str = ffi::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;
+
+ let msg_ptr = (*report)._base.message_.data_ as *const u16;
+ 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 = String::from_utf16_lossy(msg_slice);
+
+ warn!("Warning at {}:{}:{}: {}\n", fname, lineno, column, msg);
+}
+
+impl JSNativeWrapper {
+ fn is_zeroed(&self) -> bool {
+ let JSNativeWrapper { op, info } = *self;
+ op.is_none() && info.is_null()
+ }
+}
+
+pub struct RootedIdVectorWrapper {
+ pub ptr: *mut JS::PersistentRootedIdVector,
+}
+
+impl RootedIdVectorWrapper {
+ pub fn new(cx: *mut JSContext) -> RootedIdVectorWrapper {
+ RootedIdVectorWrapper {
+ ptr: unsafe { CreateRootedIdVector(cx) },
+ }
+ }
+
+ pub fn handle_mut(&self) -> JS::MutableHandleIdVector {
+ unsafe { GetMutableHandleIdVector(self.ptr) }
+ }
+}
+
+impl Drop for RootedIdVectorWrapper {
+ fn drop(&mut self) {
+ unsafe { DestroyRootedIdVector(self.ptr) }
+ }
+}
+
+impl Deref for RootedIdVectorWrapper {
+ type Target = [jsid];
+
+ fn deref(&self) -> &[jsid] {
+ unsafe {
+ let mut length = 0;
+ let pointer = SliceRootedIdVector(self.ptr as *const _, &mut length);
+ slice::from_raw_parts(pointer, length)
+ }
+ }
+}
+
+/// Defines methods on `obj`. The last entry of `methods` must contain zeroed
+/// memory.
+///
+/// # Failures
+///
+/// Returns `Err` on JSAPI failure.
+///
+/// # Panics
+///
+/// Panics if the last entry of `methods` does not contain zeroed memory.
+///
+/// # Safety
+///
+/// - `cx` must be valid.
+/// - This function calls into unaudited C++ code.
+pub unsafe fn define_methods(
+ cx: *mut JSContext,
+ obj: JS::HandleObject,
+ methods: &'static [JSFunctionSpec],
+) -> Result<(), ()> {
+ assert!({
+ match methods.last() {
+ Some(&JSFunctionSpec {
+ name: JSFunctionSpec_Name { string_: name },
+ call,
+ nargs,
+ flags,
+ selfHostedName,
+ }) => {
+ name.is_null()
+ && call.is_zeroed()
+ && nargs == 0
+ && flags == 0
+ && selfHostedName.is_null()
+ }
+ None => false,
+ }
+ });
+
+ JS_DefineFunctions(cx, obj, methods.as_ptr()).to_result()
+}
+
+/// Defines attributes on `obj`. The last entry of `properties` must contain
+/// zeroed memory.
+///
+/// # Failures
+///
+/// Returns `Err` on JSAPI failure.
+///
+/// # Panics
+///
+/// Panics if the last entry of `properties` does not contain zeroed memory.
+///
+/// # Safety
+///
+/// - `cx` must be valid.
+/// - This function calls into unaudited C++ code.
+pub unsafe fn define_properties(
+ cx: *mut JSContext,
+ obj: JS::HandleObject,
+ properties: &'static [JSPropertySpec],
+) -> Result<(), ()> {
+ assert!(!properties.is_empty());
+ assert!({
+ let spec = properties.last().unwrap();
+ let slice = slice::from_raw_parts(
+ spec as *const _ as *const u8,
+ mem::size_of::<JSPropertySpec>(),
+ );
+ slice.iter().all(|byte| *byte == 0)
+ });
+
+ JS_DefineProperties(cx, obj, 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,
+ hasInstance: None,
+ construct: None,
+ trace: Some(JS_GlobalObjectTraceHook),
+};
+
+/// This is a simple `JSClass` for global objects, primarily intended for tests.
+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)) as u32,
+ cOps: &SIMPLE_GLOBAL_CLASS_OPS as *const JSClassOps,
+ spec: 0 as *mut _,
+ ext: 0 as *mut _,
+ oOps: 0 as *mut _,
+};
+
+#[inline]
+unsafe fn get_object_group(obj: *mut JSObject) -> *mut JS::shadow::ObjectGroup {
+ assert!(!obj.is_null());
+ let obj = obj as *mut JS::shadow::Object;
+ (*obj).group
+}
+
+#[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_compartment(obj: *mut JSObject) -> *mut JS::Compartment {
+ let realm = (*get_object_group(obj)).realm as *const JS::shadow::Realm;
+ (*realm).compartment_
+}
+
+#[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_global(obj: *mut JSObject) -> bool {
+ (*get_object_class(obj)).flags & JSCLASS_IS_GLOBAL != 0
+}
+
+#[inline]
+pub unsafe fn is_window(obj: *mut JSObject) -> bool {
+ is_global(obj) && js::detail::IsWindowSlow(obj)
+}
+
+#[inline]
+pub unsafe fn try_to_outerize(rval: JS::MutableHandleValue) {
+ let obj = rval.to_object();
+ if is_window(obj) {
+ let obj = js::detail::ToWindowProxyIfWindowSlow(obj);
+ assert!(!obj.is_null());
+ rval.set(jsval::ObjectValue(&mut *obj));
+ }
+}
+
+#[inline]
+pub unsafe fn maybe_wrap_object_value(cx: *mut JSContext, rval: JS::MutableHandleValue) {
+ assert!(rval.is_object());
+
+ // There used to be inline checks if this out of line call was necessary or
+ // not here, but JSAPI no longer exposes a way to get a JSContext's
+ // compartment, and additionally JSContext is under a bunch of churn in
+ // JSAPI in general right now.
+
+ assert!(JS_WrapValue(cx, rval));
+}
+
+#[inline]
+pub unsafe fn maybe_wrap_object_or_null_value(cx: *mut JSContext, rval: JS::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: JS::MutableHandleValue) {
+ if rval.is_string() {
+ assert!(JS_WrapValue(cx, rval));
+ } else if rval.is_object() {
+ 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: JSFunctionSpec_Name { string_: 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: JSFunctionSpec_Name { string_: name },
+ call: JSNativeWrapper {
+ op: func,
+ info: ptr::null(),
+ },
+ nargs: nargs,
+ flags: flags,
+ selfHostedName: 0 as *const _,
+ }
+ }
+
+ pub const NULL: JSFunctionSpec = JSFunctionSpec {
+ name: JSFunctionSpec_Name {
+ string_: 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: JSPropertySpec_Name { string_: name },
+ flags_: flags,
+ u: JSPropertySpec_AccessorsOrValue {
+ accessors: JSPropertySpec_AccessorsOrValue_Accessors {
+ getter: JSPropertySpec_Accessor {
+ native: JSNativeWrapper {
+ op: func,
+ info: ptr::null(),
+ },
+ },
+ setter: JSPropertySpec_Accessor {
+ 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: JSPropertySpec_Name { string_: name },
+ flags_: flags,
+ u: JSPropertySpec_AccessorsOrValue {
+ accessors: JSPropertySpec_AccessorsOrValue_Accessors {
+ getter: JSPropertySpec_Accessor {
+ native: JSNativeWrapper {
+ op: g_f,
+ info: ptr::null(),
+ },
+ },
+ setter: JSPropertySpec_Accessor {
+ native: JSNativeWrapper {
+ op: s_f,
+ info: ptr::null(),
+ },
+ },
+ },
+ },
+ }
+ }
+
+ pub const NULL: JSPropertySpec = JSPropertySpec {
+ name: JSPropertySpec_Name {
+ string_: 0 as *const _,
+ },
+ flags_: 0,
+ u: JSPropertySpec_AccessorsOrValue {
+ accessors: JSPropertySpec_AccessorsOrValue_Accessors {
+ getter: JSPropertySpec_Accessor {
+ native: JSNativeWrapper {
+ op: None,
+ info: 0 as *const _,
+ },
+ },
+ setter: JSPropertySpec_Accessor {
+ native: JSNativeWrapper {
+ op: None,
+ info: 0 as *const _,
+ },
+ },
+ },
+ },
+ };
+}
diff --git a/js/rust/src/sc.rs b/js/rust/src/sc.rs
new file mode 100644
index 0000000000..590c87b421
--- /dev/null
+++ b/js/rust/src/sc.rs
@@ -0,0 +1,102 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! Nicer, Rust-y APIs for structured cloning.
+
+use glue;
+use jsapi;
+use rust::Runtime;
+use std::ptr;
+
+/// An RAII owned buffer for structured cloning into and out of.
+pub struct StructuredCloneBuffer {
+ raw: *mut jsapi::JSAutoStructuredCloneBuffer,
+}
+
+impl StructuredCloneBuffer {
+ /// Construct a new `StructuredCloneBuffer`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if the underlying JSAPI calls fail.
+ pub fn new(
+ scope: jsapi::JS::StructuredCloneScope,
+ callbacks: &jsapi::JSStructuredCloneCallbacks,
+ ) -> StructuredCloneBuffer {
+ let raw = unsafe { glue::NewJSAutoStructuredCloneBuffer(scope, callbacks) };
+ assert!(!raw.is_null());
+ StructuredCloneBuffer { raw: raw }
+ }
+
+ /// Get the raw `*mut JSStructuredCloneData` owned by this buffer.
+ pub fn data(&self) -> *mut jsapi::JSStructuredCloneData {
+ unsafe { &mut (*self.raw).data_ }
+ }
+
+ /// Copy this buffer's data into a vec.
+ pub fn copy_to_vec(&self) -> Vec<u8> {
+ let len = unsafe { glue::GetLengthOfJSStructuredCloneData(self.data()) };
+ let mut vec = Vec::with_capacity(len);
+ unsafe {
+ glue::CopyJSStructuredCloneData(self.data(), vec.as_mut_ptr());
+ }
+ vec
+ }
+
+ /// Read a JS value out of this buffer.
+ pub fn read(
+ &mut self,
+ vp: jsapi::JS::MutableHandleValue,
+ callbacks: &jsapi::JSStructuredCloneCallbacks,
+ ) -> Result<(), ()> {
+ if unsafe {
+ (*self.raw).read(
+ Runtime::get(),
+ vp,
+ &jsapi::JS::CloneDataPolicy {
+ allowIntraClusterClonableSharedObjects_: false,
+ allowSharedMemoryObjects_: false,
+ },
+ callbacks,
+ ptr::null_mut(),
+ )
+ } {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ /// Write a JS value into this buffer.
+ pub fn write(
+ &mut self,
+ v: jsapi::JS::HandleValue,
+ callbacks: &jsapi::JSStructuredCloneCallbacks,
+ ) -> Result<(), ()> {
+ if unsafe { (*self.raw).write(Runtime::get(), v, callbacks, ptr::null_mut()) } {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+
+ /// Copy the given slice into this buffer.
+ pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ()> {
+ let len = bytes.len();
+ let src = bytes.as_ptr();
+ if unsafe { glue::WriteBytesToJSStructuredCloneData(src, len, self.data()) } {
+ Ok(())
+ } else {
+ Err(())
+ }
+ }
+}
+
+impl Drop for StructuredCloneBuffer {
+ fn drop(&mut self) {
+ unsafe {
+ glue::DeleteJSAutoStructuredCloneBuffer(self.raw);
+ }
+ }
+}
diff --git a/js/rust/src/typedarray.rs b/js/rust/src/typedarray.rs
new file mode 100644
index 0000000000..aa3d535ede
--- /dev/null
+++ b/js/rust/src/typedarray.rs
@@ -0,0 +1,325 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+//! High-level, safe bindings for JS typed array APIs. Allows creating new
+//! typed arrays or wrapping existing JS reflectors, and prevents reinterpreting
+//! existing buffers as different types except in well-defined cases.
+
+use glue::GetFloat32ArrayLengthAndData;
+use glue::GetFloat64ArrayLengthAndData;
+use glue::GetInt16ArrayLengthAndData;
+use glue::GetInt32ArrayLengthAndData;
+use glue::GetInt8ArrayLengthAndData;
+use glue::GetUint16ArrayLengthAndData;
+use glue::GetUint32ArrayLengthAndData;
+use glue::GetUint8ArrayLengthAndData;
+use glue::GetUint8ClampedArrayLengthAndData;
+use jsapi::js::*;
+use jsapi::JS::*;
+use jsapi::*;
+use rust::RootedGuard;
+use std::ptr;
+use std::slice;
+
+pub enum CreateWith<'a, T: 'a> {
+ Length(u32),
+ Slice(&'a [T]),
+}
+
+/// A rooted typed array.
+pub struct TypedArray<'a, T: 'a + TypedArrayElement> {
+ object: RootedGuard<'a, *mut JSObject>,
+ computed: Option<(*mut T::Element, u32)>,
+}
+
+impl<'a, T: TypedArrayElement> TypedArray<'a, T> {
+ /// Create a typed array representation that wraps an existing JS reflector.
+ /// This operation will fail if attempted on a JS object that does not match
+ /// the expected typed array details.
+ pub fn from(
+ cx: *mut JSContext,
+ root: &'a mut Rooted<*mut JSObject>,
+ object: *mut JSObject,
+ ) -> Result<Self, ()> {
+ if object.is_null() {
+ return Err(());
+ }
+ unsafe {
+ let mut guard = RootedGuard::new(cx, root, object);
+ let unwrapped = T::unwrap_array(*guard);
+ if unwrapped.is_null() {
+ return Err(());
+ }
+
+ *guard = unwrapped;
+ Ok(TypedArray {
+ object: guard,
+ computed: None,
+ })
+ }
+ }
+
+ fn data(&mut self) -> (*mut T::Element, u32) {
+ if let Some(data) = self.computed {
+ return data;
+ }
+
+ let data = unsafe { T::length_and_data(*self.object) };
+ self.computed = Some(data);
+ data
+ }
+
+ /// # Unsafety
+ ///
+ /// The returned slice can be invalidated if the underlying typed array
+ /// is neutered.
+ pub unsafe fn as_slice(&mut self) -> &[T::Element] {
+ let (pointer, length) = self.data();
+ slice::from_raw_parts(pointer as *const T::Element, length as usize)
+ }
+
+ /// # Unsafety
+ ///
+ /// The returned slice can be invalidated if the underlying typed array
+ /// is neutered.
+ ///
+ /// The underlying `JSObject` can be aliased, which can lead to
+ /// Undefined Behavior due to mutable aliasing.
+ pub unsafe fn as_mut_slice(&mut self) -> &mut [T::Element] {
+ let (pointer, length) = self.data();
+ slice::from_raw_parts_mut(pointer, length as usize)
+ }
+}
+
+impl<'a, T: TypedArrayElementCreator + TypedArrayElement> TypedArray<'a, T> {
+ /// Create a new JS typed array, optionally providing initial data that will
+ /// be copied into the newly-allocated buffer. Returns the new JS reflector.
+ pub unsafe fn create(
+ cx: *mut JSContext,
+ with: CreateWith<T::Element>,
+ result: MutableHandleObject,
+ ) -> Result<(), ()> {
+ let length = match with {
+ CreateWith::Length(len) => len,
+ CreateWith::Slice(slice) => slice.len() as u32,
+ };
+
+ result.set(T::create_new(cx, length));
+ if result.get().is_null() {
+ return Err(());
+ }
+
+ if let CreateWith::Slice(data) = with {
+ TypedArray::<T>::update_raw(data, result.handle());
+ }
+
+ Ok(())
+ }
+
+ /// Update an existed JS typed array
+ pub unsafe fn update(&mut self, data: &[T::Element]) {
+ TypedArray::<T>::update_raw(data, self.object.handle());
+ }
+
+ unsafe fn update_raw(data: &[T::Element], result: HandleObject) {
+ let (buf, length) = T::length_and_data(result.get());
+ assert!(data.len() <= length as usize);
+ ptr::copy_nonoverlapping(data.as_ptr(), buf, data.len());
+ }
+}
+
+/// Internal trait used to associate an element type with an underlying representation
+/// and various functions required to manipulate typed arrays of that element type.
+pub trait TypedArrayElement {
+ /// Underlying primitive representation of this element type.
+ type Element;
+ /// Unwrap a typed array JS reflector for this element type.
+ unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject;
+ /// Retrieve the length and data of a typed array's buffer for this element type.
+ unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, u32);
+}
+
+/// Internal trait for creating new typed arrays.
+pub trait TypedArrayElementCreator: TypedArrayElement {
+ /// Create a new typed array.
+ unsafe fn create_new(cx: *mut JSContext, length: u32) -> *mut JSObject;
+ /// Get the data.
+ unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element;
+}
+
+macro_rules! typed_array_element {
+ ($t: ident,
+ $element: ty,
+ $unwrap: ident,
+ $length_and_data: ident) => {
+ /// A kind of typed array.
+ pub struct $t;
+
+ impl TypedArrayElement for $t {
+ type Element = $element;
+ unsafe fn unwrap_array(obj: *mut JSObject) -> *mut JSObject {
+ $unwrap(obj)
+ }
+
+ unsafe fn length_and_data(obj: *mut JSObject) -> (*mut Self::Element, u32) {
+ let mut len = 0;
+ let mut shared = false;
+ let mut data = ptr::null_mut();
+ $length_and_data(obj, &mut len, &mut shared, &mut data);
+ assert!(!shared);
+ (data, len)
+ }
+ }
+ };
+
+ ($t: ident,
+ $element: ty,
+ $unwrap: ident,
+ $length_and_data: ident,
+ $create_new: ident,
+ $get_data: ident) => {
+ typed_array_element!($t, $element, $unwrap, $length_and_data);
+
+ impl TypedArrayElementCreator for $t {
+ unsafe fn create_new(cx: *mut JSContext, length: u32) -> *mut JSObject {
+ $create_new(cx, length)
+ }
+
+ unsafe fn get_data(obj: *mut JSObject) -> *mut Self::Element {
+ let mut shared = false;
+ let data = $get_data(obj, &mut shared, ptr::null_mut());
+ assert!(!shared);
+ data
+ }
+ }
+ };
+}
+
+typed_array_element!(
+ Uint8,
+ u8,
+ UnwrapUint8Array,
+ GetUint8ArrayLengthAndData,
+ JS_NewUint8Array,
+ JS_GetUint8ArrayData
+);
+typed_array_element!(
+ Uint16,
+ u16,
+ UnwrapUint16Array,
+ GetUint16ArrayLengthAndData,
+ JS_NewUint16Array,
+ JS_GetUint16ArrayData
+);
+typed_array_element!(
+ Uint32,
+ u32,
+ UnwrapUint32Array,
+ GetUint32ArrayLengthAndData,
+ JS_NewUint32Array,
+ JS_GetUint32ArrayData
+);
+typed_array_element!(
+ Int8,
+ i8,
+ UnwrapInt8Array,
+ GetInt8ArrayLengthAndData,
+ JS_NewInt8Array,
+ JS_GetInt8ArrayData
+);
+typed_array_element!(
+ Int16,
+ i16,
+ UnwrapInt16Array,
+ GetInt16ArrayLengthAndData,
+ JS_NewInt16Array,
+ JS_GetInt16ArrayData
+);
+typed_array_element!(
+ Int32,
+ i32,
+ UnwrapInt32Array,
+ GetInt32ArrayLengthAndData,
+ JS_NewInt32Array,
+ JS_GetInt32ArrayData
+);
+typed_array_element!(
+ Float32,
+ f32,
+ UnwrapFloat32Array,
+ GetFloat32ArrayLengthAndData,
+ JS_NewFloat32Array,
+ JS_GetFloat32ArrayData
+);
+typed_array_element!(
+ Float64,
+ f64,
+ UnwrapFloat64Array,
+ GetFloat64ArrayLengthAndData,
+ JS_NewFloat64Array,
+ JS_GetFloat64ArrayData
+);
+typed_array_element!(
+ ClampedU8,
+ u8,
+ UnwrapUint8ClampedArray,
+ GetUint8ClampedArrayLengthAndData,
+ JS_NewUint8ClampedArray,
+ JS_GetUint8ClampedArrayData
+);
+typed_array_element!(
+ ArrayBufferU8,
+ u8,
+ UnwrapArrayBuffer,
+ GetArrayBufferLengthAndData,
+ NewArrayBuffer,
+ GetArrayBufferData
+);
+typed_array_element!(
+ ArrayBufferViewU8,
+ u8,
+ UnwrapArrayBufferView,
+ GetArrayBufferViewLengthAndData
+);
+
+/// The Uint8ClampedArray type.
+pub type Uint8ClampedArray<'a> = TypedArray<'a, ClampedU8>;
+/// The Uint8Array type.
+pub type Uint8Array<'a> = TypedArray<'a, Uint8>;
+/// The Int8Array type.
+pub type Int8Array<'a> = TypedArray<'a, Int8>;
+/// The Uint16Array type.
+pub type Uint16Array<'a> = TypedArray<'a, Uint16>;
+/// The Int16Array type.
+pub type Int16Array<'a> = TypedArray<'a, Int16>;
+/// The Uint32Array type.
+pub type Uint32Array<'a> = TypedArray<'a, Uint32>;
+/// The Int32Array type.
+pub type Int32Array<'a> = TypedArray<'a, Int32>;
+/// The Float32Array type.
+pub type Float32Array<'a> = TypedArray<'a, Float32>;
+/// The Float64Array type.
+pub type Float64Array<'a> = TypedArray<'a, Float64>;
+/// The ArrayBuffer type.
+pub type ArrayBuffer<'a> = TypedArray<'a, ArrayBufferU8>;
+/// The ArrayBufferView type
+pub type ArrayBufferView<'a> = TypedArray<'a, ArrayBufferViewU8>;
+
+impl<'a> ArrayBufferView<'a> {
+ pub fn get_array_type(&self) -> Scalar::Type {
+ unsafe { JS_GetArrayBufferViewType(self.object.get()) }
+ }
+}
+
+#[macro_export]
+macro_rules! typedarray {
+ (in($cx:expr) let $name:ident : $ty:ident = $init:expr) => {
+ let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+ let $name = $crate::typedarray::$ty::from($cx, &mut __root, $init);
+ };
+ (in($cx:expr) let mut $name:ident : $ty:ident = $init:expr) => {
+ let mut __root = $crate::jsapi::JS::Rooted::new_unrooted();
+ let mut $name = $crate::typedarray::$ty::from($cx, &mut __root, $init);
+ };
+}