boa/builtins/iterable/
mod.rs

1use crate::{
2    builtins::{
3        regexp::regexp_string_iterator::RegExpStringIterator,
4        string::string_iterator::StringIterator, ArrayIterator, ForInIterator, MapIterator,
5        SetIterator,
6    },
7    object::{JsObject, ObjectInitializer},
8    symbol::WellKnownSymbols,
9    BoaProfiler, Context, JsResult, JsValue,
10};
11
12#[derive(Debug, Default)]
13pub struct IteratorPrototypes {
14    iterator_prototype: JsObject,
15    array_iterator: JsObject,
16    set_iterator: JsObject,
17    string_iterator: JsObject,
18    regexp_string_iterator: JsObject,
19    map_iterator: JsObject,
20    for_in_iterator: JsObject,
21}
22
23impl IteratorPrototypes {
24    pub(crate) fn init(context: &mut Context) -> Self {
25        let iterator_prototype = create_iterator_prototype(context);
26        Self {
27            array_iterator: ArrayIterator::create_prototype(
28                iterator_prototype.clone().into(),
29                context,
30            ),
31            set_iterator: SetIterator::create_prototype(iterator_prototype.clone().into(), context),
32            string_iterator: StringIterator::create_prototype(
33                iterator_prototype.clone().into(),
34                context,
35            ),
36            regexp_string_iterator: RegExpStringIterator::create_prototype(
37                iterator_prototype.clone().into(),
38                context,
39            ),
40            map_iterator: MapIterator::create_prototype(iterator_prototype.clone().into(), context),
41            for_in_iterator: ForInIterator::create_prototype(
42                iterator_prototype.clone().into(),
43                context,
44            ),
45            iterator_prototype,
46        }
47    }
48
49    #[inline]
50    pub fn array_iterator(&self) -> JsObject {
51        self.array_iterator.clone()
52    }
53
54    #[inline]
55    pub fn iterator_prototype(&self) -> JsObject {
56        self.iterator_prototype.clone()
57    }
58
59    #[inline]
60    pub fn set_iterator(&self) -> JsObject {
61        self.set_iterator.clone()
62    }
63
64    #[inline]
65    pub fn string_iterator(&self) -> JsObject {
66        self.string_iterator.clone()
67    }
68
69    #[inline]
70    pub fn regexp_string_iterator(&self) -> JsObject {
71        self.regexp_string_iterator.clone()
72    }
73
74    #[inline]
75    pub fn map_iterator(&self) -> JsObject {
76        self.map_iterator.clone()
77    }
78
79    #[inline]
80    pub fn for_in_iterator(&self) -> JsObject {
81        self.for_in_iterator.clone()
82    }
83}
84
85/// CreateIterResultObject( value, done )
86///
87/// Generates an object supporting the IteratorResult interface.
88pub fn create_iter_result_object(value: JsValue, done: bool, context: &mut Context) -> JsValue {
89    // 1. Assert: Type(done) is Boolean.
90    // 2. Let obj be ! OrdinaryObjectCreate(%Object.prototype%).
91    let obj = context.construct_object();
92
93    // 3. Perform ! CreateDataPropertyOrThrow(obj, "value", value).
94    obj.create_data_property_or_throw("value", value, context)
95        .unwrap();
96    // 4. Perform ! CreateDataPropertyOrThrow(obj, "done", done).
97    obj.create_data_property_or_throw("done", done, context)
98        .unwrap();
99    // 5. Return obj.
100    obj.into()
101}
102
103/// Get an iterator record
104pub fn get_iterator(iterable: &JsValue, context: &mut Context) -> JsResult<IteratorRecord> {
105    let iterator_function = iterable.get_field(WellKnownSymbols::iterator(), context)?;
106    if iterator_function.is_null_or_undefined() {
107        return Err(context.construct_type_error("Not an iterable"));
108    }
109    let iterator_object = context.call(&iterator_function, iterable, &[])?;
110    let next_function = iterator_object.get_field("next", context)?;
111    if next_function.is_null_or_undefined() {
112        return Err(context.construct_type_error("Could not find property `next`"));
113    }
114    Ok(IteratorRecord::new(iterator_object, next_function))
115}
116
117/// Create the %IteratorPrototype% object
118///
119/// More information:
120///  - [ECMA reference][spec]
121///
122/// [spec]: https://tc39.es/ecma262/#sec-%iteratorprototype%-object
123fn create_iterator_prototype(context: &mut Context) -> JsObject {
124    let _timer = BoaProfiler::global().start_event("Iterator Prototype", "init");
125
126    let symbol_iterator = WellKnownSymbols::iterator();
127    let iterator_prototype = ObjectInitializer::new(context)
128        .function(
129            |v, _, _| Ok(v.clone()),
130            (symbol_iterator, "[Symbol.iterator]"),
131            0,
132        )
133        .build();
134    iterator_prototype
135}
136
137#[derive(Debug)]
138pub struct IteratorRecord {
139    iterator_object: JsValue,
140    next_function: JsValue,
141}
142
143impl IteratorRecord {
144    pub fn new(iterator_object: JsValue, next_function: JsValue) -> Self {
145        Self {
146            iterator_object,
147            next_function,
148        }
149    }
150
151    /// Get the next value in the iterator
152    ///
153    /// More information:
154    ///  - [ECMA reference][spec]
155    ///
156    /// [spec]: https://tc39.es/ecma262/#sec-iteratornext
157    pub(crate) fn next(&self, context: &mut Context) -> JsResult<IteratorResult> {
158        let next = context.call(&self.next_function, &self.iterator_object, &[])?;
159        let done = next.get_field("done", context)?.to_boolean();
160
161        let value = next.get_field("value", context)?;
162        Ok(IteratorResult { value, done })
163    }
164
165    /// Cleanup the iterator
166    ///
167    /// More information:
168    ///  - [ECMA reference][spec]
169    ///
170    ///  [spec]: https://tc39.es/ecma262/#sec-iteratorclose
171    pub(crate) fn close(
172        &self,
173        completion: JsResult<JsValue>,
174        context: &mut Context,
175    ) -> JsResult<JsValue> {
176        let mut inner_result = self.iterator_object.get_field("return", context);
177
178        // 5
179        if let Ok(inner_value) = inner_result {
180            // b
181            if inner_value.is_undefined() {
182                return completion;
183            }
184            // c
185            inner_result = context.call(&inner_value, &self.iterator_object, &[]);
186        }
187
188        // 6
189        let completion = completion?;
190
191        // 7
192        let inner_result = inner_result?;
193
194        // 8
195        if !inner_result.is_object() {
196            return context.throw_type_error("`return` method of iterator didn't return an Object");
197        }
198
199        // 9
200        Ok(completion)
201    }
202}
203
204#[derive(Debug)]
205pub struct IteratorResult {
206    pub value: JsValue,
207    pub done: bool,
208}