Browse Source

Implement `TryFromJs` for `JsObject` wrappers (#2809)

This change allows using built-in `JsObject` wrappers with the `TryFromJs` logic, which makes it easier to derive it in complex objects containing known built-ins, being able to use all the power of the wrappers even inside complex structures.

It changes the following:

 * Implements `TryFromJs` for all `JsObject` wrappers ~, except for `JsProxy` and `Js<Int>Array` wrappers~ -> This has now been added.
 *  Adds checker functions for individual typed array objects.
 * Adds some missing checker functions from `Object` to `JsObject`

~The reason for not implementing it for `JsProxy` is that we don't have a way (as far as I can tell) to know if a `JsObject` is a `Proxy` object. The reason for the typed arrays (note that `JsTypedArray` implements `TryFromJs`) is that I didn't find an easy way to know which type of typed array a `JsObject` was. Do we have a way of using a `JsObject` to create a Rust `JsUint8Array` or the like?~
pull/2812/head
Iban Eguia Moraza 1 year ago
parent
commit
a8578f7cef
  1. 15
      boa_engine/src/object/builtins/jsarray.rs
  2. 14
      boa_engine/src/object/builtins/jsarraybuffer.rs
  3. 14
      boa_engine/src/object/builtins/jsdataview.rs
  4. 24
      boa_engine/src/object/builtins/jsdate.rs
  5. 18
      boa_engine/src/object/builtins/jsfunction.rs
  6. 14
      boa_engine/src/object/builtins/jsgenerator.rs
  7. 14
      boa_engine/src/object/builtins/jsmap.rs
  8. 14
      boa_engine/src/object/builtins/jsmap_iterator.rs
  9. 16
      boa_engine/src/object/builtins/jspromise.rs
  10. 27
      boa_engine/src/object/builtins/jsproxy.rs
  11. 14
      boa_engine/src/object/builtins/jsregexp.rs
  12. 14
      boa_engine/src/object/builtins/jsset.rs
  13. 14
      boa_engine/src/object/builtins/jsset_iterator.rs
  14. 136
      boa_engine/src/object/builtins/jstypedarray.rs
  15. 121
      boa_engine/src/object/jsobject.rs
  16. 114
      boa_engine/src/object/mod.rs
  17. 14
      boa_engine/src/object/operations.rs

15
boa_engine/src/object/builtins/jsarray.rs

@ -3,7 +3,7 @@ use crate::{
builtins::Array, builtins::Array,
error::JsNativeError, error::JsNativeError,
object::{JsFunction, JsObject, JsObjectType}, object::{JsFunction, JsObject, JsObjectType},
value::IntoOrUndefined, value::{IntoOrUndefined, TryFromJs},
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -40,7 +40,7 @@ impl JsArray {
/// This does not clone the fields of the array, it only does a shallow clone of the object. /// This does not clone the fields of the array, it only does a shallow clone of the object.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_array() { if object.is_array() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -399,3 +399,14 @@ impl Deref for JsArray {
} }
impl JsObjectType for JsArray {} impl JsObjectType for JsArray {}
impl TryFromJs for JsArray {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not an Array object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsarraybuffer.rs

@ -6,6 +6,7 @@ use crate::{
object::{ object::{
internal_methods::get_prototype_from_constructor, JsObject, JsObjectType, ObjectData, internal_methods::get_prototype_from_constructor, JsObject, JsObjectType, ObjectData,
}, },
value::TryFromJs,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
@ -117,7 +118,7 @@ impl JsArrayBuffer {
/// This does not clone the fields of the array buffer, it only does a shallow clone of the object. /// This does not clone the fields of the array buffer, it only does a shallow clone of the object.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_array_buffer() { if object.is_array_buffer() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -222,3 +223,14 @@ impl Deref for JsArrayBuffer {
} }
impl JsObjectType for JsArrayBuffer {} impl JsObjectType for JsArrayBuffer {}
impl TryFromJs for JsArrayBuffer {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not an ArrayBuffer object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsdataview.rs

@ -6,6 +6,7 @@ use crate::{
internal_methods::get_prototype_from_constructor, JsArrayBuffer, JsObject, JsObjectType, internal_methods::get_prototype_from_constructor, JsArrayBuffer, JsObject, JsObjectType,
ObjectData, ObjectData,
}, },
value::TryFromJs,
Context, JsNativeError, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
}; };
@ -111,7 +112,7 @@ impl JsDataView {
/// Create a new `JsDataView` object from an existing object. /// Create a new `JsDataView` object from an existing object.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_data_view() { if object.is_data_view() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -485,3 +486,14 @@ impl Deref for JsDataView {
} }
impl JsObjectType for JsDataView {} impl JsObjectType for JsDataView {}
impl TryFromJs for JsDataView {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not an DataView object")
.into()),
}
}
}

24
boa_engine/src/object/builtins/jsdate.rs

@ -7,6 +7,7 @@ use chrono::DateTime;
use crate::{ use crate::{
builtins::Date, builtins::Date,
object::{JsObject, JsObjectType, ObjectData}, object::{JsObject, JsObjectType, ObjectData},
value::TryFromJs,
Context, JsNativeError, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
}; };
@ -47,6 +48,18 @@ impl JsDate {
Self { inner } Self { inner }
} }
/// Create a new `JsDate` object from an existing object.
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.is_date() {
Ok(Self { inner: object })
} else {
Err(JsNativeError::typ()
.with_message("object is not a Date")
.into())
}
}
/// Return a `Number` representing the milliseconds elapsed since the UNIX epoch. /// Return a `Number` representing the milliseconds elapsed since the UNIX epoch.
/// ///
/// Same as JavaScript's `Date.now()` /// Same as JavaScript's `Date.now()`
@ -587,3 +600,14 @@ impl Deref for JsDate {
} }
impl JsObjectType for JsDate {} impl JsObjectType for JsDate {}
impl TryFromJs for JsDate {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Date object")
.into()),
}
}
}

18
boa_engine/src/object/builtins/jsfunction.rs

@ -1,7 +1,8 @@
//! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object //! A Rust API wrapper for Boa's `Function` Builtin ECMAScript Object
use crate::{ use crate::{
object::{JsObject, JsObjectType}, object::{JsObject, JsObjectType},
JsValue, value::TryFromJs,
Context, JsNativeError, JsResult, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use std::ops::Deref; use std::ops::Deref;
@ -52,3 +53,18 @@ impl Deref for JsFunction {
} }
impl JsObjectType for JsFunction {} impl JsObjectType for JsFunction {}
impl TryFromJs for JsFunction {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()).ok_or_else(|| {
JsNativeError::typ()
.with_message("object is not a function")
.into()
}),
_ => Err(JsNativeError::typ()
.with_message("value is not a Function object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsgenerator.rs

@ -2,6 +2,7 @@
use crate::{ use crate::{
builtins::generator::{Generator, GeneratorState}, builtins::generator::{Generator, GeneratorState},
object::{JsObject, JsObjectType, ObjectData}, object::{JsObject, JsObjectType, ObjectData},
value::TryFromJs,
Context, JsNativeError, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
}; };
@ -34,7 +35,7 @@ impl JsGenerator {
/// Create a `JsGenerator` from a regular expression `JsObject` /// Create a `JsGenerator` from a regular expression `JsObject`
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_generator() { if object.is_generator() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -99,3 +100,14 @@ impl Deref for JsGenerator {
} }
impl JsObjectType for JsGenerator {} impl JsObjectType for JsGenerator {}
impl TryFromJs for JsGenerator {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Generator object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsmap.rs

@ -5,6 +5,7 @@ use crate::{
error::JsNativeError, error::JsNativeError,
object::{JsFunction, JsMapIterator, JsObject, JsObjectType, ObjectData}, object::{JsFunction, JsMapIterator, JsObject, JsObjectType, ObjectData},
string::utf16, string::utf16,
value::TryFromJs,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -169,7 +170,7 @@ impl JsMap {
/// ``` /// ```
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_map() { if object.is_map() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -422,3 +423,14 @@ impl Deref for JsMap {
} }
impl JsObjectType for JsMap {} impl JsObjectType for JsMap {}
impl TryFromJs for JsMap {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Map object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsmap_iterator.rs

@ -3,6 +3,7 @@ use crate::{
builtins::map::MapIterator, builtins::map::MapIterator,
error::JsNativeError, error::JsNativeError,
object::{JsObject, JsObjectType}, object::{JsObject, JsObjectType},
value::TryFromJs,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -19,7 +20,7 @@ impl JsMapIterator {
/// Create a [`JsMapIterator`] from a [`JsObject`]. If object is not a `MapIterator`, throw `TypeError` /// Create a [`JsMapIterator`] from a [`JsObject`]. If object is not a `MapIterator`, throw `TypeError`
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_map_iterator() { if object.is_map_iterator() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -58,3 +59,14 @@ impl Deref for JsMapIterator {
} }
impl JsObjectType for JsMapIterator {} impl JsObjectType for JsMapIterator {}
impl TryFromJs for JsMapIterator {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a MapIterator object")
.into()),
}
}
}

16
boa_engine/src/object/builtins/jspromise.rs

@ -11,6 +11,7 @@ use crate::{
}, },
context::intrinsics::StandardConstructors, context::intrinsics::StandardConstructors,
object::{JsObject, JsObjectType, ObjectData}, object::{JsObject, JsObjectType, ObjectData},
value::TryFromJs,
Context, JsError, JsNativeError, JsResult, JsValue, Context, JsError, JsNativeError, JsResult, JsValue,
}; };
@ -243,13 +244,13 @@ impl JsPromise {
/// # } /// # }
/// ``` /// ```
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<JsPromise> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if !object.is_promise() { if !object.is_promise() {
return Err(JsNativeError::typ() return Err(JsNativeError::typ()
.with_message("`object` is not a Promise") .with_message("`object` is not a Promise")
.into()); .into());
} }
Ok(JsPromise { inner: object }) Ok(Self { inner: object })
} }
/// Resolves a `JsValue` into a `JsPromise`. /// Resolves a `JsValue` into a `JsPromise`.
@ -883,3 +884,14 @@ impl std::ops::Deref for JsPromise {
} }
impl JsObjectType for JsPromise {} impl JsObjectType for JsPromise {}
impl TryFromJs for JsPromise {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Promise object")
.into()),
}
}
}

27
boa_engine/src/object/builtins/jsproxy.rs

@ -6,7 +6,8 @@ use crate::{
native_function::{NativeFunction, NativeFunctionPointer}, native_function::{NativeFunction, NativeFunctionPointer},
object::{FunctionObjectBuilder, JsObject, JsObjectType, ObjectData}, object::{FunctionObjectBuilder, JsObject, JsObjectType, ObjectData},
string::utf16, string::utf16,
Context, JsResult, JsValue, value::TryFromJs,
Context, JsNativeError, JsResult, JsValue,
}; };
use super::JsFunction; use super::JsFunction;
@ -33,6 +34,19 @@ impl JsProxy {
pub fn builder(target: JsObject) -> JsProxyBuilder { pub fn builder(target: JsObject) -> JsProxyBuilder {
JsProxyBuilder::new(target) JsProxyBuilder::new(target)
} }
/// Create a [`JsProxy`] from a [`JsObject`], if the object is not a `Proxy` throw a
/// `TypeError`.
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_proxy() {
Ok(Self { inner: object })
} else {
Err(JsNativeError::typ()
.with_message("object is not a Proxy")
.into())
}
}
} }
impl From<JsProxy> for JsObject { impl From<JsProxy> for JsObject {
@ -60,6 +74,17 @@ impl std::ops::Deref for JsProxy {
impl JsObjectType for JsProxy {} impl JsObjectType for JsProxy {}
impl TryFromJs for JsProxy {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Proxy object")
.into()),
}
}
}
/// `JsRevocableProxy` provides a wrapper for `JsProxy` that can be disabled. /// `JsRevocableProxy` provides a wrapper for `JsProxy` that can be disabled.
/// ///
/// Safe interface for the [`Proxy.revocable`][revocable] method that creates a /// Safe interface for the [`Proxy.revocable`][revocable] method that creates a

14
boa_engine/src/object/builtins/jsregexp.rs

@ -2,6 +2,7 @@
use crate::{ use crate::{
builtins::RegExp, builtins::RegExp,
object::{JsArray, JsObject, JsObjectType}, object::{JsArray, JsObject, JsObjectType},
value::TryFromJs,
Context, JsNativeError, JsResult, JsValue, Context, JsNativeError, JsResult, JsValue,
}; };
@ -79,7 +80,7 @@ impl JsRegExp {
/// Create a `JsRegExp` from a regular expression `JsObject` /// Create a `JsRegExp` from a regular expression `JsObject`
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_regexp() { if object.is_regexp() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -279,3 +280,14 @@ impl Deref for JsRegExp {
} }
impl JsObjectType for JsRegExp {} impl JsObjectType for JsRegExp {}
impl TryFromJs for JsRegExp {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a RegExp object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsset.rs

@ -7,6 +7,7 @@ use crate::{
builtins::Set, builtins::Set,
error::JsNativeError, error::JsNativeError,
object::{JsFunction, JsObject, JsObjectType, JsSetIterator}, object::{JsFunction, JsObject, JsObjectType, JsSetIterator},
value::TryFromJs,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -143,7 +144,7 @@ impl JsSet {
/// Utility: Creates `JsSet` from `JsObject`, if not a Set throw `TypeError`. /// Utility: Creates `JsSet` from `JsObject`, if not a Set throw `TypeError`.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_set() { if object.is_set() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -185,3 +186,14 @@ impl Deref for JsSet {
} }
impl JsObjectType for JsSet {} impl JsObjectType for JsSet {}
impl TryFromJs for JsSet {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a Set object")
.into()),
}
}
}

14
boa_engine/src/object/builtins/jsset_iterator.rs

@ -7,6 +7,7 @@ use crate::{
builtins::set::SetIterator, builtins::set::SetIterator,
error::JsNativeError, error::JsNativeError,
object::{JsObject, JsObjectType}, object::{JsObject, JsObjectType},
value::TryFromJs,
Context, JsResult, JsValue, Context, JsResult, JsValue,
}; };
@ -20,7 +21,7 @@ impl JsSetIterator {
/// Create a `JsSetIterator` from a `JsObject`. /// Create a `JsSetIterator` from a `JsObject`.
/// If object is not a `SetIterator`, throw `TypeError`. /// If object is not a `SetIterator`, throw `TypeError`.
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_set_iterator() { if object.is_set_iterator() {
Ok(Self { inner: object }) Ok(Self { inner: object })
} else { } else {
Err(JsNativeError::typ() Err(JsNativeError::typ()
@ -58,3 +59,14 @@ impl Deref for JsSetIterator {
} }
impl JsObjectType for JsSetIterator {} impl JsObjectType for JsSetIterator {}
impl TryFromJs for JsSetIterator {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a SetIterator object")
.into()),
}
}
}

136
boa_engine/src/object/builtins/jstypedarray.rs

@ -4,25 +4,28 @@ use crate::{
builtins::BuiltInConstructor, builtins::BuiltInConstructor,
error::JsNativeError, error::JsNativeError,
object::{JsArrayBuffer, JsFunction, JsObject, JsObjectType}, object::{JsArrayBuffer, JsFunction, JsObject, JsObjectType},
value::IntoOrUndefined, value::{IntoOrUndefined, TryFromJs},
Context, JsResult, JsString, JsValue, Context, JsResult, JsString, JsValue,
}; };
use boa_gc::{Finalize, Trace}; use boa_gc::{Finalize, Trace};
use std::ops::Deref; use std::ops::Deref;
/// `JsTypedArray` provides a wrapper for Boa's implementation of the ECMAScript `TypedArray` builtin object. /// `JsTypedArray` provides a wrapper for Boa's implementation of the ECMAScript `TypedArray`
/// builtin object.
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
pub struct JsTypedArray { pub struct JsTypedArray {
inner: JsValue, inner: JsValue,
} }
impl JsTypedArray { impl JsTypedArray {
/// Create a [`JsTypedArray`] from a [`JsObject`], if the object is not a typed array throw a `TypeError`. /// Create a [`JsTypedArray`] from a [`JsObject`], if the object is not a typed array throw a
/// `TypeError`.
/// ///
/// This does not clone the fields of the typed array, it only does a shallow clone of the object. /// This does not clone the fields of the typed array, it only does a shallow clone of the
/// object.
#[inline] #[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> { pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.borrow().is_typed_array() { if object.is_typed_array() {
Ok(Self { Ok(Self {
inner: object.into(), inner: object.into(),
}) })
@ -346,15 +349,55 @@ impl Deref for JsTypedArray {
impl JsObjectType for JsTypedArray {} impl JsObjectType for JsTypedArray {}
impl TryFromJs for JsTypedArray {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message("value is not a TypedArray object")
.into()),
}
}
}
macro_rules! JsTypedArrayType { macro_rules! JsTypedArrayType {
($name:ident, $constructor_function:ident, $constructor_object:ident, $element:ty) => { (
#[doc = concat!("`", stringify!($name), "` provides a wrapper for Boa's implementation of the ECMAScript `", stringify!($constructor_function) ,"` builtin object.")] $name:ident,
$constructor_function:ident,
$checker_function:ident,
$constructor_object:ident,
$element:ty
) => {
#[doc = concat!(
"`", stringify!($name),
"` provides a wrapper for Boa's implementation of the ECMAScript `",
stringify!($constructor_function) ,"` builtin object."
)]
#[derive(Debug, Clone, Trace, Finalize)] #[derive(Debug, Clone, Trace, Finalize)]
pub struct $name { pub struct $name {
inner: JsTypedArray, inner: JsTypedArray,
} }
impl $name { impl $name {
#[doc = concat!("Creates a `", stringify!($name),
"` using a [`JsObject`]. It will make sure that the object is of the correct kind."
)]
#[inline]
pub fn from_object(object: JsObject) -> JsResult<Self> {
if object.$checker_function() {
Ok(Self {
inner: JsTypedArray {
inner: object.into(),
},
})
} else {
Err(JsNativeError::typ()
.with_message("object is not a TypedArray")
.into())
}
}
/// Create the typed array from a [`JsArrayBuffer`]. /// Create the typed array from a [`JsArrayBuffer`].
pub fn from_array_buffer( pub fn from_array_buffer(
array_buffer: JsArrayBuffer, array_buffer: JsArrayBuffer,
@ -441,14 +484,77 @@ macro_rules! JsTypedArrayType {
&self.inner &self.inner
} }
} }
impl TryFromJs for $name {
fn try_from_js(value: &JsValue, _context: &mut Context<'_>) -> JsResult<Self> {
match value {
JsValue::Object(o) => Self::from_object(o.clone()),
_ => Err(JsNativeError::typ()
.with_message(concat!(
"value is not a ",
stringify!($constructor_function),
" object"
))
.into()),
}
}
}
}; };
} }
JsTypedArrayType!(JsUint8Array, Uint8Array, typed_uint8_array, u8); JsTypedArrayType!(
JsTypedArrayType!(JsInt8Array, Int8Array, typed_int8_array, i8); JsUint8Array,
JsTypedArrayType!(JsUint16Array, Uint16Array, typed_uint16_array, u16); Uint8Array,
JsTypedArrayType!(JsInt16Array, Int16Array, typed_int16_array, i16); is_typed_uint8_array,
JsTypedArrayType!(JsUint32Array, Uint32Array, typed_uint32_array, u32); typed_uint8_array,
JsTypedArrayType!(JsInt32Array, Int32Array, typed_int32_array, i32); u8
JsTypedArrayType!(JsFloat32Array, Float32Array, typed_float32_array, f32); );
JsTypedArrayType!(JsFloat64Array, Float64Array, typed_float64_array, f64); JsTypedArrayType!(
JsInt8Array,
Int8Array,
is_typed_int8_array,
typed_int8_array,
i8
);
JsTypedArrayType!(
JsUint16Array,
Uint16Array,
is_typed_uint16_array,
typed_uint16_array,
u16
);
JsTypedArrayType!(
JsInt16Array,
Int16Array,
is_typed_int16_array,
typed_int16_array,
i16
);
JsTypedArrayType!(
JsUint32Array,
Uint32Array,
is_typed_uint32_array,
typed_uint32_array,
u32
);
JsTypedArrayType!(
JsInt32Array,
Int32Array,
is_typed_int32_array,
typed_int32_array,
i32
);
JsTypedArrayType!(
JsFloat32Array,
Float32Array,
is_typed_float32_array,
typed_float32_array,
f32
);
JsTypedArrayType!(
JsFloat64Array,
Float64Array,
is_typed_float64_array,
typed_float64_array,
f64
);

121
boa_engine/src/object/jsobject.rs

@ -309,6 +309,17 @@ impl JsObject {
self.borrow().is_array() self.borrow().is_array()
} }
/// Checks if it's a `DataView` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_data_view(&self) -> bool {
self.borrow().is_data_view()
}
/// Checks if it is an `ArrayIterator` object. /// Checks if it is an `ArrayIterator` object.
/// ///
/// # Panics /// # Panics
@ -463,6 +474,17 @@ impl JsObject {
self.borrow().is_bigint() self.borrow().is_bigint()
} }
/// Checks if it's a `Date` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_date(&self) -> bool {
self.borrow().is_date()
}
/// Checks if it's a `RegExp` object. /// Checks if it's a `RegExp` object.
/// ///
/// # Panics /// # Panics
@ -485,6 +507,94 @@ impl JsObject {
self.borrow().is_typed_array() self.borrow().is_typed_array()
} }
/// Checks if it's a `Uint8Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_uint8_array(&self) -> bool {
self.borrow().is_typed_uint8_array()
}
/// Checks if it's a `Int8Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_int8_array(&self) -> bool {
self.borrow().is_typed_int8_array()
}
/// Checks if it's a `Uint16Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_uint16_array(&self) -> bool {
self.borrow().is_typed_uint16_array()
}
/// Checks if it's a `Int16Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_int16_array(&self) -> bool {
self.borrow().is_typed_int16_array()
}
/// Checks if it's a `Uint32Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_uint32_array(&self) -> bool {
self.borrow().is_typed_uint32_array()
}
/// Checks if it's a `Int32Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_int32_array(&self) -> bool {
self.borrow().is_typed_int32_array()
}
/// Checks if it's a `Float32Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_float32_array(&self) -> bool {
self.borrow().is_typed_float32_array()
}
/// Checks if it's a `Float64Array` object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_typed_float64_array(&self) -> bool {
self.borrow().is_typed_float64_array()
}
/// Checks if it's a `Promise` object. /// Checks if it's a `Promise` object.
/// ///
/// # Panics /// # Panics
@ -507,6 +617,17 @@ impl JsObject {
self.borrow().is_ordinary() self.borrow().is_ordinary()
} }
/// Checks if it's a proxy object.
///
/// # Panics
///
/// Panics if the object is currently mutably borrowed.
#[inline]
#[track_caller]
pub fn is_proxy(&self) -> bool {
self.borrow().is_proxy()
}
/// Returns `true` if it holds an Rust type that implements `NativeObject`. /// Returns `true` if it holds an Rust type that implements `NativeObject`.
/// ///
/// # Panics /// # Panics

114
boa_engine/src/object/mod.rs

@ -45,7 +45,7 @@ use crate::{
set::ordered_set::OrderedSet, set::ordered_set::OrderedSet,
set::SetIterator, set::SetIterator,
string::StringIterator, string::StringIterator,
typed_array::integer_indexed_object::IntegerIndexed, typed_array::{integer_indexed_object::IntegerIndexed, TypedArrayKind},
DataView, Date, Promise, RegExp, DataView, Date, Promise, RegExp,
}, },
js_string, js_string,
@ -1429,6 +1429,118 @@ impl Object {
) )
} }
/// Checks if it a `Uint8Array` object.
#[inline]
pub const fn is_typed_uint8_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Uint8)
} else {
false
}
}
/// Checks if it a `Int8Array` object.
#[inline]
pub const fn is_typed_int8_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Int8)
} else {
false
}
}
/// Checks if it a `Uint16Array` object.
#[inline]
pub const fn is_typed_uint16_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Uint16)
} else {
false
}
}
/// Checks if it a `Int16Array` object.
#[inline]
pub const fn is_typed_int16_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Int16)
} else {
false
}
}
/// Checks if it a `Uint32Array` object.
#[inline]
pub const fn is_typed_uint32_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Uint32)
} else {
false
}
}
/// Checks if it a `Int32Array` object.
#[inline]
pub const fn is_typed_int32_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Int32)
} else {
false
}
}
/// Checks if it a `Float32Array` object.
#[inline]
pub const fn is_typed_float32_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Float32)
} else {
false
}
}
/// Checks if it a `Float64Array` object.
#[inline]
pub const fn is_typed_float64_array(&self) -> bool {
if let ObjectData {
kind: ObjectKind::IntegerIndexed(ref int),
..
} = self.data
{
matches!(int.typed_array_name(), TypedArrayKind::Float64)
} else {
false
}
}
/// Gets the data view data if the object is a `DataView`. /// Gets the data view data if the object is a `DataView`.
#[inline] #[inline]
pub const fn as_data_view(&self) -> Option<&DataView> { pub const fn as_data_view(&self) -> Option<&DataView> {

14
boa_engine/src/object/operations.rs

@ -300,13 +300,16 @@ impl JsObject {
Ok(desc.is_some()) Ok(desc.is_some())
} }
/// Call this object. /// `Call ( F, V [ , argumentsList ] )`
/// ///
/// # Panics /// # Panics
/// ///
/// Panics if the object is currently mutably borrowed. /// Panics if the object is currently mutably borrowed.
// <https://tc39.es/ecma262/#sec-prepareforordinarycall> ///
// <https://tc39.es/ecma262/#sec-ecmascript-function-objects-call-thisargument-argumentslist> /// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-call
#[track_caller] #[track_caller]
#[inline] #[inline]
pub fn call( pub fn call(
@ -317,8 +320,9 @@ impl JsObject {
) -> JsResult<JsValue> { ) -> JsResult<JsValue> {
// 1. If argumentsList is not present, set argumentsList to a new empty List. // 1. If argumentsList is not present, set argumentsList to a new empty List.
// 2. If IsCallable(F) is false, throw a TypeError exception. // 2. If IsCallable(F) is false, throw a TypeError exception.
let function = JsFunction::from_object(self.clone()) let function = JsFunction::from_object(self.clone()).ok_or_else(|| {
.ok_or_else(|| JsNativeError::typ().with_message("not a function"))?; JsNativeError::typ().with_message("only callable objects / functions can be called")
})?;
// 3. Return ? F.[[Call]](V, argumentsList). // 3. Return ? F.[[Call]](V, argumentsList).
function.__call__(this, args, context) function.__call__(this, args, context)

Loading…
Cancel
Save