From 75a65730c13d2e1e11c466897ddfcd1e13aaf4dd Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 3 Apr 2022 21:50:53 +0200 Subject: [PATCH 1/2] Feature `JsTypedArray` --- boa_engine/src/builtins/typed_array/mod.rs | 140 +++++-- boa_engine/src/object/jstypedarray.rs | 407 +++++++++++++++++++++ boa_engine/src/object/mod.rs | 2 + boa_engine/src/value/conversions.rs | 42 ++- boa_examples/src/bin/jstypedarray.rs | 46 +++ 5 files changed, 604 insertions(+), 33 deletions(-) create mode 100644 boa_engine/src/object/jstypedarray.rs create mode 100644 boa_examples/src/bin/jstypedarray.rs diff --git a/boa_engine/src/builtins/typed_array/mod.rs b/boa_engine/src/builtins/typed_array/mod.rs index d7ea9c48957..edffcfedb0a 100644 --- a/boa_engine/src/builtins/typed_array/mod.rs +++ b/boa_engine/src/builtins/typed_array/mod.rs @@ -114,7 +114,7 @@ macro_rules! typed_array { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-typedarray - fn constructor( + pub(crate) fn constructor( new_target: &JsValue, args: &[JsValue], context: &mut Context, @@ -549,7 +549,7 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.at - fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn at(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -622,7 +622,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.bytelength - fn byte_length(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn byte_length( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. @@ -651,7 +655,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.byteoffset - fn byte_offset(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn byte_offset( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. @@ -877,7 +885,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.every - fn every(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn every( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -936,7 +948,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.fill - fn fill(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn fill( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1016,7 +1032,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.filter - fn filter(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn filter( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1097,7 +1117,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.find - fn find(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn find( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1155,7 +1179,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.findindex - fn findindex(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn findindex( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1212,7 +1240,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach - fn foreach(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn foreach( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1264,7 +1296,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.includes - fn includes(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn includes( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1337,7 +1373,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.indexof - fn index_of(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn index_of( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1419,7 +1459,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.join - fn join(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn join( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1476,7 +1520,7 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.keys - fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn keys(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let o = this @@ -1504,7 +1548,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.lastindexof - fn last_index_of(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn last_index_of( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1578,7 +1626,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.length - fn length(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn length( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has [[ViewedArrayBuffer]] and [[ArrayLength]] internal slots. @@ -1607,7 +1659,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.map - fn map(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn map( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1665,7 +1721,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduce - fn reduce(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn reduce( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1742,7 +1802,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.reduceright - fn reduceright(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn reduceright( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1822,7 +1886,11 @@ impl TypedArray { /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.reverse #[allow(clippy::float_cmp)] - fn reverse(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn reverse( + this: &JsValue, + _: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -1877,7 +1945,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.set - fn set(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn set( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let target be the this value. // 2. Perform ? RequireInternalSlot(target, [[TypedArrayName]]). // 3. Assert: target has a [[ViewedArrayBuffer]] internal slot. @@ -2255,7 +2327,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.slice - fn slice(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn slice( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -2420,7 +2496,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.some - fn some(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn some( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? ValidateTypedArray(O). let obj = this @@ -2478,7 +2558,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.sort - fn sort(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn sort( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. If comparefn is not undefined and IsCallable(comparefn) is false, throw a TypeError exception. let compare_fn = match args.get(0) { None | Some(JsValue::Undefined) => None, @@ -2686,7 +2770,11 @@ impl TypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.subarray - fn subarray(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn subarray( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. @@ -2951,7 +3039,7 @@ impl TypedArray { } /// - fn initialize_from_list( + pub(crate) fn initialize_from_list( o: &JsObject, values: Vec, context: &mut Context, diff --git a/boa_engine/src/object/jstypedarray.rs b/boa_engine/src/object/jstypedarray.rs new file mode 100644 index 00000000000..cfe4cf712db --- /dev/null +++ b/boa_engine/src/object/jstypedarray.rs @@ -0,0 +1,407 @@ +use crate::{ + builtins::typed_array::TypedArray, + object::{JsArray, JsObject, JsObjectType}, + value::IntoOrUndefined, + Context, JsResult, JsString, JsValue, +}; +use boa_gc::{Finalize, Trace}; +use std::ops::Deref; + +/// JavaScript `TypedArray` rust object. +#[derive(Debug, Clone, Trace, Finalize)] +pub struct JsTypedArray { + inner: JsObject, +} + +impl JsTypedArray { + #[inline] + pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { + if object.borrow().is_typed_array() { + Ok(Self { inner: object }) + } else { + context.throw_type_error("object is not an TypedArray") + } + } + + /// Get the length of the array. + /// + /// Same a `array.length` in JavaScript. + #[inline] + pub fn length(&self, context: &mut Context) -> JsResult { + Ok( + TypedArray::length(&self.inner.clone().into(), &[], context)? + .as_number() + .map(|x| x as usize) + .expect("length should return a number"), + ) + } + + /// Check if the array is empty, i.e. the `length` is zero. + #[inline] + pub fn is_empty(&self, context: &mut Context) -> JsResult { + self.inner.length_of_array_like(context).map(|len| len == 0) + } + + #[inline] + pub fn at(&self, index: T, context: &mut Context) -> JsResult + where + T: Into, + { + TypedArray::at(&self.inner.clone().into(), &[index.into().into()], context) + } + + #[inline] + pub fn byte_length(&self, context: &mut Context) -> JsResult { + Ok( + TypedArray::byte_length(&self.inner.clone().into(), &[], context)? + .as_number() + .map(|x| x as usize) + .expect("byteLength should return a number"), + ) + } + + #[inline] + pub fn byte_offset(&self, context: &mut Context) -> JsResult { + Ok( + TypedArray::byte_offset(&self.inner.clone().into(), &[], context)? + .as_number() + .map(|x| x as usize) + .expect("byteLength should return a number"), + ) + } + + #[inline] + pub fn fill( + &self, + value: T, + start: Option, + end: Option, + context: &mut Context, + ) -> JsResult + where + T: Into, + { + TypedArray::fill( + &self.inner.clone().into(), + &[ + value.into(), + start.into_or_undefined(), + end.into_or_undefined(), + ], + context, + )?; + Ok(self.clone()) + } + + pub fn every( + &self, + predicate: JsObject, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + let result = TypedArray::every( + &self.inner.clone().into(), + &[predicate.into(), this_arg.into_or_undefined()], + context, + )? + .as_boolean() + .expect("TypedArray.prototype.every should always return boolean"); + + Ok(result) + } + + #[inline] + pub fn some( + &self, + callback: JsObject, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + let result = TypedArray::some( + &self.inner.clone().into(), + &[callback.into(), this_arg.into_or_undefined()], + context, + )? + .as_boolean() + .expect("TypedArray.prototype.some should always return boolean"); + + Ok(result) + } + + #[inline] + pub fn sort(&self, compare_fn: Option, context: &mut Context) -> JsResult { + TypedArray::sort( + &self.inner.clone().into(), + &[compare_fn.into_or_undefined()], + context, + )?; + + Ok(self.clone()) + } + + #[inline] + pub fn filter( + &self, + callback: JsObject, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + let object = TypedArray::filter( + &self.inner.clone().into(), + &[callback.into(), this_arg.into_or_undefined()], + context, + )? + .as_object() + .cloned() + .expect("TypedArray.prototype.filter should always return object"); + + Ok(Self { inner: object }) + } + + #[inline] + pub fn map( + &self, + callback: JsObject, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + let object = TypedArray::map( + &self.inner.clone().into(), + &[callback.into(), this_arg.into_or_undefined()], + context, + )? + .as_object() + .cloned() + .expect("TypedArray.prototype.map should always return object"); + + Ok(Self { inner: object }) + } + + #[inline] + pub fn reduce( + &self, + callback: JsObject, + initial_value: Option, + context: &mut Context, + ) -> JsResult { + TypedArray::reduce( + &self.inner.clone().into(), + &[callback.into(), initial_value.into_or_undefined()], + context, + ) + } + + #[inline] + pub fn reduce_right( + &self, + callback: JsObject, + initial_value: Option, + context: &mut Context, + ) -> JsResult { + TypedArray::reduceright( + &self.inner.clone().into(), + &[callback.into(), initial_value.into_or_undefined()], + context, + ) + } + + #[inline] + pub fn reverse(&self, context: &mut Context) -> JsResult { + TypedArray::reverse(&self.inner.clone().into(), &[], context)?; + Ok(self.clone()) + } + + #[inline] + pub fn slice( + &self, + start: Option, + end: Option, + context: &mut Context, + ) -> JsResult { + let object = TypedArray::slice( + &self.inner.clone().into(), + &[start.into_or_undefined(), end.into_or_undefined()], + context, + )? + .as_object() + .cloned() + .expect("TypedArray.prototype.slice should always return object"); + + Ok(Self { inner: object }) + } + + #[inline] + pub fn find( + &self, + predicate: JsObject, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + TypedArray::find( + &self.inner.clone().into(), + &[predicate.into(), this_arg.into_or_undefined()], + context, + ) + } + + #[inline] + pub fn index_of( + &self, + search_element: T, + from_index: Option, + context: &mut Context, + ) -> JsResult> + where + T: Into, + { + let index = TypedArray::index_of( + &self.inner.clone().into(), + &[search_element.into(), from_index.into_or_undefined()], + context, + )? + .as_number() + .expect("TypedArray.prototype.indexOf should always return number"); + + #[allow(clippy::float_cmp)] + if index == -1.0 { + Ok(None) + } else { + Ok(Some(index as usize)) + } + } + + #[inline] + pub fn last_index_of( + &self, + search_element: T, + from_index: Option, + context: &mut Context, + ) -> JsResult> + where + T: Into, + { + let index = TypedArray::last_index_of( + &self.inner.clone().into(), + &[search_element.into(), from_index.into_or_undefined()], + context, + )? + .as_number() + .expect("TypedArray.prototype.lastIndexOf should always return number"); + + #[allow(clippy::float_cmp)] + if index == -1.0 { + Ok(None) + } else { + Ok(Some(index as usize)) + } + } + + #[inline] + pub fn join(&self, separator: Option, context: &mut Context) -> JsResult { + TypedArray::join( + &self.inner.clone().into(), + &[separator.into_or_undefined()], + context, + ) + .map(|x| { + x.as_string() + .cloned() + .expect("TypedArray.prototype.join always returns string") + }) + } +} + +impl From for JsObject { + #[inline] + fn from(o: JsTypedArray) -> Self { + o.inner.clone() + } +} + +impl From for JsValue { + #[inline] + fn from(o: JsTypedArray) -> Self { + o.inner.clone().into() + } +} + +impl Deref for JsTypedArray { + type Target = JsObject; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } +} + +impl JsObjectType for JsTypedArray {} + +macro_rules! JsTypedArrayType { + ($name:ident, $constructor_function:ident, $constructor_object:ident, $element:ty) => { + #[doc = concat!("JavaScript `", stringify!($constructor_function), "` rust object.")] + #[derive(Debug, Clone, Trace, Finalize)] + pub struct $name { + inner: JsTypedArray, + } + + impl $name { + #[inline] + pub fn from_iter(elements: I, context: &mut Context) -> JsResult + where + I: IntoIterator, + { + let array = JsArray::from_iter(elements.into_iter().map(JsValue::new), context); + let new_target = context + .intrinsics() + .constructors() + .$constructor_object() + .constructor() + .into(); + let object = crate::builtins::typed_array::$constructor_function::constructor( + &new_target, + &[array.into()], + context, + )? + .as_object() + .expect("object") + .clone(); + + Ok(Self { + inner: JsTypedArray { inner: object }, + }) + } + } + + impl From<$name> for JsObject { + #[inline] + fn from(o: $name) -> Self { + o.inner.inner.clone() + } + } + + impl From<$name> for JsValue { + #[inline] + fn from(o: $name) -> Self { + o.inner.inner.clone().into() + } + } + + impl Deref for $name { + type Target = JsTypedArray; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.inner + } + } + }; +} + +JsTypedArrayType!(JsUint8Array, Uint8Array, typed_uint8_array, u8); +JsTypedArrayType!(JsInt8Array, Int8Array, typed_int8_array, i8); +JsTypedArrayType!(JsUint16Array, Uint16Array, typed_uint16_array, u16); +JsTypedArrayType!(JsInt16Array, Int16Array, typed_int16_array, i16); +JsTypedArrayType!(JsUint32Array, Uint32Array, typed_uint32_array, u32); +JsTypedArrayType!(JsInt32Array, Int32Array, typed_int32_array, i32); +JsTypedArrayType!(JsFloat32Array, Float32Array, typed_float32_array, f32); +JsTypedArrayType!(JsFloat64Array, Float64Array, typed_float64_array, f64); diff --git a/boa_engine/src/object/mod.rs b/boa_engine/src/object/mod.rs index 9b33d425c42..5a847542aed 100644 --- a/boa_engine/src/object/mod.rs +++ b/boa_engine/src/object/mod.rs @@ -59,10 +59,12 @@ mod tests; pub(crate) mod internal_methods; mod jsarray; mod jsobject; +mod jstypedarray; mod operations; mod property_map; pub use jsarray::*; +pub use jstypedarray::*; pub(crate) trait JsObjectType: Into + Into + Deref diff --git a/boa_engine/src/value/conversions.rs b/boa_engine/src/value/conversions.rs index ef2e7f8e8f1..771acbbf8c2 100644 --- a/boa_engine/src/value/conversions.rs +++ b/boa_engine/src/value/conversions.rs @@ -42,30 +42,58 @@ impl Display for TryFromCharError { } } -impl From for JsValue { +impl From for JsValue { #[allow(clippy::float_cmp)] #[inline] - fn from(value: f64) -> Self { + fn from(value: f32) -> Self { // if value as i32 as f64 == value { // Self::Integer(value as i32) // } else { - Self::Rational(value) + Self::Rational(value.into()) // } } } -impl From for JsValue { +impl From for JsValue { #[allow(clippy::float_cmp)] #[inline] - fn from(value: f32) -> Self { - // if value as i32 as f32 == value { + fn from(value: f64) -> Self { + // if value as i32 as f64 == value { // Self::Integer(value as i32) // } else { - Self::Rational(value.into()) + Self::Rational(value) // } } } +impl From for JsValue { + #[inline] + fn from(value: u8) -> Self { + Self::Integer(value.into()) + } +} + +impl From for JsValue { + #[inline] + fn from(value: i8) -> Self { + Self::Integer(value.into()) + } +} + +impl From for JsValue { + #[inline] + fn from(value: u16) -> Self { + Self::Integer(value.into()) + } +} + +impl From for JsValue { + #[inline] + fn from(value: i16) -> Self { + Self::Integer(value.into()) + } +} + impl From for JsValue { #[inline] fn from(value: u32) -> Self { diff --git a/boa_examples/src/bin/jstypedarray.rs b/boa_examples/src/bin/jstypedarray.rs new file mode 100644 index 00000000000..e10b788e5fb --- /dev/null +++ b/boa_examples/src/bin/jstypedarray.rs @@ -0,0 +1,46 @@ +// This example shows how to manipulate a Javascript array using Rust code. + +use boa_engine::{ + object::{FunctionBuilder, JsUint8Array}, + property::Attribute, + Context, JsResult, JsValue, +}; + +fn main() -> JsResult<()> { + // We create a new `Context` to create a new Javascript executor. + let context = &mut Context::default(); + + let data: Vec = (0..=255).collect(); + + let array = JsUint8Array::from_iter(data, context)?; + + assert_eq!(array.get(0, context)?, JsValue::new(0)); + + let mut sum = 0; + + for i in 0..=255 { + assert_eq!(array.at(i, context)?, JsValue::new(i)); + sum += i; + } + + let callback = FunctionBuilder::native(context, |_this, args, context| { + let accumulator = args.get(0).cloned().unwrap_or_default(); + let value = args.get(1).cloned().unwrap_or_default(); + + accumulator.add(&value, context) + }) + .build(); + + assert_eq!( + array.reduce(callback, Some(JsValue::new(0)), context)?, + JsValue::new(sum) + ); + + context.register_global_property( + "myUint8Array", + array, + Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + ); + + Ok(()) +} From 0d520b8ff38b704f382876b83fcedf0333e8eea4 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 13 Apr 2022 13:58:44 +0200 Subject: [PATCH 2/2] Change `JsTypedArray::inner` to JsValue --- boa_engine/src/object/jstypedarray.rs | 113 ++++++++++++-------------- 1 file changed, 50 insertions(+), 63 deletions(-) diff --git a/boa_engine/src/object/jstypedarray.rs b/boa_engine/src/object/jstypedarray.rs index cfe4cf712db..02b28e30109 100644 --- a/boa_engine/src/object/jstypedarray.rs +++ b/boa_engine/src/object/jstypedarray.rs @@ -10,14 +10,16 @@ use std::ops::Deref; /// JavaScript `TypedArray` rust object. #[derive(Debug, Clone, Trace, Finalize)] pub struct JsTypedArray { - inner: JsObject, + inner: JsValue, } impl JsTypedArray { #[inline] pub fn from_object(object: JsObject, context: &mut Context) -> JsResult { if object.borrow().is_typed_array() { - Ok(Self { inner: object }) + Ok(Self { + inner: object.into(), + }) } else { context.throw_type_error("object is not an TypedArray") } @@ -28,18 +30,16 @@ impl JsTypedArray { /// Same a `array.length` in JavaScript. #[inline] pub fn length(&self, context: &mut Context) -> JsResult { - Ok( - TypedArray::length(&self.inner.clone().into(), &[], context)? - .as_number() - .map(|x| x as usize) - .expect("length should return a number"), - ) + Ok(TypedArray::length(&self.inner, &[], context)? + .as_number() + .map(|x| x as usize) + .expect("length should return a number")) } /// Check if the array is empty, i.e. the `length` is zero. #[inline] pub fn is_empty(&self, context: &mut Context) -> JsResult { - self.inner.length_of_array_like(context).map(|len| len == 0) + Ok(self.length(context)? == 0) } #[inline] @@ -47,27 +47,23 @@ impl JsTypedArray { where T: Into, { - TypedArray::at(&self.inner.clone().into(), &[index.into().into()], context) + TypedArray::at(&self.inner, &[index.into().into()], context) } #[inline] pub fn byte_length(&self, context: &mut Context) -> JsResult { - Ok( - TypedArray::byte_length(&self.inner.clone().into(), &[], context)? - .as_number() - .map(|x| x as usize) - .expect("byteLength should return a number"), - ) + Ok(TypedArray::byte_length(&self.inner, &[], context)? + .as_number() + .map(|x| x as usize) + .expect("byteLength should return a number")) } #[inline] pub fn byte_offset(&self, context: &mut Context) -> JsResult { - Ok( - TypedArray::byte_offset(&self.inner.clone().into(), &[], context)? - .as_number() - .map(|x| x as usize) - .expect("byteLength should return a number"), - ) + Ok(TypedArray::byte_offset(&self.inner, &[], context)? + .as_number() + .map(|x| x as usize) + .expect("byteLength should return a number")) } #[inline] @@ -82,7 +78,7 @@ impl JsTypedArray { T: Into, { TypedArray::fill( - &self.inner.clone().into(), + &self.inner, &[ value.into(), start.into_or_undefined(), @@ -100,7 +96,7 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { let result = TypedArray::every( - &self.inner.clone().into(), + &self.inner, &[predicate.into(), this_arg.into_or_undefined()], context, )? @@ -118,7 +114,7 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { let result = TypedArray::some( - &self.inner.clone().into(), + &self.inner, &[callback.into(), this_arg.into_or_undefined()], context, )? @@ -130,11 +126,7 @@ impl JsTypedArray { #[inline] pub fn sort(&self, compare_fn: Option, context: &mut Context) -> JsResult { - TypedArray::sort( - &self.inner.clone().into(), - &[compare_fn.into_or_undefined()], - context, - )?; + TypedArray::sort(&self.inner, &[compare_fn.into_or_undefined()], context)?; Ok(self.clone()) } @@ -147,13 +139,10 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { let object = TypedArray::filter( - &self.inner.clone().into(), + &self.inner, &[callback.into(), this_arg.into_or_undefined()], context, - )? - .as_object() - .cloned() - .expect("TypedArray.prototype.filter should always return object"); + )?; Ok(Self { inner: object }) } @@ -166,13 +155,10 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { let object = TypedArray::map( - &self.inner.clone().into(), + &self.inner, &[callback.into(), this_arg.into_or_undefined()], context, - )? - .as_object() - .cloned() - .expect("TypedArray.prototype.map should always return object"); + )?; Ok(Self { inner: object }) } @@ -185,7 +171,7 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { TypedArray::reduce( - &self.inner.clone().into(), + &self.inner, &[callback.into(), initial_value.into_or_undefined()], context, ) @@ -199,7 +185,7 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { TypedArray::reduceright( - &self.inner.clone().into(), + &self.inner, &[callback.into(), initial_value.into_or_undefined()], context, ) @@ -207,7 +193,7 @@ impl JsTypedArray { #[inline] pub fn reverse(&self, context: &mut Context) -> JsResult { - TypedArray::reverse(&self.inner.clone().into(), &[], context)?; + TypedArray::reverse(&self.inner, &[], context)?; Ok(self.clone()) } @@ -219,13 +205,10 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { let object = TypedArray::slice( - &self.inner.clone().into(), + &self.inner, &[start.into_or_undefined(), end.into_or_undefined()], context, - )? - .as_object() - .cloned() - .expect("TypedArray.prototype.slice should always return object"); + )?; Ok(Self { inner: object }) } @@ -238,7 +221,7 @@ impl JsTypedArray { context: &mut Context, ) -> JsResult { TypedArray::find( - &self.inner.clone().into(), + &self.inner, &[predicate.into(), this_arg.into_or_undefined()], context, ) @@ -255,7 +238,7 @@ impl JsTypedArray { T: Into, { let index = TypedArray::index_of( - &self.inner.clone().into(), + &self.inner, &[search_element.into(), from_index.into_or_undefined()], context, )? @@ -281,7 +264,7 @@ impl JsTypedArray { T: Into, { let index = TypedArray::last_index_of( - &self.inner.clone().into(), + &self.inner, &[search_element.into(), from_index.into_or_undefined()], context, )? @@ -298,12 +281,7 @@ impl JsTypedArray { #[inline] pub fn join(&self, separator: Option, context: &mut Context) -> JsResult { - TypedArray::join( - &self.inner.clone().into(), - &[separator.into_or_undefined()], - context, - ) - .map(|x| { + TypedArray::join(&self.inner, &[separator.into_or_undefined()], context).map(|x| { x.as_string() .cloned() .expect("TypedArray.prototype.join always returns string") @@ -314,14 +292,17 @@ impl JsTypedArray { impl From for JsObject { #[inline] fn from(o: JsTypedArray) -> Self { - o.inner.clone() + o.inner + .as_object() + .expect("should always be an object") + .clone() } } impl From for JsValue { #[inline] fn from(o: JsTypedArray) -> Self { - o.inner.clone().into() + o.inner.clone() } } @@ -330,7 +311,7 @@ impl Deref for JsTypedArray { #[inline] fn deref(&self) -> &Self::Target { - &self.inner + self.inner.as_object().expect("should always be an object") } } @@ -367,7 +348,9 @@ macro_rules! JsTypedArrayType { .clone(); Ok(Self { - inner: JsTypedArray { inner: object }, + inner: JsTypedArray { + inner: object.into(), + }, }) } } @@ -375,14 +358,18 @@ macro_rules! JsTypedArrayType { impl From<$name> for JsObject { #[inline] fn from(o: $name) -> Self { - o.inner.inner.clone() + o.inner + .inner + .as_object() + .expect("should always be an object") + .clone() } } impl From<$name> for JsValue { #[inline] fn from(o: $name) -> Self { - o.inner.inner.clone().into() + o.inner.inner.clone() } }