Skip to main content

boa_engine/object/builtins/
jstypedarray.rs

1//! Rust API wrappers for the `TypedArray` Builtin ECMAScript Objects
2use crate::{
3    Context, JsExpect, JsResult, JsString, JsValue,
4    builtins::{
5        BuiltInConstructor,
6        array_buffer::AlignedVec,
7        typed_array::{BuiltinTypedArray, TypedArray, TypedArrayKind},
8    },
9    error::JsNativeError,
10    object::{JsArrayBuffer, JsFunction, JsObject, JsSharedArrayBuffer},
11    value::{IntoOrUndefined, TryFromJs},
12};
13use boa_gc::{Finalize, Trace};
14use std::ops::Deref;
15use std::sync::atomic::Ordering;
16
17/// `JsTypedArray` provides a wrapper for Boa's implementation of the ECMAScript `TypedArray`
18/// builtin object.
19#[derive(Debug, Clone, Trace, Finalize)]
20pub struct JsTypedArray {
21    inner: JsObject,
22}
23
24impl JsTypedArray {
25    /// Create a [`JsTypedArray`] from a [`JsObject`], if the object is not a typed array throw a
26    /// `TypeError`.
27    ///
28    /// This does not clone the fields of the typed array, it only does a shallow clone of the
29    /// object.
30    #[inline]
31    pub fn from_object(object: JsObject) -> JsResult<Self> {
32        if object.is::<TypedArray>() {
33            Ok(Self { inner: object })
34        } else {
35            Err(JsNativeError::typ()
36                .with_message("object is not a TypedArray")
37                .into())
38        }
39    }
40
41    /// Return the kind of typed array this is. This can be used in conjunction with
42    /// [`js_typed_array_from_kind`] to create a typed array of the same kind.
43    #[inline]
44    #[must_use]
45    pub fn kind(&self) -> Option<TypedArrayKind> {
46        self.inner
47            .downcast_ref::<TypedArray>()
48            .map(|arr| arr.kind())
49    }
50
51    /// Get the length of the array.
52    ///
53    /// Same as `array.length` in JavaScript.
54    ///
55    /// # Errors
56    ///
57    /// Returns an error if the length value is not a number.
58    #[inline]
59    pub fn length(&self, context: &mut Context) -> JsResult<usize> {
60        Ok(
61            BuiltinTypedArray::length(&self.inner.clone().into(), &[], context)?
62                .as_number()
63                .js_expect("length should return a number")? as usize,
64        )
65    }
66
67    /// Check if the array is empty, i.e. the `length` is zero.
68    #[inline]
69    pub fn is_empty(&self, context: &mut Context) -> JsResult<bool> {
70        Ok(self.length(context)? == 0)
71    }
72
73    /// Calls `TypedArray.prototype.at()`.
74    pub fn at<T>(&self, index: T, context: &mut Context) -> JsResult<JsValue>
75    where
76        T: Into<i64>,
77    {
78        BuiltinTypedArray::at(&self.inner.clone().into(), &[index.into().into()], context)
79    }
80
81    /// Returns the `ArrayBuffer` referenced by this typed array at construction time.
82    ///
83    /// Calls `TypedArray.prototype.buffer()`.
84    ///
85    /// # Examples
86    ///
87    /// ```
88    /// # use boa_engine::{js_string, JsResult, object::{builtins::{JsUint8Array, JsArrayBuffer}}, property::{PropertyKey}, JsValue, Context};
89    /// # fn main() -> JsResult<()> {
90    ///
91    /// let context = &mut Context::default();
92    /// let array_buffer8 = JsArrayBuffer::new(8, context)?;
93    /// let array = JsUint8Array::from_array_buffer(array_buffer8, context)?;
94    /// assert_eq!(
95    ///     array.buffer(context)?.as_object().unwrap().get(PropertyKey::String(js_string!("byteLength")), context).unwrap(),
96    ///     JsValue::new(8)
97    /// );
98    /// # Ok(())
99    /// # }
100    /// ```
101    #[inline]
102    pub fn buffer(&self, context: &mut Context) -> JsResult<JsValue> {
103        BuiltinTypedArray::buffer(&self.inner.clone().into(), &[], context)
104    }
105
106    /// Returns `TypedArray.prototype.byteLength`.
107    ///
108    /// # Errors
109    ///
110    /// Returns an error if the byteLength value is not a number.
111    #[inline]
112    pub fn byte_length(&self, context: &mut Context) -> JsResult<usize> {
113        Ok(
114            BuiltinTypedArray::byte_length(&self.inner.clone().into(), &[], context)?
115                .as_number()
116                .js_expect("byteLength should return a number")? as usize,
117        )
118    }
119
120    /// Returns `TypedArray.prototype.byteOffset`.
121    ///
122    /// # Errors
123    ///
124    /// Returns an error if the byteOffset value is not a number.
125    #[inline]
126    pub fn byte_offset(&self, context: &mut Context) -> JsResult<usize> {
127        Ok(
128            BuiltinTypedArray::byte_offset(&self.inner.clone().into(), &[], context)?
129                .as_number()
130                .js_expect("byteLength should return a number")? as usize,
131        )
132    }
133
134    /// Function that created the instance object. It is the hidden `TypedArray` constructor function,
135    /// but each typed array subclass also defines its own constructor property.
136    ///
137    /// Returns `TypedArray.prototype.constructor`.
138    ///
139    /// # Examples
140    ///
141    /// ```
142    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsNativeError, Context};
143    /// # fn main() -> JsResult<()> {
144    ///
145    /// let context = &mut Context::default();
146    /// let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?;
147    /// assert_eq!(
148    ///     Err(JsNativeError::typ()
149    ///         .with_message("the TypedArray constructor should never be called directly")
150    ///         .into()),
151    ///     array.constructor(context)
152    /// );
153    ///
154    /// # Ok(())
155    /// # }
156    /// ```
157    #[inline]
158    pub fn constructor(&self, context: &mut Context) -> JsResult<JsValue> {
159        BuiltinTypedArray::constructor(&self.inner.clone().into(), &[], context)
160    }
161
162    /// Shallow copies part of this typed array to another location in the same typed
163    /// array and returns this typed array without modifying its length.
164    ///
165    /// Returns `TypedArray.prototype.copyWithin()`.
166    ///
167    /// # Examples
168    ///
169    /// ```
170    /// # use boa_engine::{JsResult, JsValue, object::{builtins::{JsUint8Array}}, Context};
171    /// # fn main() -> JsResult<()> {
172    ///
173    /// let context = &mut Context::default();
174    /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;
175    /// array.copy_within(3, 1, Some(3), context)?;
176    /// assert_eq!(array.get(0, context)?, JsValue::new(1.0));
177    /// assert_eq!(array.get(1, context)?, JsValue::new(2.0));
178    /// assert_eq!(array.get(2, context)?, JsValue::new(3.0));
179    /// assert_eq!(array.get(3, context)?, JsValue::new(2.0));
180    /// assert_eq!(array.get(4, context)?, JsValue::new(3.0));
181    /// assert_eq!(array.get(5, context)?, JsValue::new(6.0));
182    /// assert_eq!(array.get(6, context)?, JsValue::new(7.0));
183    /// assert_eq!(array.get(7, context)?, JsValue::new(8.0));
184    ///
185    /// # Ok(())
186    /// # }
187    /// ```
188    #[inline]
189    pub fn copy_within<T>(
190        &self,
191        target: T,
192        start: u64,
193        end: Option<u64>,
194        context: &mut Context,
195    ) -> JsResult<Self>
196    where
197        T: Into<JsValue>,
198    {
199        let object = BuiltinTypedArray::copy_within(
200            &self.inner.clone().into(),
201            &[target.into(), start.into(), end.into_or_undefined()],
202            context,
203        )?;
204
205        Ok(Self {
206            inner: object
207                .as_object()
208                .js_expect("`copyWithin` must always return a `TypedArray` on success")?,
209        })
210    }
211
212    /// Calls `TypedArray.prototype.fill()`.
213    pub fn fill<T>(
214        &self,
215        value: T,
216        start: Option<usize>,
217        end: Option<usize>,
218        context: &mut Context,
219    ) -> JsResult<Self>
220    where
221        T: Into<JsValue>,
222    {
223        BuiltinTypedArray::fill(
224            &self.inner.clone().into(),
225            &[
226                value.into(),
227                start.into_or_undefined(),
228                end.into_or_undefined(),
229            ],
230            context,
231        )?;
232        Ok(self.clone())
233    }
234
235    /// Calls `TypedArray.prototype.every()`.
236    pub fn every(
237        &self,
238        predicate: JsFunction,
239        this_arg: Option<JsValue>,
240        context: &mut Context,
241    ) -> JsResult<bool> {
242        let result = BuiltinTypedArray::every(
243            &self.inner.clone().into(),
244            &[predicate.into(), this_arg.into_or_undefined()],
245            context,
246        )?
247        .as_boolean()
248        .js_expect("TypedArray.prototype.every should always return boolean")?;
249
250        Ok(result)
251    }
252
253    /// Calls `TypedArray.prototype.some()`.
254    #[inline]
255    pub fn some(
256        &self,
257        callback: JsFunction,
258        this_arg: Option<JsValue>,
259        context: &mut Context,
260    ) -> JsResult<bool> {
261        let result = BuiltinTypedArray::some(
262            &self.inner.clone().into(),
263            &[callback.into(), this_arg.into_or_undefined()],
264            context,
265        )?
266        .as_boolean()
267        .js_expect("TypedArray.prototype.some should always return boolean")?;
268
269        Ok(result)
270    }
271
272    /// Calls `TypedArray.prototype.sort()`.
273    #[inline]
274    pub fn sort(&self, compare_fn: Option<JsFunction>, context: &mut Context) -> JsResult<Self> {
275        BuiltinTypedArray::sort(
276            &self.inner.clone().into(),
277            &[compare_fn.into_or_undefined()],
278            context,
279        )?;
280
281        Ok(self.clone())
282    }
283
284    /// Returns a new typed array on the same `ArrayBuffer` store and with the same element
285    /// types as for this typed array.
286    /// The begin offset is inclusive and the end offset is exclusive.
287    ///
288    /// Calls `TypedArray.prototype.subarray()`.
289    ///
290    /// # Examples
291    ///
292    /// ```
293    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsValue, Context};
294    /// # fn main() -> JsResult<()> {
295    ///
296    /// let context = &mut Context::default();
297    /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;
298    /// let subarray2_6 = array.subarray(2, 6, context)?;
299    /// assert_eq!(subarray2_6.length(context)?, 4);
300    /// assert_eq!(subarray2_6.get(0, context)?, JsValue::new(3.0));
301    /// assert_eq!(subarray2_6.get(1, context)?, JsValue::new(4.0));
302    /// assert_eq!(subarray2_6.get(2, context)?, JsValue::new(5.0));
303    /// assert_eq!(subarray2_6.get(3, context)?, JsValue::new(6.0));
304    /// let subarray4_6 = array.subarray(-4, 6, context)?;
305    /// assert_eq!(subarray4_6.length(context)?, 2);
306    /// assert_eq!(subarray4_6.get(0, context)?, JsValue::new(5.0));
307    /// assert_eq!(subarray4_6.get(1, context)?, JsValue::new(6.0));
308    ///
309    /// # Ok(())
310    /// # }
311    /// ```
312    #[inline]
313    pub fn subarray(&self, begin: i64, end: i64, context: &mut Context) -> JsResult<Self> {
314        let subarray = BuiltinTypedArray::subarray(
315            &self.inner.clone().into(),
316            &[begin.into(), end.into()],
317            context,
318        )?;
319
320        Ok(Self {
321            inner: subarray
322                .as_object()
323                .js_expect("`subarray` must always return a `TypedArray` on success")?,
324        })
325    }
326
327    /// Calls `TypedArray.prototype.toLocaleString()`
328    #[inline]
329    pub fn to_locale_string(
330        &self,
331        reserved1: Option<JsValue>,
332        reserved2: Option<JsValue>,
333        context: &mut Context,
334    ) -> JsResult<JsValue> {
335        BuiltinTypedArray::to_locale_string(
336            &self.inner.clone().into(),
337            &[reserved1.into_or_undefined(), reserved2.into_or_undefined()],
338            context,
339        )
340    }
341
342    /// Calls `TypedArray.prototype.filter()`.
343    #[inline]
344    pub fn filter(
345        &self,
346        callback: JsFunction,
347        this_arg: Option<JsValue>,
348        context: &mut Context,
349    ) -> JsResult<Self> {
350        let object = BuiltinTypedArray::filter(
351            &self.inner.clone().into(),
352            &[callback.into(), this_arg.into_or_undefined()],
353            context,
354        )?;
355
356        Ok(Self {
357            inner: object
358                .as_object()
359                .js_expect("`filter` must always return a `TypedArray` on success")?,
360        })
361    }
362
363    /// Calls `TypedArray.prototype.map()`.
364    #[inline]
365    pub fn map(
366        &self,
367        callback: JsFunction,
368        this_arg: Option<JsValue>,
369        context: &mut Context,
370    ) -> JsResult<Self> {
371        let object = BuiltinTypedArray::map(
372            &self.inner.clone().into(),
373            &[callback.into(), this_arg.into_or_undefined()],
374            context,
375        )?;
376
377        Ok(Self {
378            inner: object
379                .as_object()
380                .js_expect("`map` must always return a `TypedArray` on success")?,
381        })
382    }
383
384    /// Calls `TypedArray.prototype.reduce()`.
385    #[inline]
386    pub fn reduce(
387        &self,
388        callback: JsFunction,
389        initial_value: Option<JsValue>,
390        context: &mut Context,
391    ) -> JsResult<JsValue> {
392        BuiltinTypedArray::reduce(
393            &self.inner.clone().into(),
394            &[callback.into(), initial_value.into_or_undefined()],
395            context,
396        )
397    }
398
399    /// Calls `TypedArray.prototype.reduceRight()`.
400    #[inline]
401    pub fn reduce_right(
402        &self,
403        callback: JsFunction,
404        initial_value: Option<JsValue>,
405        context: &mut Context,
406    ) -> JsResult<JsValue> {
407        BuiltinTypedArray::reduceright(
408            &self.inner.clone().into(),
409            &[callback.into(), initial_value.into_or_undefined()],
410            context,
411        )
412    }
413
414    /// Calls `TypedArray.prototype.reverse()`.
415    #[inline]
416    pub fn reverse(&self, context: &mut Context) -> JsResult<Self> {
417        BuiltinTypedArray::reverse(&self.inner.clone().into(), &[], context)?;
418        Ok(self.clone())
419    }
420
421    /// Stores multiple values in the typed array, reading input values from a specified array.
422    ///
423    /// Returns `TypedArray.prototype.set()`.
424    ///
425    ///
426    /// # Examples
427    ///
428    /// ```
429    /// # use boa_engine::{JsResult, object::{builtins::{JsUint8Array, JsArray, JsArrayBuffer}}, JsValue, Context};
430    /// # fn main() -> JsResult<()> {
431    ///
432    /// let context = &mut Context::default();
433    /// let array_buffer8 = JsArrayBuffer::new(8, context)?;
434    /// let initialized8_array = JsUint8Array::from_array_buffer(array_buffer8, context)?;
435    /// initialized8_array.set_values(
436    ///   JsArray::from_iter(vec![JsValue::new(1), JsValue::new(2)], context).into(),
437    ///   Some(3),
438    ///   context,
439    /// )?;
440    /// assert_eq!(initialized8_array.get(0, context)?, JsValue::new(0));
441    /// assert_eq!(initialized8_array.get(1, context)?, JsValue::new(0));
442    /// assert_eq!(initialized8_array.get(2, context)?, JsValue::new(0));
443    /// assert_eq!(initialized8_array.get(3, context)?, JsValue::new(1.0));
444    /// assert_eq!(initialized8_array.get(4, context)?, JsValue::new(2.0));
445    /// assert_eq!(initialized8_array.get(5, context)?, JsValue::new(0));
446    /// assert_eq!(initialized8_array.get(6, context)?, JsValue::new(0));
447    /// assert_eq!(initialized8_array.get(7, context)?, JsValue::new(0));
448    /// assert_eq!(initialized8_array.get(8, context)?, JsValue::undefined());
449    ///
450    /// # Ok(())
451    /// # }
452    /// ```
453    #[inline]
454    pub fn set_values(
455        &self,
456        source: JsValue,
457        offset: Option<u64>,
458        context: &mut Context,
459    ) -> JsResult<JsValue> {
460        BuiltinTypedArray::set(
461            &self.inner.clone().into(),
462            &[source, offset.into_or_undefined()],
463            context,
464        )
465    }
466
467    /// Calls `TypedArray.prototype.slice()`.
468    #[inline]
469    pub fn slice(
470        &self,
471        start: Option<usize>,
472        end: Option<usize>,
473        context: &mut Context,
474    ) -> JsResult<Self> {
475        let object = BuiltinTypedArray::slice(
476            &self.inner.clone().into(),
477            &[start.into_or_undefined(), end.into_or_undefined()],
478            context,
479        )?;
480
481        Ok(Self {
482            inner: object
483                .as_object()
484                .js_expect("`slice` must always return a `TypedArray` on success")?,
485        })
486    }
487
488    /// Calls `TypedArray.prototype.find()`.
489    #[inline]
490    pub fn find(
491        &self,
492        predicate: JsFunction,
493        this_arg: Option<JsValue>,
494        context: &mut Context,
495    ) -> JsResult<JsValue> {
496        BuiltinTypedArray::find(
497            &self.inner.clone().into(),
498            &[predicate.into(), this_arg.into_or_undefined()],
499            context,
500        )
501    }
502
503    /// Returns the index of the first element in an array that satisfies the
504    /// provided testing function.
505    /// If no elements satisfy the testing function, `JsResult::Ok(None)` is returned.
506    ///
507    /// Calls `TypedArray.prototype.findIndex()`.
508    ///
509    /// # Examples
510    ///
511    /// ```
512    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};
513    /// # fn main() -> JsResult<()> {
514    /// let context = &mut Context::default();
515    /// let data: Vec<u8> = (0..=255).collect();
516    /// let array = JsUint8Array::from_iter(data, context)?;
517    ///
518    /// let greater_than_10_predicate = FunctionObjectBuilder::new(
519    ///     context.realm(),
520    ///     NativeFunction::from_fn_ptr(|_this, args, _context| {
521    ///         let element = args
522    ///             .first()
523    ///             .cloned()
524    ///             .unwrap_or_default()
525    ///             .as_number()
526    ///             .expect("error at number conversion");
527    ///         Ok(JsValue::from(element > 10.0))
528    ///     }),
529    /// )
530    /// .build();
531    /// assert_eq!(
532    ///     array.find_index(greater_than_10_predicate, None, context),
533    ///     Ok(Some(11))
534    /// );
535    ///
536    /// # Ok(())
537    /// # }
538    /// ```
539    #[inline]
540    pub fn find_index(
541        &self,
542        predicate: JsFunction,
543        this_arg: Option<JsValue>,
544        context: &mut Context,
545    ) -> JsResult<Option<u64>> {
546        let index = BuiltinTypedArray::find_index(
547            &self.inner.clone().into(),
548            &[predicate.into(), this_arg.into_or_undefined()],
549            context,
550        )?
551        .as_number()
552        .js_expect("TypedArray.prototype.findIndex() should always return number")?;
553
554        if index >= 0.0 {
555            Ok(Some(index as u64))
556        } else {
557            Ok(None)
558        }
559    }
560
561    /// Iterates the typed array in reverse order and returns the value of
562    /// the first element that satisfies the provided testing function.
563    /// If no elements satisfy the testing function, `JsResult::Ok(None)` is returned.
564    ///
565    /// Calls `TypedArray.prototype.findLast()`.
566    ///
567    /// # Examples
568    ///
569    /// ```
570    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};
571    /// # fn main() -> JsResult<()> {
572    /// let context = &mut Context::default();
573    /// let data: Vec<u8> = (0..=255).collect();
574    /// let array = JsUint8Array::from_iter(data, context)?;
575    ///
576    /// let lower_than_200_predicate = FunctionObjectBuilder::new(
577    ///     context.realm(),
578    ///     NativeFunction::from_fn_ptr(|_this, args, _context| {
579    ///         let element = args
580    ///             .first()
581    ///             .cloned()
582    ///             .unwrap_or_default()
583    ///             .as_number()
584    ///             .expect("error at number conversion");
585    ///         Ok(JsValue::from(element < 200.0))
586    ///     }),
587    /// )
588    /// .build();
589    /// assert_eq!(
590    ///     array.find_last(lower_than_200_predicate.clone(), None, context),
591    ///     Ok(JsValue::new(199))
592    /// );
593    ///
594    /// # Ok(())
595    /// # }
596    /// ```
597    #[inline]
598    pub fn find_last(
599        &self,
600        predicate: JsFunction,
601        this_arg: Option<JsValue>,
602        context: &mut Context,
603    ) -> JsResult<JsValue> {
604        BuiltinTypedArray::find_last(
605            &self.inner.clone().into(),
606            &[predicate.into(), this_arg.into_or_undefined()],
607            context,
608        )
609    }
610
611    /// Iterates the typed array in reverse order and returns the index of
612    /// the first element that satisfies the provided testing function.
613    /// If no elements satisfy the testing function, `JsResult::OK(None)` is returned.
614    ///
615    /// Calls `TypedArray.prototype.findLastIndex()`.
616    ///
617    /// # Examples
618    ///
619    /// ```
620    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};
621    /// # fn main() -> JsResult<()> {
622    /// let context = &mut Context::default();
623    /// let data: Vec<u8> = (0..=255).collect();
624    /// let array = JsUint8Array::from_iter(data, context)?;
625    ///
626    /// let lower_than_200_predicate = FunctionObjectBuilder::new(
627    ///     context.realm(),
628    ///     NativeFunction::from_fn_ptr(|_this, args, _context| {
629    ///         let element = args
630    ///             .first()
631    ///             .cloned()
632    ///             .unwrap_or_default()
633    ///             .as_number()
634    ///             .expect("error at number conversion");
635    ///         Ok(JsValue::from(element < 200.0))
636    ///     }),
637    /// )
638    /// .build();
639    /// assert_eq!(
640    ///     array.find_last(lower_than_200_predicate.clone(), None, context),
641    ///     Ok(JsValue::new(199))
642    /// );
643    ///
644    /// # Ok(())
645    /// # }
646    /// ```
647    #[inline]
648    pub fn find_last_index(
649        &self,
650        predicate: JsFunction,
651        this_arg: Option<JsValue>,
652        context: &mut Context,
653    ) -> JsResult<Option<u64>> {
654        let index = BuiltinTypedArray::find_last_index(
655            &self.inner.clone().into(),
656            &[predicate.into(), this_arg.into_or_undefined()],
657            context,
658        )?
659        .as_number()
660        .js_expect("TypedArray.prototype.findLastIndex() should always return number")?;
661
662        if index >= 0.0 {
663            Ok(Some(index as u64))
664        } else {
665            Ok(None)
666        }
667    }
668
669    /// Executes a provided function once for each typed array element.
670    ///
671    /// Calls `TypedArray.prototype.forEach()`.
672    ///
673    /// # Examples
674    ///
675    /// ```
676    /// # use boa_gc::{Gc, GcRefCell};
677    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array, FunctionObjectBuilder}, NativeFunction, JsValue, Context};
678    /// # fn main() -> JsResult<()> {
679    /// let context = &mut Context::default();
680    /// let array = JsUint8Array::from_iter(vec![1, 2, 3, 4, 5], context)?;
681    /// let num_to_modify = Gc::new(GcRefCell::new(0u8));
682    ///
683    /// let js_function = FunctionObjectBuilder::new(
684    ///     context.realm(),
685    ///     NativeFunction::from_copy_closure_with_captures(
686    ///         |_, args, captures, inner_context| {
687    ///             let element = args
688    ///                 .first()
689    ///                 .cloned()
690    ///                 .unwrap_or_default()
691    ///                 .to_uint8(inner_context)
692    ///                 .expect("error at number conversion");
693    ///             *captures.borrow_mut() += element;
694    ///             Ok(JsValue::undefined())
695    ///         },
696    ///         Gc::clone(&num_to_modify),
697    ///     ),
698    /// )
699    /// .build();
700    ///
701    /// array.for_each(js_function, None, context);
702    /// let borrow = *num_to_modify.borrow();
703    /// assert_eq!(borrow, 15u8);
704    ///
705    /// # Ok(())
706    /// # }
707    /// ```
708    #[inline]
709    pub fn for_each(
710        &self,
711        callback: JsFunction,
712        this_arg: Option<JsValue>,
713        context: &mut Context,
714    ) -> JsResult<JsValue> {
715        BuiltinTypedArray::for_each(
716            &self.inner.clone().into(),
717            &[callback.into(), this_arg.into_or_undefined()],
718            context,
719        )
720    }
721
722    /// Determines whether a typed array includes a certain value among its entries,
723    /// returning true or false as appropriate.
724    ///
725    /// Calls `TypedArray.prototype.includes()`.
726    ///
727    /// # Examples
728    ///
729    /// ```
730    /// # use boa_engine::{JsResult, object::{builtins::JsUint8Array}, JsValue, Context};
731    /// # fn main() -> JsResult<()> {
732    ///
733    /// let context = &mut Context::default();
734    /// let data: Vec<u8> = (0..=255).collect();
735    /// let array = JsUint8Array::from_iter(data, context)?;
736    ///
737    /// assert_eq!(array.includes(JsValue::new(2), None, context), Ok(true));
738    /// let empty_array = JsUint8Array::from_iter(vec![], context)?;
739    /// assert_eq!(
740    ///     empty_array.includes(JsValue::new(2), None, context),
741    ///     Ok(false)
742    /// );
743    ///
744    /// # Ok(())
745    /// # }
746    /// ```
747    #[inline]
748    pub fn includes<T>(
749        &self,
750        search_element: T,
751        from_index: Option<u64>,
752        context: &mut Context,
753    ) -> JsResult<bool>
754    where
755        T: Into<JsValue>,
756    {
757        let result = BuiltinTypedArray::includes(
758            &self.inner.clone().into(),
759            &[search_element.into(), from_index.into_or_undefined()],
760            context,
761        )?
762        .as_boolean()
763        .js_expect("TypedArray.prototype.includes should always return boolean")?;
764
765        Ok(result)
766    }
767
768    /// Calls `TypedArray.prototype.indexOf()`.
769    pub fn index_of<T>(
770        &self,
771        search_element: T,
772        from_index: Option<usize>,
773        context: &mut Context,
774    ) -> JsResult<Option<usize>>
775    where
776        T: Into<JsValue>,
777    {
778        let index = BuiltinTypedArray::index_of(
779            &self.inner.clone().into(),
780            &[search_element.into(), from_index.into_or_undefined()],
781            context,
782        )?
783        .as_number()
784        .js_expect("TypedArray.prototype.indexOf should always return number")?;
785
786        #[allow(clippy::float_cmp)]
787        if index == -1.0 {
788            Ok(None)
789        } else {
790            Ok(Some(index as usize))
791        }
792    }
793
794    /// Calls `TypedArray.prototype.lastIndexOf()`.
795    pub fn last_index_of<T>(
796        &self,
797        search_element: T,
798        from_index: Option<usize>,
799        context: &mut Context,
800    ) -> JsResult<Option<usize>>
801    where
802        T: Into<JsValue>,
803    {
804        let index = BuiltinTypedArray::last_index_of(
805            &self.inner.clone().into(),
806            &[search_element.into(), from_index.into_or_undefined()],
807            context,
808        )?
809        .as_number()
810        .js_expect("TypedArray.prototype.lastIndexOf should always return number")?;
811
812        #[allow(clippy::float_cmp)]
813        if index == -1.0 {
814            Ok(None)
815        } else {
816            Ok(Some(index as usize))
817        }
818    }
819
820    /// Calls `TypedArray.prototype.join()`.
821    #[inline]
822    pub fn join(&self, separator: Option<JsString>, context: &mut Context) -> JsResult<JsString> {
823        BuiltinTypedArray::join(
824            &self.inner.clone().into(),
825            &[separator.into_or_undefined()],
826            context,
827        )
828        .and_then(|x| {
829            x.as_string()
830                .js_expect("TypedArray.prototype.join always returns string")
831                .map_err(Into::into)
832        })
833    }
834
835    /// Calls `TypedArray.prototype.toReversed ( )`.
836    #[inline]
837    pub fn to_reversed(&self, context: &mut Context) -> JsResult<Self> {
838        let array = BuiltinTypedArray::to_reversed(&self.inner.clone().into(), &[], context)?;
839
840        Ok(Self {
841            inner: array
842                .as_object()
843                .js_expect("`to_reversed` must always return a `TypedArray` on success")?,
844        })
845    }
846
847    /// Calls `TypedArray.prototype.toSorted ( comparefn )`.
848    #[inline]
849    pub fn to_sorted(
850        &self,
851        compare_fn: Option<JsFunction>,
852        context: &mut Context,
853    ) -> JsResult<Self> {
854        let array = BuiltinTypedArray::to_sorted(
855            &self.inner.clone().into(),
856            &[compare_fn.into_or_undefined()],
857            context,
858        )?;
859
860        Ok(Self {
861            inner: array
862                .as_object()
863                .js_expect("`to_sorted` must always return a `TypedArray` on success")?,
864        })
865    }
866
867    /// Calls `TypedArray.prototype.with ( index, value )`.
868    #[inline]
869    pub fn with(&self, index: u64, value: JsValue, context: &mut Context) -> JsResult<Self> {
870        let array =
871            BuiltinTypedArray::with(&self.inner.clone().into(), &[index.into(), value], context)?;
872
873        Ok(Self {
874            inner: array
875                .as_object()
876                .js_expect("`with` must always return a `TypedArray` on success")?,
877        })
878    }
879
880    /// Calls `TypedArray.prototype.entries()`.
881    #[inline]
882    pub fn entries(&self, context: &mut Context) -> JsResult<JsValue> {
883        BuiltinTypedArray::entries(&self.inner.clone().into(), &[], context)
884    }
885
886    /// Calls `TypedArray.prototype.keys()`.
887    #[inline]
888    pub fn keys(&self, context: &mut Context) -> JsResult<JsValue> {
889        BuiltinTypedArray::keys(&self.inner.clone().into(), &[], context)
890    }
891
892    /// Calls `TypedArray.prototype.values()`.
893    #[inline]
894    pub fn values(&self, context: &mut Context) -> JsResult<JsValue> {
895        BuiltinTypedArray::values(&self.inner.clone().into(), &[], context)
896    }
897
898    /// Calls `TypedArray.prototype[@@iterator]()`.
899    #[inline]
900    pub fn iterator(&self, context: &mut Context) -> JsResult<JsValue> {
901        BuiltinTypedArray::values(&self.inner.clone().into(), &[], context)
902    }
903
904    /// Calls `TypedArray.prototype.toString()`.
905    #[inline]
906    pub fn to_string(&self, context: &mut Context) -> JsResult<JsString> {
907        // TypedArray.prototype.toString is the same as Array.prototype.toString
908        let result = crate::builtins::Array::to_string(&self.inner.clone().into(), &[], context)?;
909        result
910            .as_string()
911            .js_expect("Array.prototype.toString always returns string")
912            .map_err(Into::into)
913    }
914
915    /// It is a getter that returns the same string as the typed array constructor's name.
916    /// It returns `Ok(JsValue::Undefined)` if the this value is not one of the typed array subclasses.
917    ///
918    /// Returns `TypedArray.prototype.toStringTag()`.
919    ///
920    /// # Examples
921    ///
922    /// ```
923    /// # use boa_engine::{JsResult, js_string, object::{builtins::{JsUint8Array}}, Context};
924    /// # fn main() -> JsResult<()> {
925    ///
926    /// let context = &mut Context::default();
927    /// let array = JsUint8Array::from_iter(vec![1u8, 2u8, 3u8, 4u8, 5u8, 6u8, 7u8, 8u8], context)?;
928    /// let tag = array.to_string_tag(context)?.to_string(context)?;
929    /// assert_eq!(tag, js_string!("Uint8Array"));
930    ///
931    /// # Ok(())
932    /// # }
933    /// ```
934    #[inline]
935    pub fn to_string_tag(&self, context: &mut Context) -> JsResult<JsValue> {
936        BuiltinTypedArray::to_string_tag(&self.inner.clone().into(), &[], context)
937    }
938}
939
940impl From<JsTypedArray> for JsObject {
941    #[inline]
942    fn from(o: JsTypedArray) -> Self {
943        o.inner.clone()
944    }
945}
946
947impl From<JsTypedArray> for JsValue {
948    #[inline]
949    fn from(o: JsTypedArray) -> Self {
950        o.inner.clone().into()
951    }
952}
953
954impl Deref for JsTypedArray {
955    type Target = JsObject;
956
957    #[inline]
958    fn deref(&self) -> &Self::Target {
959        &self.inner
960    }
961}
962
963impl TryFromJs for JsTypedArray {
964    fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
965        if let Some(o) = value.as_object() {
966            Self::from_object(o.clone())
967        } else {
968            Err(JsNativeError::typ()
969                .with_message("value is not a TypedArray object")
970                .into())
971        }
972    }
973}
974
975macro_rules! JsTypedArrayType {
976    (
977        $name:ident,
978        $constructor_function:ident,
979        $kind:expr,
980        $constructor_object:ident,
981        $value_to_elem:ident,
982        $element:ty
983    ) => {
984
985        #[doc = concat!(
986            "`", stringify!($name),
987            "` provides a wrapper for Boa's implementation of the ECMAScript `",
988            stringify!($constructor_function) ,"` builtin object."
989        )]
990        #[derive(Debug, Clone, Trace, Finalize)]
991        pub struct $name {
992            inner: JsTypedArray,
993        }
994
995        impl $name {
996            #[doc = concat!("Creates a `", stringify!($name),
997                "` using a [`JsObject`]. It will make sure that the object is of the correct kind."
998            )]
999            #[inline]
1000            pub fn from_object(object: JsObject) -> JsResult<Self> {
1001                let is_kind = if let Some(int) = object.downcast_ref::<TypedArray>() {
1002                    int.kind() == $kind
1003                } else {
1004                    false
1005                };
1006                if is_kind {
1007                    Ok(Self {
1008                        inner: JsTypedArray {
1009                            inner: object.into(),
1010                        },
1011                    })
1012                } else {
1013                    Err(JsNativeError::typ()
1014                        .with_message("object is not a TypedArray")
1015                        .into())
1016                }
1017            }
1018
1019            /// Create the typed array from a [`JsArrayBuffer`].
1020            pub fn from_array_buffer(
1021                array_buffer: JsArrayBuffer,
1022                context: &mut Context,
1023            ) -> JsResult<Self> {
1024                let new_target = context
1025                    .intrinsics()
1026                    .constructors()
1027                    .$constructor_object()
1028                    .constructor()
1029                    .into();
1030                let object = crate::builtins::typed_array::$constructor_function::constructor(
1031                    &new_target,
1032                    &[array_buffer.into()],
1033                    context,
1034                )?
1035                .as_object()
1036                .js_expect("object")?
1037                .clone();
1038
1039                Ok(Self {
1040                    inner: JsTypedArray {
1041                        inner: object.into(),
1042                    },
1043                })
1044            }
1045
1046            /// Create the typed array from a [`JsSharedArrayBuffer`].
1047            pub fn from_shared_array_buffer(
1048                buffer: JsSharedArrayBuffer,
1049                context: &mut Context,
1050            ) -> JsResult<Self> {
1051                let new_target = context
1052                    .intrinsics()
1053                    .constructors()
1054                    .$constructor_object()
1055                    .constructor()
1056                    .into();
1057                let object = crate::builtins::typed_array::$constructor_function::constructor(
1058                    &new_target,
1059                    &[buffer.into()],
1060                    context,
1061                )?
1062                .as_object()
1063                .js_expect("object")?
1064                .clone();
1065
1066                Ok(Self {
1067                    inner: JsTypedArray {
1068                        inner: object.into(),
1069                    },
1070                })
1071            }
1072
1073            /// Create the typed array from an iterator.
1074            pub fn from_iter<I>(elements: I, context: &mut Context) -> JsResult<Self>
1075            where
1076                I: IntoIterator<Item = $element>,
1077            {
1078                let bytes = AlignedVec::from_iter(
1079                    0,
1080                    elements
1081                    .into_iter()
1082                    .flat_map(<$element>::to_ne_bytes)
1083                );
1084                let array_buffer = JsArrayBuffer::from_byte_block(bytes, context)?;
1085                let new_target = context
1086                    .intrinsics()
1087                    .constructors()
1088                    .$constructor_object()
1089                    .constructor()
1090                    .into();
1091                let object = crate::builtins::typed_array::$constructor_function::constructor(
1092                    &new_target,
1093                    &[array_buffer.into()],
1094                    context,
1095                )?
1096                .as_object()
1097                .js_expect("object")?
1098                .clone();
1099
1100                Ok(Self {
1101                    inner: JsTypedArray {
1102                        inner: object.into(),
1103                    },
1104                })
1105            }
1106
1107            /// Create an iterator over the typed array's elements.
1108            pub fn iter<'a>(&'a self, context: &'a mut Context) -> impl Iterator<Item = $element> + 'a {
1109                let length = self.length(context).unwrap_or(0);
1110                let mut index = 0;
1111                std::iter::from_fn(move || {
1112                    if index < length {
1113                        let value = self.get(index, context).ok()?;
1114                        index += 1;
1115                        value.$value_to_elem(context).ok()
1116                    } else {
1117                        None
1118                    }
1119                })
1120            }
1121        }
1122
1123        impl From<$name> for JsObject {
1124            #[inline]
1125            fn from(o: $name) -> Self {
1126                o.inner
1127                    .inner
1128                    .clone()
1129            }
1130        }
1131
1132        impl From<$name> for JsValue {
1133            #[inline]
1134            fn from(o: $name) -> Self {
1135                o.inner.inner.clone().into()
1136            }
1137        }
1138
1139        impl Deref for $name {
1140            type Target = JsTypedArray;
1141
1142            #[inline]
1143            fn deref(&self) -> &Self::Target {
1144                &self.inner
1145            }
1146        }
1147
1148        impl TryFromJs for $name {
1149            fn try_from_js(value: &JsValue, _context: &mut Context) -> JsResult<Self> {
1150                if let Some(o) = value.as_object() {
1151                    Self::from_object(o.clone())
1152                } else {
1153                    Err(JsNativeError::typ()
1154                        .with_message(concat!(
1155                            "value is not a ",
1156                            stringify!($constructor_function),
1157                            " object"
1158                        ))
1159                        .into())
1160                }
1161            }
1162        }
1163    };
1164}
1165
1166JsTypedArrayType!(
1167    JsUint8Array,
1168    Uint8Array,
1169    TypedArrayKind::Uint8,
1170    typed_uint8_array,
1171    to_uint8,
1172    u8
1173);
1174
1175impl JsUint8Array {
1176    /// Copies the viewed byte range of this `Uint8Array` into a new `Vec<u8>`.
1177    ///
1178    /// Works with both `ArrayBuffer` and `SharedArrayBuffer` backing storage. The buffer must not be
1179    /// detached; otherwise a `TypeError` is returned.
1180    ///
1181    /// # Example
1182    ///
1183    /// ```
1184    /// # use boa_engine::{Context, JsResult, object::builtins::JsUint8Array};
1185    /// # fn main() -> JsResult<()> {
1186    ///
1187    /// let context = &mut Context::default();
1188    /// let data: Vec<u8> = (0..=255).collect();
1189    /// let array = JsUint8Array::from_iter(data.clone(), context)?;
1190    /// let bytes = array.to_vec(context)?;
1191    /// assert_eq!(bytes, data);
1192    ///
1193    /// # Ok(())
1194    /// # }
1195    /// ```
1196    #[inline]
1197    pub fn to_vec(&self, _context: &mut Context) -> JsResult<Vec<u8>> {
1198        let this_val = self.inner.inner.clone().into();
1199        let (obj, buf_byte_len) = TypedArray::validate(&this_val, Ordering::SeqCst)?;
1200        let vec = {
1201            let array = obj.borrow();
1202            let ta = array.data();
1203            let buffer = ta.viewed_array_buffer().as_buffer();
1204            let Some(slice) = buffer.bytes(Ordering::SeqCst) else {
1205                return Err(JsNativeError::typ()
1206                    .with_message("typed array buffer is detached or out of bounds")
1207                    .into());
1208            };
1209            if ta.is_out_of_bounds(slice.len()) {
1210                return Err(JsNativeError::typ()
1211                    .with_message("typed array is outside the bounds of its inner buffer")
1212                    .into());
1213            }
1214            let byte_offset = ta.byte_offset() as usize;
1215            let byte_len = ta.byte_length(buf_byte_len) as usize;
1216            slice.subslice(byte_offset..byte_offset + byte_len).to_vec()
1217        };
1218        Ok(vec)
1219    }
1220}
1221
1222JsTypedArrayType!(
1223    JsUint8ClampedArray,
1224    Uint8ClampedArray,
1225    TypedArrayKind::Uint8Clamped,
1226    typed_uint8clamped_array,
1227    to_uint8_clamp,
1228    u8
1229);
1230JsTypedArrayType!(
1231    JsInt8Array,
1232    Int8Array,
1233    TypedArrayKind::Int8,
1234    typed_int8_array,
1235    to_int8,
1236    i8
1237);
1238JsTypedArrayType!(
1239    JsUint16Array,
1240    Uint16Array,
1241    TypedArrayKind::Uint16,
1242    typed_uint16_array,
1243    to_uint16,
1244    u16
1245);
1246JsTypedArrayType!(
1247    JsInt16Array,
1248    Int16Array,
1249    TypedArrayKind::Int16,
1250    typed_int16_array,
1251    to_int16,
1252    i16
1253);
1254JsTypedArrayType!(
1255    JsUint32Array,
1256    Uint32Array,
1257    TypedArrayKind::Uint32,
1258    typed_uint32_array,
1259    to_u32,
1260    u32
1261);
1262JsTypedArrayType!(
1263    JsInt32Array,
1264    Int32Array,
1265    TypedArrayKind::Int32,
1266    typed_int32_array,
1267    to_i32,
1268    i32
1269);
1270#[cfg(feature = "float16")]
1271JsTypedArrayType!(
1272    JsFloat16Array,
1273    Float16Array,
1274    TypedArrayKind::Float16,
1275    typed_float16_array,
1276    to_f16,
1277    float16::f16
1278);
1279JsTypedArrayType!(
1280    JsBigInt64Array,
1281    BigInt64Array,
1282    TypedArrayKind::BigInt64,
1283    typed_bigint64_array,
1284    to_big_int64,
1285    i64
1286);
1287JsTypedArrayType!(
1288    JsBigUint64Array,
1289    BigUint64Array,
1290    TypedArrayKind::BigUint64,
1291    typed_biguint64_array,
1292    to_big_uint64,
1293    u64
1294);
1295JsTypedArrayType!(
1296    JsFloat32Array,
1297    Float32Array,
1298    TypedArrayKind::Float32,
1299    typed_float32_array,
1300    to_f32,
1301    f32
1302);
1303JsTypedArrayType!(
1304    JsFloat64Array,
1305    Float64Array,
1306    TypedArrayKind::Float64,
1307    typed_float64_array,
1308    to_number,
1309    f64
1310);
1311
1312/// Create a [`JsTypedArray`] from a [`TypedArrayKind`] and a backing [`JsArrayBuffer`].
1313#[inline]
1314pub fn js_typed_array_from_kind(
1315    kind: TypedArrayKind,
1316    inner: JsArrayBuffer,
1317    context: &mut Context,
1318) -> JsResult<JsValue> {
1319    match kind {
1320        TypedArrayKind::Int8 => JsInt8Array::from_array_buffer(inner, context).map(Into::into),
1321        TypedArrayKind::Uint8 => JsUint8Array::from_array_buffer(inner, context).map(Into::into),
1322        TypedArrayKind::Uint8Clamped => {
1323            JsUint8ClampedArray::from_array_buffer(inner, context).map(Into::into)
1324        }
1325        TypedArrayKind::Int16 => JsInt16Array::from_array_buffer(inner, context).map(Into::into),
1326        TypedArrayKind::Uint16 => JsUint16Array::from_array_buffer(inner, context).map(Into::into),
1327        TypedArrayKind::Int32 => JsInt32Array::from_array_buffer(inner, context).map(Into::into),
1328        TypedArrayKind::Uint32 => JsUint32Array::from_array_buffer(inner, context).map(Into::into),
1329        TypedArrayKind::BigInt64 => {
1330            JsBigInt64Array::from_array_buffer(inner, context).map(Into::into)
1331        }
1332        TypedArrayKind::BigUint64 => {
1333            JsBigUint64Array::from_array_buffer(inner, context).map(Into::into)
1334        }
1335        #[cfg(feature = "float16")]
1336        TypedArrayKind::Float16 => {
1337            JsFloat16Array::from_array_buffer(inner, context).map(Into::into)
1338        }
1339        TypedArrayKind::Float32 => {
1340            JsFloat32Array::from_array_buffer(inner, context).map(Into::into)
1341        }
1342        TypedArrayKind::Float64 => {
1343            JsFloat64Array::from_array_buffer(inner, context).map(Into::into)
1344        }
1345    }
1346}
1347
1348#[test]
1349fn typed_iterators_uint8() {
1350    let context = &mut Context::default();
1351    let vec = vec![1u8, 2, 3, 4, 5, 6, 7, 8];
1352
1353    let array = JsUint8Array::from_iter(vec.clone(), context).unwrap();
1354    let vec2 = array.iter(context).collect::<Vec<_>>();
1355    assert_eq!(vec, vec2);
1356}
1357
1358#[test]
1359fn uint8_array_to_vec_roundtrip() {
1360    let context = &mut Context::default();
1361    let data: Vec<u8> = (0..=255).collect();
1362    let array = JsUint8Array::from_iter(data.clone(), context).unwrap();
1363    let bytes = array.to_vec(context).unwrap();
1364    assert_eq!(bytes, data);
1365}
1366
1367#[test]
1368fn typed_iterators_uint32() {
1369    let context = &mut Context::default();
1370    let vec = vec![1u32, 2, 0xFFFF, 4, 0xFF12_3456, 6, 7, 8];
1371
1372    let array = JsUint32Array::from_iter(vec.clone(), context).unwrap();
1373    let vec2 = array.iter(context).collect::<Vec<_>>();
1374    assert_eq!(vec, vec2);
1375}
1376
1377#[test]
1378fn typed_iterators_f32() {
1379    let context = &mut Context::default();
1380    let vec = vec![0.1f32, 0.2, 0.3, 0.4, 1.1, 9.99999];
1381
1382    let array = JsFloat32Array::from_iter(vec.clone(), context).unwrap();
1383    let vec2 = array.iter(context).collect::<Vec<_>>();
1384    assert_eq!(vec, vec2);
1385}
1386
1387#[test]
1388fn typed_array_to_string() {
1389    let context = &mut Context::default();
1390    let vec = vec![1u8, 2, 3];
1391    let array = JsUint8Array::from_iter(vec, context).unwrap();
1392    assert_eq!(
1393        array.to_string(context).unwrap(),
1394        crate::js_string!("1,2,3")
1395    );
1396}
1397
1398#[test]
1399fn typed_array_entries() {
1400    let context = &mut Context::default();
1401    let vec = vec![1u8, 2];
1402    let array = JsUint8Array::from_iter(vec, context).unwrap();
1403    let entries = array.entries(context).unwrap();
1404    let mut entries_vec = Vec::new();
1405    let next_str = crate::js_string!("next");
1406    loop {
1407        let next_fn = entries
1408            .as_object()
1409            .unwrap()
1410            .get(next_str.clone(), context)
1411            .unwrap();
1412        let result = next_fn
1413            .as_object()
1414            .unwrap()
1415            .call(&entries, &[], context)
1416            .unwrap();
1417        if result
1418            .as_object()
1419            .unwrap()
1420            .get(crate::js_string!("done"), context)
1421            .unwrap()
1422            .to_boolean()
1423        {
1424            break;
1425        }
1426        entries_vec.push(
1427            result
1428                .as_object()
1429                .unwrap()
1430                .get(crate::js_string!("value"), context)
1431                .unwrap(),
1432        );
1433    }
1434    assert_eq!(entries_vec.len(), 2);
1435}
1436
1437#[test]
1438fn typed_array_keys() {
1439    let context = &mut Context::default();
1440    let vec = vec![1u8, 2];
1441    let array = JsUint8Array::from_iter(vec, context).unwrap();
1442    let keys = array.keys(context).unwrap();
1443    let mut keys_vec = Vec::new();
1444    let next_str = crate::js_string!("next");
1445    loop {
1446        let next_fn = keys
1447            .as_object()
1448            .unwrap()
1449            .get(next_str.clone(), context)
1450            .unwrap();
1451        let result = next_fn
1452            .as_object()
1453            .unwrap()
1454            .call(&keys, &[], context)
1455            .unwrap();
1456        if result
1457            .as_object()
1458            .unwrap()
1459            .get(crate::js_string!("done"), context)
1460            .unwrap()
1461            .to_boolean()
1462        {
1463            break;
1464        }
1465        keys_vec.push(
1466            result
1467                .as_object()
1468                .unwrap()
1469                .get(crate::js_string!("value"), context)
1470                .unwrap(),
1471        );
1472    }
1473    assert_eq!(keys_vec, vec![JsValue::new(0), JsValue::new(1)]);
1474}
1475
1476#[test]
1477fn typed_array_values() {
1478    let context = &mut Context::default();
1479    let vec = vec![1u8, 2];
1480    let array = JsUint8Array::from_iter(vec, context).unwrap();
1481    let values = array.values(context).unwrap();
1482    let mut values_vec = Vec::new();
1483    let next_str = crate::js_string!("next");
1484    loop {
1485        let next_fn = values
1486            .as_object()
1487            .unwrap()
1488            .get(next_str.clone(), context)
1489            .unwrap();
1490        let result = next_fn
1491            .as_object()
1492            .unwrap()
1493            .call(&values, &[], context)
1494            .unwrap();
1495        if result
1496            .as_object()
1497            .unwrap()
1498            .get(crate::js_string!("done"), context)
1499            .unwrap()
1500            .to_boolean()
1501        {
1502            break;
1503        }
1504        values_vec.push(
1505            result
1506                .as_object()
1507                .unwrap()
1508                .get(crate::js_string!("value"), context)
1509                .unwrap(),
1510        );
1511    }
1512    assert_eq!(values_vec, vec![JsValue::new(1), JsValue::new(2)]);
1513}
1514
1515#[test]
1516fn typed_array_iterator() {
1517    let context = &mut Context::default();
1518    let array = JsUint8Array::from_iter(vec![1u8, 2], context).unwrap();
1519    let values = array.iterator(context).unwrap();
1520    let mut values_vec = Vec::new();
1521    let next_str = crate::js_string!("next");
1522    loop {
1523        let next_fn = values
1524            .as_object()
1525            .unwrap()
1526            .get(next_str.clone(), context)
1527            .unwrap();
1528        let result = next_fn
1529            .as_object()
1530            .unwrap()
1531            .call(&values, &[], context)
1532            .unwrap();
1533        if result
1534            .as_object()
1535            .unwrap()
1536            .get(crate::js_string!("done"), context)
1537            .unwrap()
1538            .to_boolean()
1539        {
1540            break;
1541        }
1542        values_vec.push(
1543            result
1544                .as_object()
1545                .unwrap()
1546                .get(crate::js_string!("value"), context)
1547                .unwrap(),
1548        );
1549    }
1550    assert_eq!(values_vec, vec![JsValue::new(1), JsValue::new(2)]);
1551}
1552
1553#[test]
1554fn typed_array_to_reversed() {
1555    let context = &mut Context::default();
1556    let array = JsUint8Array::from_iter(vec![3u8, 1, 2], context).unwrap();
1557
1558    let reversed = array.to_reversed(context).unwrap();
1559
1560    // New array has reversed order
1561    assert_eq!(reversed.at(0i64, context).unwrap(), JsValue::new(2));
1562    assert_eq!(reversed.at(1i64, context).unwrap(), JsValue::new(1));
1563    assert_eq!(reversed.at(2i64, context).unwrap(), JsValue::new(3));
1564
1565    // Original is unchanged
1566    assert_eq!(array.at(0i64, context).unwrap(), JsValue::new(3));
1567    assert_eq!(array.at(1i64, context).unwrap(), JsValue::new(1));
1568    assert_eq!(array.at(2i64, context).unwrap(), JsValue::new(2));
1569}
1570
1571#[test]
1572fn typed_array_to_sorted() {
1573    let context = &mut Context::default();
1574    let array = JsUint8Array::from_iter(vec![3u8, 1, 2], context).unwrap();
1575
1576    let sorted = array.to_sorted(None, context).unwrap();
1577
1578    // New array is sorted
1579    assert_eq!(sorted.at(0i64, context).unwrap(), JsValue::new(1));
1580    assert_eq!(sorted.at(1i64, context).unwrap(), JsValue::new(2));
1581    assert_eq!(sorted.at(2i64, context).unwrap(), JsValue::new(3));
1582
1583    // Original is unchanged
1584    assert_eq!(array.at(0i64, context).unwrap(), JsValue::new(3));
1585    assert_eq!(array.at(1i64, context).unwrap(), JsValue::new(1));
1586    assert_eq!(array.at(2i64, context).unwrap(), JsValue::new(2));
1587}
1588
1589#[test]
1590fn typed_array_to_locale_string() {
1591    let context = &mut Context::default();
1592    let array = JsUint8Array::from_iter(vec![1u8, 2, 3], context).unwrap();
1593
1594    let result = array.to_locale_string(None, None, context).unwrap();
1595
1596    let result_str = result
1597        .as_string()
1598        .expect("toLocaleString should return a string");
1599    assert!(
1600        result_str.to_std_string_escaped().contains('1'),
1601        "result should contain element values"
1602    );
1603}