From 14b34fe395ffb84829ae352bab46fe0e40802b6a Mon Sep 17 00:00:00 2001 From: Angelo Ceccato <30749948+AngeloChecked@users.noreply.github.com> Date: Sun, 25 Feb 2024 00:36:01 +0100 Subject: [PATCH] Js typed array methods (#3481) * implemented jstypedarray find_index * implemented find_last * implemented find_last_index * implemented forEach * implemented includes * implemented set * implemented subarray * implemented get buffer * implemented constructor * implemented copyWithin * implemented toLocaleString * implemented toStringTag * make linter happy * doc test for buffer and update example * rename foreach to for_each * format --------- Co-authored-by: Angelo Ceccato Co-authored-by: Angelo Ceccato --- .../src/builtins/typed_array/builtin.rs | 20 +- .../src/object/builtins/jstypedarray.rs | 498 ++++++++++++++++++ examples/src/bin/jstypedarray.rs | 174 +++++- 3 files changed, 683 insertions(+), 9 deletions(-) diff --git a/core/engine/src/builtins/typed_array/builtin.rs b/core/engine/src/builtins/typed_array/builtin.rs index 0ea69306cc..60ab66c33a 100644 --- a/core/engine/src/builtins/typed_array/builtin.rs +++ b/core/engine/src/builtins/typed_array/builtin.rs @@ -120,7 +120,7 @@ impl IntrinsicObject for BuiltinTypedArray { .method(Self::find_index, js_string!("findIndex"), 1) .method(Self::find_last, js_string!("findLast"), 1) .method(Self::find_last_index, js_string!("findLastIndex"), 1) - .method(Self::foreach, js_string!("forEach"), 1) + .method(Self::for_each, js_string!("forEach"), 1) .method(Self::includes, js_string!("includes"), 1) .method(Self::index_of, js_string!("indexOf"), 1) .method(Self::join, js_string!("join"), 1) @@ -402,7 +402,7 @@ impl BuiltinTypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer - fn buffer(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + pub(crate) fn buffer(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { // 1. Let O be the this value. // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. @@ -488,7 +488,11 @@ impl BuiltinTypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin - fn copy_within(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult { + pub(crate) fn copy_within( + this: &JsValue, + args: &[JsValue], + context: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?; @@ -994,7 +998,7 @@ impl BuiltinTypedArray { /// - [ECMAScript reference][spec] /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach - pub(crate) fn foreach( + pub(crate) fn for_each( this: &JsValue, args: &[JsValue], context: &mut Context, @@ -2432,7 +2436,7 @@ impl BuiltinTypedArray { /// /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring /// [spec-402]: https://402.ecma-international.org/10.0/#sup-array.prototype.tolocalestring - fn to_locale_string( + pub(crate) fn to_locale_string( this: &JsValue, args: &[JsValue], context: &mut Context, @@ -2604,7 +2608,11 @@ impl BuiltinTypedArray { /// /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag #[allow(clippy::unnecessary_wraps)] - fn to_string_tag(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult { + pub(crate) fn to_string_tag( + this: &JsValue, + _: &[JsValue], + _: &mut Context, + ) -> JsResult { // 1. Let O be the this value. // 2. If Type(O) is not Object, return undefined. // 3. If O does not have a [[TypedArrayName]] internal slot, return undefined. diff --git a/core/engine/src/object/builtins/jstypedarray.rs b/core/engine/src/object/builtins/jstypedarray.rs index c533925a33..567d5c8ee2 100644 --- a/core/engine/src/object/builtins/jstypedarray.rs +++ b/core/engine/src/object/builtins/jstypedarray.rs @@ -61,6 +61,31 @@ impl JsTypedArray { BuiltinTypedArray::at(&self.inner.clone().into(), &[index.into().into()], context) } + /// Returns the `ArrayBuffer` referenced by this typed array at construction time. + /// + /// Calls `TypedArray.prototype.buffer()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{js_string, JsResult, object::{builtins::{JsUint8Array, JsArrayBuffer}}, property::{PropertyKey}, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let array_buffer8 = JsArrayBuffer::new(8, context)?; + /// let array = JsUint8Array::from_array_buffer(array_buffer8, context)?; + /// assert_eq!( + /// array.buffer(context)?.as_object().unwrap().get(PropertyKey::String(js_string!("byteLength")), context).unwrap(), + /// JsValue::new(8) + /// ); + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn buffer(&self, context: &mut Context) -> JsResult { + BuiltinTypedArray::buffer(&self.inner.clone().into(), &[], context) + } + /// Returns `TypedArray.prototype.byteLength`. #[inline] pub fn byte_length(&self, context: &mut Context) -> JsResult { @@ -83,6 +108,85 @@ impl JsTypedArray { ) } + /// Function that created the instance object. It is the hidden `TypedArray` constructor function, + /// but each typed array subclass also defines its own constructor property. + /// + /// Returns `TypedArray.prototype.constructor`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsNativeError, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?; + /// assert_eq!( + /// Err(JsNativeError::typ() + /// .with_message("the TypedArray constructor should never be called directly") + /// .into()), + /// array.constructor(context) + /// ); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn constructor(&self, context: &mut Context) -> JsResult { + BuiltinTypedArray::constructor(&self.inner.clone().into(), &[], context) + } + + /// Shallow copies part of this typed array to another location in the same typed + /// array and returns this typed array without modifying its length. + /// + /// Returns `TypedArray.prototype.copyWithin()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, JsValue, object::{builtins::{JsUint8Array}}, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; + /// array.copy_within(3, 1, Some(3), context)?; + /// assert_eq!(array.get(0, context)?, JsValue::new(1.0)); + /// assert_eq!(array.get(1, context)?, JsValue::new(2.0)); + /// assert_eq!(array.get(2, context)?, JsValue::new(3.0)); + /// assert_eq!(array.get(3, context)?, JsValue::new(2.0)); + /// assert_eq!(array.get(4, context)?, JsValue::new(3.0)); + /// assert_eq!(array.get(5, context)?, JsValue::new(6.0)); + /// assert_eq!(array.get(6, context)?, JsValue::new(7.0)); + /// assert_eq!(array.get(7, context)?, JsValue::new(8.0)); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn copy_within( + &self, + target: T, + start: u64, + end: Option, + context: &mut Context, + ) -> JsResult + where + T: Into, + { + let object = BuiltinTypedArray::copy_within( + &self.inner.clone().into(), + &[target.into(), start.into(), end.into_or_undefined()], + context, + )?; + + Ok(Self { + inner: object + .as_object() + .cloned() + .expect("`copyWithin` must always return a `TypedArray` on success"), + }) + } + /// Calls `TypedArray.prototype.fill()`. pub fn fill( &self, @@ -155,6 +259,65 @@ impl JsTypedArray { Ok(self.clone()) } + /// Returns a new typed array on the same `ArrayBuffer` store and with the same element + /// types as for this typed array. + /// The begin offset is inclusive and the end offset is exclusive. + /// + /// Calls `TypedArray.prototype.subarray()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; + /// let subarray2_6 = array.subarray(2, 6, context)?; + /// assert_eq!(subarray2_6.length(context)?, 4); + /// assert_eq!(subarray2_6.get(0, context)?, JsValue::new(3.0)); + /// assert_eq!(subarray2_6.get(1, context)?, JsValue::new(4.0)); + /// assert_eq!(subarray2_6.get(2, context)?, JsValue::new(5.0)); + /// assert_eq!(subarray2_6.get(3, context)?, JsValue::new(6.0)); + /// let subarray4_6 = array.subarray(-4, 6, context)?; + /// assert_eq!(subarray4_6.length(context)?, 2); + /// assert_eq!(subarray4_6.get(0, context)?, JsValue::new(5.0)); + /// assert_eq!(subarray4_6.get(1, context)?, JsValue::new(6.0)); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn subarray(&self, begin: i64, end: i64, context: &mut Context) -> JsResult { + let subarray = BuiltinTypedArray::subarray( + &self.inner.clone().into(), + &[begin.into(), end.into()], + context, + )?; + + Ok(Self { + inner: subarray + .as_object() + .cloned() + .expect("`subarray` must always return a `TypedArray` on success"), + }) + } + + /// Calls `TypedArray.prototype.toLocaleString()` + #[inline] + pub fn to_locale_string( + &self, + reserved1: Option, + reserved2: Option, + context: &mut Context, + ) -> JsResult { + BuiltinTypedArray::to_locale_string( + &self.inner.clone().into(), + &[reserved1.into_or_undefined(), reserved2.into_or_undefined()], + context, + ) + } + /// Calls `TypedArray.prototype.filter()`. #[inline] pub fn filter( @@ -236,6 +399,52 @@ impl JsTypedArray { Ok(self.clone()) } + /// Stores multiple values in the typed array, reading input values from a specified array. + /// + /// Returns `TypedArray.prototype.set()`. + /// + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::{JsUint8Array, JsArray, JsArrayBuffer}}, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let array_buffer8 = JsArrayBuffer::new(8, context)?; + /// let initialized8_array = JsUint8Array::from_array_buffer(array_buffer8, context)?; + /// initialized8_array.set_values( + /// JsArray::from_iter(vec![JsValue::new(1), JsValue::new(2)], context).into(), + /// Some(3), + /// context, + /// )?; + /// assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0)); + /// assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0)); + /// assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0)); + /// assert_eq!(initialized8_array.get(8, context)?, JsValue::Undefined); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn set_values( + &self, + source: JsValue, + offset: Option, + context: &mut Context, + ) -> JsResult { + BuiltinTypedArray::set( + &self.inner.clone().into(), + &[source, offset.into_or_undefined()], + context, + ) + } + /// Calls `TypedArray.prototype.slice()`. #[inline] pub fn slice( @@ -273,6 +482,271 @@ impl JsTypedArray { ) } + /// Returns the index of the first element in an array that satisfies the + /// provided testing function. + /// If no elements satisfy the testing function, `JsResult::Ok(None)` is returned. + /// + /// Calls `TypedArray.prototype.findIndex()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// let context = &mut Context::default(); + /// let data: Vec = (0..=255).collect(); + /// let array = JsUint8Array::from_iter(data, context)?; + /// + /// let greter_than_10_predicate = FunctionObjectBuilder::new( + /// context.realm(), + /// NativeFunction::from_fn_ptr(|_this, args, _context| { + /// let element = args + /// .first() + /// .cloned() + /// .unwrap_or_default() + /// .as_number() + /// .expect("error at number conversion"); + /// Ok(JsValue::Boolean(element > 10.0)) + /// }), + /// ) + /// .build(); + /// assert_eq!( + /// array.find_index(greter_than_10_predicate, None, context), + /// Ok(Some(11)) + /// ); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn find_index( + &self, + predicate: JsFunction, + this_arg: Option, + context: &mut Context, + ) -> JsResult> { + let index = BuiltinTypedArray::find_index( + &self.inner.clone().into(), + &[predicate.into(), this_arg.into_or_undefined()], + context, + )? + .as_number() + .expect("TypedArray.prototype.findIndex() should always return number"); + + if index >= 0.0 { + Ok(Some(index as u64)) + } else { + Ok(None) + } + } + + /// Iterates the typed array in reverse order and returns the value of + /// the first element that satisfies the provided testing function. + /// If no elements satisfy the testing function, `JsResult::Ok(None)` is returned. + /// + /// Calls `TypedArray.prototype.findLast()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// let context = &mut Context::default(); + /// let data: Vec = (0..=255).collect(); + /// let array = JsUint8Array::from_iter(data, context)?; + /// + /// let lower_than_200_predicate = FunctionObjectBuilder::new( + /// context.realm(), + /// NativeFunction::from_fn_ptr(|_this, args, _context| { + /// let element = args + /// .first() + /// .cloned() + /// .unwrap_or_default() + /// .as_number() + /// .expect("error at number conversion"); + /// Ok(JsValue::Boolean(element < 200.0)) + /// }), + /// ) + /// .build(); + /// assert_eq!( + /// array.find_last(lower_than_200_predicate.clone(), None, context), + /// Ok(JsValue::Integer(199)) + /// ); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn find_last( + &self, + predicate: JsFunction, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + BuiltinTypedArray::find_last( + &self.inner.clone().into(), + &[predicate.into(), this_arg.into_or_undefined()], + context, + ) + } + + /// Iterates the typed array in reverse order and returns the index of + /// the first element that satisfies the provided testing function. + /// If no elements satisfy the testing function, `JsResult::OK(None)` is returned. + /// + /// Calls `TypedArray.prototype.findLastIndex()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// let context = &mut Context::default(); + /// let data: Vec = (0..=255).collect(); + /// let array = JsUint8Array::from_iter(data, context)?; + /// + /// let lower_than_200_predicate = FunctionObjectBuilder::new( + /// context.realm(), + /// NativeFunction::from_fn_ptr(|_this, args, _context| { + /// let element = args + /// .first() + /// .cloned() + /// .unwrap_or_default() + /// .as_number() + /// .expect("error at number conversion"); + /// Ok(JsValue::Boolean(element < 200.0)) + /// }), + /// ) + /// .build(); + /// assert_eq!( + /// array.find_last(lower_than_200_predicate.clone(), None, context), + /// Ok(JsValue::Integer(199)) + /// ); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn find_last_index( + &self, + predicate: JsFunction, + this_arg: Option, + context: &mut Context, + ) -> JsResult> { + let index = BuiltinTypedArray::find_last_index( + &self.inner.clone().into(), + &[predicate.into(), this_arg.into_or_undefined()], + context, + )? + .as_number() + .expect("TypedArray.prototype.findLastIndex() should always return number"); + + if index >= 0.0 { + Ok(Some(index as u64)) + } else { + Ok(None) + } + } + + /// Executes a provided function once for each typed array element. + /// + /// Calls `TypedArray.prototype.forEach()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_gc::{Gc, GcRefCell}; + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// let context = &mut Context::default(); + /// let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?; + /// let num_to_modify = Gc::new(GcRefCell::new(0u8)); + /// + /// let js_function = FunctionObjectBuilder::new( + /// context.realm(), + /// NativeFunction::from_copy_closure_with_captures( + /// |_, args, captures, inner_context| { + /// let element = args + /// .first() + /// .cloned() + /// .unwrap_or_default() + /// .to_uint8(inner_context) + /// .expect("error at number conversion"); + /// *captures.borrow_mut() += element; + /// Ok(JsValue::Undefined) + /// }, + /// Gc::clone(&num_to_modify), + /// ), + /// ) + /// .build(); + /// + /// array.for_each(js_function, None, context); + /// let borrow = *num_to_modify.borrow(); + /// assert_eq!(borrow, 15u8); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn for_each( + &self, + callback: JsFunction, + this_arg: Option, + context: &mut Context, + ) -> JsResult { + BuiltinTypedArray::for_each( + &self.inner.clone().into(), + &[callback.into(), this_arg.into_or_undefined()], + context, + ) + } + + /// Determines whether a typed array includes a certain value among its entries, + /// returning true or false as appropriate. + /// + /// Calls `TypedArray.prototype.includes()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsValue, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let data: Vec = (0..=255).collect(); + /// let array = JsUint8Array::from_iter(data, context)?; + /// + /// assert_eq!(array.includes(JsValue::new(2), None, context), Ok(true)); + /// let empty_array = JsUint8Array::from_iter(vec![], context)?; + /// assert_eq!( + /// empty_array.includes(JsValue::new(2), None, context), + /// Ok(false) + /// ); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn includes( + &self, + search_element: T, + from_index: Option, + context: &mut Context, + ) -> JsResult + where + T: Into, + { + let result = BuiltinTypedArray::includes( + &self.inner.clone().into(), + &[search_element.into(), from_index.into_or_undefined()], + context, + )? + .as_boolean() + .expect("TypedArray.prototype.includes should always return boolean"); + + Ok(result) + } + /// Calls `TypedArray.prototype.indexOf()`. pub fn index_of( &self, @@ -387,6 +861,30 @@ impl JsTypedArray { .expect("`with` must always return a `TypedArray` on success"), }) } + + /// It is a getter that returns the same string as the typed array constructor's name. + /// It returns `Ok(JsValue::Undefined)` if the this value is not one of the typed array subclasses. + /// + /// Returns `TypedArray.prototype.toStringTag()`. + /// + /// # Examples + /// + /// ``` + /// # use boa_engine::{JsResult, js_string, object::{builtins::{JsUint8Array}}, Context}; + /// # fn main() -> JsResult<()> { + /// + /// let context = &mut Context::default(); + /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; + /// let tag = array.to_string_tag(context)?.to_string(context)?; + /// assert_eq!(tag, js_string!("Uint8Array")); + /// + /// # Ok(()) + /// # } + /// ``` + #[inline] + pub fn to_string_tag(&self, context: &mut Context) -> JsResult { + BuiltinTypedArray::to_string_tag(&self.inner.clone().into(), &[], context) + } } impl From for JsObject { diff --git a/examples/src/bin/jstypedarray.rs b/examples/src/bin/jstypedarray.rs index d5fdf5915a..8b6faabfd8 100644 --- a/examples/src/bin/jstypedarray.rs +++ b/examples/src/bin/jstypedarray.rs @@ -3,10 +3,14 @@ use boa_engine::{ js_string, native_function::NativeFunction, - object::{builtins::JsUint8Array, FunctionObjectBuilder}, - property::Attribute, - Context, JsResult, JsValue, + object::{ + builtins::{JsArray, JsArrayBuffer, JsUint8Array}, + FunctionObjectBuilder, + }, + property::{Attribute, PropertyKey}, + Context, JsNativeError, JsResult, JsValue, }; +use boa_gc::{Gc, GcRefCell}; fn main() -> JsResult<()> { // We create a new `Context` to create a new Javascript executor. @@ -41,6 +45,170 @@ fn main() -> JsResult<()> { JsValue::new(sum) ); + let greter_than_10_predicate = FunctionObjectBuilder::new( + context.realm(), + NativeFunction::from_fn_ptr(|_this, args, _context| { + let element = args + .first() + .cloned() + .unwrap_or_default() + .as_number() + .expect("error at number conversion"); + Ok(JsValue::Boolean(element > 10.0)) + }), + ) + .build(); + + assert_eq!( + array.find_index(greter_than_10_predicate, None, context), + Ok(Some(11)) + ); + + let lower_than_200_predicate = FunctionObjectBuilder::new( + context.realm(), + NativeFunction::from_fn_ptr(|_this, args, _context| { + let element = args + .first() + .cloned() + .unwrap_or_default() + .as_number() + .expect("error at number conversion"); + Ok(JsValue::Boolean(element < 200.0)) + }), + ) + .build(); + + assert_eq!( + array.find_last(lower_than_200_predicate.clone(), None, context), + Ok(JsValue::Integer(199)) + ); + + let data: Vec = vec![90, 120, 150, 180, 210, 240]; + let array = JsUint8Array::from_iter(data, context)?; + + assert_eq!( + array.find_last_index(lower_than_200_predicate, None, context), + Ok(Some(3)) + ); + + // forEach + let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?; + let num_to_modify = Gc::new(GcRefCell::new(0u8)); + + let js_function = FunctionObjectBuilder::new( + context.realm(), + NativeFunction::from_copy_closure_with_captures( + |_, args, captures, inner_context| { + let element = args + .first() + .cloned() + .unwrap_or_default() + .to_uint8(inner_context) + .expect("error at number conversion"); + + *captures.borrow_mut() += element; + Ok(JsValue::Undefined) + }, + Gc::clone(&num_to_modify), + ), + ) + .build(); + + let _unused = array.for_each(js_function, None, context); + + let borrow = *num_to_modify.borrow(); + assert_eq!(borrow, 15u8); + + // includes + assert_eq!(array.includes(JsValue::new(2), None, context), Ok(true)); + let empty_array = JsUint8Array::from_iter(vec![], context)?; + assert_eq!( + empty_array.includes(JsValue::new(2), None, context), + Ok(false) + ); + + // set + let array_buffer8 = JsArrayBuffer::new(8, context)?; + let initialized8_array = JsUint8Array::from_array_buffer(array_buffer8, context)?; + initialized8_array.set_values( + JsArray::from_iter(vec![JsValue::new(1), JsValue::new(2)], context).into(), + Some(3), + context, + )?; + assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0)); + assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0)); + assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0)); + assert_eq!(initialized8_array.get(8, context)?, JsValue::Undefined); + + // subarray + let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; + let subarray2_6 = array.subarray(2, 6, context)?; + assert_eq!(subarray2_6.length(context)?, 4); + assert_eq!(subarray2_6.get(0, context)?, JsValue::new(3.0)); + assert_eq!(subarray2_6.get(1, context)?, JsValue::new(4.0)); + assert_eq!(subarray2_6.get(2, context)?, JsValue::new(5.0)); + assert_eq!(subarray2_6.get(3, context)?, JsValue::new(6.0)); + + let subarray4_6 = array.subarray(-4, 6, context)?; + assert_eq!(subarray4_6.length(context)?, 2); + assert_eq!(subarray4_6.get(0, context)?, JsValue::new(5.0)); + assert_eq!(subarray4_6.get(1, context)?, JsValue::new(6.0)); + + // buffer + let array_buffer8 = JsArrayBuffer::new(8, context)?; + let array = JsUint8Array::from_array_buffer(array_buffer8, context)?; + + assert_eq!( + array + .buffer(context)? + .as_object() + .unwrap() + .get(PropertyKey::String(js_string!("byteLength")), context) + .unwrap(), + JsValue::new(8) + ); + + // constructor + assert_eq!( + Err(JsNativeError::typ() + .with_message("the TypedArray constructor should never be called directly") + .into()), + array.constructor(context) + ); + + // copyWithin + let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; + array.copy_within(3, 1, Some(3), context)?; + assert_eq!(array.get(0, context)?, JsValue::new(1.0)); + assert_eq!(array.get(1, context)?, JsValue::new(2.0)); + assert_eq!(array.get(2, context)?, JsValue::new(3.0)); + assert_eq!(array.get(3, context)?, JsValue::new(2.0)); + assert_eq!(array.get(4, context)?, JsValue::new(3.0)); + assert_eq!(array.get(5, context)?, JsValue::new(6.0)); + assert_eq!(array.get(6, context)?, JsValue::new(7.0)); + assert_eq!(array.get(7, context)?, JsValue::new(8.0)); + + // toLocaleString + // let array = JsUint32Array::from_iter(vec![500, 8123, 12], context)?; + // let locales: Option = Some(js_string!("de-DE").into()); + // let options = Some(context.eval(Source::from_bytes( + // r##"let options = { style: "currency", currency: "EUR" }; options;"##, + // ))?); + // assert_eq!( + // array.to_locale_string(locales, options, context)?, + // js_string!("500,00 €,8.123,00 €,12,00 €").into() + // ); + + // toStringTag + let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?; + let tag = array.to_string_tag(context)?.to_string(context)?; + assert_eq!(tag, js_string!("Uint8Array")); + context .register_global_property( js_string!("myUint8Array"),