boa/syntax/ast/node/iteration/for_of_loop/
mod.rs1use 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 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 }
258 }
259 let _ = context.pop_environment();
260 }
261 Ok(result)
262 }
263}