boa/builtins/array/
array_iterator.rs1use crate::{
2 builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
3 gc::{Finalize, Trace},
4 object::{JsObject, ObjectData},
5 property::{PropertyDescriptor, PropertyNameKind},
6 symbol::WellKnownSymbols,
7 BoaProfiler, Context, JsResult,
8};
9
10#[derive(Debug, Clone, Finalize, Trace)]
17pub struct ArrayIterator {
18 array: JsValue,
19 next_index: u32,
20 kind: PropertyNameKind,
21}
22
23impl ArrayIterator {
24 pub(crate) const NAME: &'static str = "ArrayIterator";
25
26 fn new(array: JsValue, kind: PropertyNameKind) -> Self {
27 ArrayIterator {
28 array,
29 kind,
30 next_index: 0,
31 }
32 }
33
34 pub(crate) fn create_array_iterator(
43 array: JsValue,
44 kind: PropertyNameKind,
45 context: &Context,
46 ) -> JsValue {
47 let array_iterator = JsValue::new_object(context);
48 array_iterator.set_data(ObjectData::array_iterator(Self::new(array, kind)));
49 array_iterator
50 .as_object()
51 .expect("array iterator object")
52 .set_prototype_instance(context.iterator_prototypes().array_iterator().into());
53 array_iterator
54 }
55
56 pub(crate) fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
65 if let JsValue::Object(ref object) = this {
66 let mut object = object.borrow_mut();
67 if let Some(array_iterator) = object.as_array_iterator_mut() {
68 let index = array_iterator.next_index;
69 if array_iterator.array.is_undefined() {
70 return Ok(create_iter_result_object(
71 JsValue::undefined(),
72 true,
73 context,
74 ));
75 }
76 let len = array_iterator
77 .array
78 .get_field("length", context)?
79 .as_number()
80 .ok_or_else(|| context.construct_type_error("Not an array"))?
81 as u32;
82 if array_iterator.next_index >= len {
83 array_iterator.array = JsValue::undefined();
84 return Ok(create_iter_result_object(
85 JsValue::undefined(),
86 true,
87 context,
88 ));
89 }
90 array_iterator.next_index = index + 1;
91 return match array_iterator.kind {
92 PropertyNameKind::Key => {
93 Ok(create_iter_result_object(index.into(), false, context))
94 }
95 PropertyNameKind::Value => {
96 let element_value = array_iterator.array.get_field(index, context)?;
97 Ok(create_iter_result_object(element_value, false, context))
98 }
99 PropertyNameKind::KeyAndValue => {
100 let element_value = array_iterator.array.get_field(index, context)?;
101 let result =
102 Array::create_array_from_list([index.into(), element_value], context);
103 Ok(create_iter_result_object(result.into(), false, context))
104 }
105 };
106 }
107 }
108 context.throw_type_error("`this` is not an ArrayIterator")
109 }
110
111 pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
118 let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
119
120 let array_iterator = context.construct_object();
122 make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
123 array_iterator.set_prototype_instance(iterator_prototype);
124
125 let to_string_tag = WellKnownSymbols::to_string_tag();
126 let to_string_tag_property = PropertyDescriptor::builder()
127 .value("Array Iterator")
128 .writable(false)
129 .enumerable(false)
130 .configurable(true);
131 array_iterator.insert(to_string_tag, to_string_tag_property);
132 array_iterator
133 }
134}