Browse Source

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 <angeloceccato@IT-mac-cean-669341865.local>
Co-authored-by: Angelo Ceccato <angeloceccato@IT-mac-cean-669341865-old.local>
pull/3693/head
Angelo Ceccato 9 months ago committed by GitHub
parent
commit
14b34fe395
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 20
      core/engine/src/builtins/typed_array/builtin.rs
  2. 498
      core/engine/src/object/builtins/jstypedarray.rs
  3. 174
      examples/src/bin/jstypedarray.rs

20
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_index, js_string!("findIndex"), 1)
.method(Self::find_last, js_string!("findLast"), 1) .method(Self::find_last, js_string!("findLast"), 1)
.method(Self::find_last_index, js_string!("findLastIndex"), 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::includes, js_string!("includes"), 1)
.method(Self::index_of, js_string!("indexOf"), 1) .method(Self::index_of, js_string!("indexOf"), 1)
.method(Self::join, js_string!("join"), 1) .method(Self::join, js_string!("join"), 1)
@ -402,7 +402,7 @@ impl BuiltinTypedArray {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype.buffer
fn buffer(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { pub(crate) fn buffer(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]). // 2. Perform ? RequireInternalSlot(O, [[TypedArrayName]]).
// 3. Assert: O has a [[ViewedArrayBuffer]] internal slot. // 3. Assert: O has a [[ViewedArrayBuffer]] internal slot.
@ -488,7 +488,11 @@ impl BuiltinTypedArray {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.copywithin
fn copy_within(this: &JsValue, args: &[JsValue], context: &mut Context) -> JsResult<JsValue> { pub(crate) fn copy_within(
this: &JsValue,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. Let taRecord be ? ValidateTypedArray(O, seq-cst). // 2. Let taRecord be ? ValidateTypedArray(O, seq-cst).
let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?; let (ta, buf_len) = TypedArray::validate(this, Ordering::SeqCst)?;
@ -994,7 +998,7 @@ impl BuiltinTypedArray {
/// - [ECMAScript reference][spec] /// - [ECMAScript reference][spec]
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.foreach
pub(crate) fn foreach( pub(crate) fn for_each(
this: &JsValue, this: &JsValue,
args: &[JsValue], args: &[JsValue],
context: &mut Context, context: &mut Context,
@ -2432,7 +2436,7 @@ impl BuiltinTypedArray {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring /// [spec]: https://tc39.es/ecma262/#sec-%typedarray%.prototype.tolocalestring
/// [spec-402]: https://402.ecma-international.org/10.0/#sup-array.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, this: &JsValue,
args: &[JsValue], args: &[JsValue],
context: &mut Context, context: &mut Context,
@ -2604,7 +2608,11 @@ impl BuiltinTypedArray {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag /// [spec]: https://tc39.es/ecma262/#sec-get-%typedarray%.prototype-@@tostringtag
#[allow(clippy::unnecessary_wraps)] #[allow(clippy::unnecessary_wraps)]
fn to_string_tag(this: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> { pub(crate) fn to_string_tag(
this: &JsValue,
_: &[JsValue],
_: &mut Context,
) -> JsResult<JsValue> {
// 1. Let O be the this value. // 1. Let O be the this value.
// 2. If Type(O) is not Object, return undefined. // 2. If Type(O) is not Object, return undefined.
// 3. If O does not have a [[TypedArrayName]] internal slot, return undefined. // 3. If O does not have a [[TypedArrayName]] internal slot, return undefined.

498
core/engine/src/object/builtins/jstypedarray.rs

@ -61,6 +61,31 @@ impl JsTypedArray {
BuiltinTypedArray::at(&self.inner.clone().into(), &[index.into().into()], context) 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<JsValue> {
BuiltinTypedArray::buffer(&self.inner.clone().into(), &[], context)
}
/// Returns `TypedArray.prototype.byteLength`. /// Returns `TypedArray.prototype.byteLength`.
#[inline] #[inline]
pub fn byte_length(&self, context: &mut Context) -> JsResult<usize> { pub fn byte_length(&self, context: &mut Context) -> JsResult<usize> {
@ -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<JsValue> {
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<T>(
&self,
target: T,
start: u64,
end: Option<u64>,
context: &mut Context,
) -> JsResult<Self>
where
T: Into<JsValue>,
{
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()`. /// Calls `TypedArray.prototype.fill()`.
pub fn fill<T>( pub fn fill<T>(
&self, &self,
@ -155,6 +259,65 @@ impl JsTypedArray {
Ok(self.clone()) 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<Self> {
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<JsValue>,
reserved2: Option<JsValue>,
context: &mut Context,
) -> JsResult<JsValue> {
BuiltinTypedArray::to_locale_string(
&self.inner.clone().into(),
&[reserved1.into_or_undefined(), reserved2.into_or_undefined()],
context,
)
}
/// Calls `TypedArray.prototype.filter()`. /// Calls `TypedArray.prototype.filter()`.
#[inline] #[inline]
pub fn filter( pub fn filter(
@ -236,6 +399,52 @@ impl JsTypedArray {
Ok(self.clone()) 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<u64>,
context: &mut Context,
) -> JsResult<JsValue> {
BuiltinTypedArray::set(
&self.inner.clone().into(),
&[source, offset.into_or_undefined()],
context,
)
}
/// Calls `TypedArray.prototype.slice()`. /// Calls `TypedArray.prototype.slice()`.
#[inline] #[inline]
pub fn slice( 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<u8> = (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<JsValue>,
context: &mut Context,
) -> JsResult<Option<u64>> {
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<u8> = (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<JsValue>,
context: &mut Context,
) -> JsResult<JsValue> {
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<u8> = (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<JsValue>,
context: &mut Context,
) -> JsResult<Option<u64>> {
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<JsValue>,
context: &mut Context,
) -> JsResult<JsValue> {
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<u8> = (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<T>(
&self,
search_element: T,
from_index: Option<u64>,
context: &mut Context,
) -> JsResult<bool>
where
T: Into<JsValue>,
{
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()`. /// Calls `TypedArray.prototype.indexOf()`.
pub fn index_of<T>( pub fn index_of<T>(
&self, &self,
@ -387,6 +861,30 @@ impl JsTypedArray {
.expect("`with` must always return a `TypedArray` on success"), .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<JsValue> {
BuiltinTypedArray::to_string_tag(&self.inner.clone().into(), &[], context)
}
} }
impl From<JsTypedArray> for JsObject { impl From<JsTypedArray> for JsObject {

174
examples/src/bin/jstypedarray.rs

@ -3,10 +3,14 @@
use boa_engine::{ use boa_engine::{
js_string, js_string,
native_function::NativeFunction, native_function::NativeFunction,
object::{builtins::JsUint8Array, FunctionObjectBuilder}, object::{
property::Attribute, builtins::{JsArray, JsArrayBuffer, JsUint8Array},
Context, JsResult, JsValue, FunctionObjectBuilder,
},
property::{Attribute, PropertyKey},
Context, JsNativeError, JsResult, JsValue,
}; };
use boa_gc::{Gc, GcRefCell};
fn main() -> JsResult<()> { fn main() -> JsResult<()> {
// We create a new `Context` to create a new Javascript executor. // We create a new `Context` to create a new Javascript executor.
@ -41,6 +45,170 @@ fn main() -> JsResult<()> {
JsValue::new(sum) 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<u8> = 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<JsValue> = 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 context
.register_global_property( .register_global_property(
js_string!("myUint8Array"), js_string!("myUint8Array"),

Loading…
Cancel
Save