boa/syntax/ast/node/iteration/for_of_loop/
mod.rs

1use crate::{
2    builtins::iterable::get_iterator,
3    environment::{
4        declarative_environment_record::DeclarativeEnvironmentRecord,
5        lexical_environment::VariableScope,
6    },
7    exec::{Executable, InterpreterState},
8    gc::{Finalize, Trace},
9    syntax::ast::node::{Declaration, Node},
10    BoaProfiler, Context, JsResult, JsValue,
11};
12use std::fmt;
13
14#[cfg(feature = "deser")]
15use serde::{Deserialize, Serialize};
16
17#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
18#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
19pub struct ForOfLoop {
20    variable: Box<Node>,
21    iterable: Box<Node>,
22    body: Box<Node>,
23    label: Option<Box<str>>,
24}
25
26impl ForOfLoop {
27    pub fn new<V, I, B>(variable: V, iterable: I, body: B) -> Self
28    where
29        V: Into<Node>,
30        I: Into<Node>,
31        B: Into<Node>,
32    {
33        Self {
34            variable: Box::new(variable.into()),
35            iterable: Box::new(iterable.into()),
36            body: Box::new(body.into()),
37            label: None,
38        }
39    }
40
41    pub fn variable(&self) -> &Node {
42        &self.variable
43    }
44
45    pub fn iterable(&self) -> &Node {
46        &self.iterable
47    }
48
49    pub fn body(&self) -> &Node {
50        &self.body
51    }
52
53    pub fn label(&self) -> Option<&str> {
54        self.label.as_ref().map(Box::as_ref)
55    }
56
57    pub fn set_label(&mut self, label: Box<str>) {
58        self.label = Some(label);
59    }
60
61    pub fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result {
62        if let Some(ref label) = self.label {
63            write!(f, "{}: ", label)?;
64        }
65        write!(f, "for ({} of {}) ", self.variable, self.iterable)?;
66        self.body().display(f, indentation)
67    }
68}
69
70impl fmt::Display for ForOfLoop {
71    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
72        self.display(f, 0)
73    }
74}
75
76impl From<ForOfLoop> for Node {
77    fn from(for_of: ForOfLoop) -> Node {
78        Self::ForOfLoop(for_of)
79    }
80}
81
82impl Executable for ForOfLoop {
83    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
84        let _timer = BoaProfiler::global().start_event("ForOf", "exec");
85        let iterable = self.iterable().run(context)?;
86        let iterator = get_iterator(&iterable, context)?;
87        let mut result = JsValue::undefined();
88
89        loop {
90            {
91                let env = context.get_current_environment();
92                context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
93            }
94            let iterator_result = iterator.next(context)?;
95            if iterator_result.done {
96                context.pop_environment();
97                break;
98            }
99            let next_result = iterator_result.value;
100
101            match self.variable() {
102                Node::Identifier(ref name) => {
103                    if context.has_binding(name.as_ref())? {
104                        // Binding already exists
105                        context.set_mutable_binding(
106                            name.as_ref(),
107                            next_result.clone(),
108                            context.strict(),
109                        )?;
110                    } else {
111                        context.create_mutable_binding(
112                            name.as_ref(),
113                            true,
114                            VariableScope::Function,
115                        )?;
116                        context.initialize_binding(name.as_ref(), next_result)?;
117                    }
118                }
119                Node::VarDeclList(ref list) => match list.as_ref() {
120                    [var] => {
121                        if var.init().is_some() {
122                            return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
123                        }
124
125                        match &var {
126                            Declaration::Identifier { ident, .. } => {
127                                if context.has_binding(ident.as_ref())? {
128                                    context.set_mutable_binding(
129                                        ident.as_ref(),
130                                        next_result,
131                                        context.strict(),
132                                    )?;
133                                } else {
134                                    context.create_mutable_binding(
135                                        ident.as_ref(),
136                                        false,
137                                        VariableScope::Function,
138                                    )?;
139                                    context.initialize_binding(ident.as_ref(), next_result)?;
140                                }
141                            }
142                            Declaration::Pattern(p) => {
143                                for (ident, value) in p.run(Some(next_result), context)? {
144                                    if context.has_binding(ident.as_ref())? {
145                                        context.set_mutable_binding(
146                                            ident.as_ref(),
147                                            value,
148                                            context.strict(),
149                                        )?;
150                                    } else {
151                                        context.create_mutable_binding(
152                                            ident.as_ref(),
153                                            false,
154                                            VariableScope::Function,
155                                        )?;
156                                        context.initialize_binding(ident.as_ref(), value)?;
157                                    }
158                                }
159                            }
160                        }
161                    }
162                    _ => {
163                        return context.throw_syntax_error(
164                            "only one variable can be declared in the head of a for-of loop",
165                        )
166                    }
167                },
168                Node::LetDeclList(ref list) => match list.as_ref() {
169                    [var] => {
170                        if var.init().is_some() {
171                            return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
172                        }
173
174                        match &var {
175                            Declaration::Identifier { ident, .. } => {
176                                context.create_mutable_binding(
177                                    ident.as_ref(),
178                                    false,
179                                    VariableScope::Block,
180                                )?;
181                                context.initialize_binding(ident.as_ref(), next_result)?;
182                            }
183                            Declaration::Pattern(p) => {
184                                for (ident, value) in p.run(Some(next_result), context)? {
185                                    context.create_mutable_binding(
186                                        ident.as_ref(),
187                                        false,
188                                        VariableScope::Block,
189                                    )?;
190                                    context.initialize_binding(ident.as_ref(), value)?;
191                                }
192                            }
193                        }
194                    }
195                    _ => {
196                        return context.throw_syntax_error(
197                            "only one variable can be declared in the head of a for-of loop",
198                        )
199                    }
200                },
201                Node::ConstDeclList(ref list) => match list.as_ref() {
202                    [var] => {
203                        if var.init().is_some() {
204                            return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
205                        }
206
207                        match &var {
208                            Declaration::Identifier { ident, .. } => {
209                                context.create_immutable_binding(
210                                    ident.as_ref(),
211                                    false,
212                                    VariableScope::Block,
213                                )?;
214                                context.initialize_binding(ident.as_ref(), next_result)?;
215                            }
216                            Declaration::Pattern(p) => {
217                                for (ident, value) in p.run(Some(next_result), context)? {
218                                    context.create_immutable_binding(
219                                        ident.as_ref(),
220                                        false,
221                                        VariableScope::Block,
222                                    )?;
223                                    context.initialize_binding(ident.as_ref(), value)?;
224                                }
225                            }
226                        }
227                    }
228                    _ => {
229                        return context.throw_syntax_error(
230                            "only one variable can be declared in the head of a for-of loop",
231                        )
232                    }
233                },
234                Node::Assign(_) => {
235                    return context.throw_syntax_error(
236                        "a declaration in the head of a for-of loop can't have an initializer",
237                    );
238                }
239                _ => {
240                    return context
241                        .throw_syntax_error("unknown left hand side in head of for-of loop")
242                }
243            }
244
245            result = self.body().run(context)?;
246            match context.executor().get_current_state() {
247                InterpreterState::Break(label) => {
248                    handle_state_with_labels!(self, label, context, break);
249                    break;
250                }
251                InterpreterState::Continue(label) => {
252                    handle_state_with_labels!(self, label, context, continue);
253                }
254                InterpreterState::Return => return Ok(result),
255                InterpreterState::Executing => {
256                    // Continue execution.
257                }
258            }
259            let _ = context.pop_environment();
260        }
261        Ok(result)
262    }
263}