Skip to main content

boa_ast/statement/iteration/
for_loop.rs

1use crate::operations::{ContainsSymbol, contains};
2use crate::scope::Scope;
3use crate::visitor::{VisitWith, Visitor, VisitorMut};
4use crate::{
5    Expression,
6    declaration::{LexicalDeclaration, VarDeclaration},
7    statement::Statement,
8};
9use boa_interner::{Interner, ToIndentedString, ToInternedString};
10use core::{fmt::Write as _, ops::ControlFlow};
11
12/// The `for` statement creates a loop that consists of three optional expressions.
13///
14/// A [`for`][mdn] loop repeats until a specified condition evaluates to `false`.
15/// The JavaScript for loop is similar to the Java and C for loop.
16///
17/// More information:
18///  - [ECMAScript reference][spec]
19///
20/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
21/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
22#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
23#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
24#[derive(Clone, Debug, PartialEq)]
25pub struct ForLoop {
26    #[cfg_attr(feature = "serde", serde(flatten))]
27    pub(crate) inner: Box<InnerForLoop>,
28}
29
30impl ForLoop {
31    /// Creates a new for loop AST node.
32    #[inline]
33    #[must_use]
34    pub fn new(
35        init: Option<ForLoopInitializer>,
36        condition: Option<Expression>,
37        final_expr: Option<Expression>,
38        body: Statement,
39    ) -> Self {
40        Self {
41            inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
42        }
43    }
44
45    /// Gets the initialization node.
46    #[inline]
47    #[must_use]
48    pub const fn init(&self) -> Option<&ForLoopInitializer> {
49        self.inner.init()
50    }
51
52    /// Gets the loop condition node.
53    #[inline]
54    #[must_use]
55    pub const fn condition(&self) -> Option<&Expression> {
56        self.inner.condition()
57    }
58
59    /// Gets the final expression node.
60    #[inline]
61    #[must_use]
62    pub const fn final_expr(&self) -> Option<&Expression> {
63        self.inner.final_expr()
64    }
65
66    /// Gets the body of the for loop.
67    #[inline]
68    #[must_use]
69    pub const fn body(&self) -> &Statement {
70        self.inner.body()
71    }
72}
73
74impl ToIndentedString for ForLoop {
75    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
76        let mut buf = String::from("for (");
77        if let Some(init) = self.init() {
78            buf.push_str(&init.to_interned_string(interner));
79        }
80        buf.push_str("; ");
81        if let Some(condition) = self.condition() {
82            buf.push_str(&condition.to_interned_string(interner));
83        }
84        buf.push_str("; ");
85        if let Some(final_expr) = self.final_expr() {
86            buf.push_str(&final_expr.to_interned_string(interner));
87        }
88        let _ = write!(
89            buf,
90            ") {}",
91            self.inner.body().to_indented_string(interner, indentation)
92        );
93
94        buf
95    }
96}
97
98impl From<ForLoop> for Statement {
99    #[inline]
100    fn from(for_loop: ForLoop) -> Self {
101        Self::ForLoop(for_loop)
102    }
103}
104
105impl VisitWith for ForLoop {
106    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
107    where
108        V: Visitor<'a>,
109    {
110        if let Some(fli) = &self.inner.init {
111            visitor.visit_for_loop_initializer(fli)?;
112        }
113        if let Some(expr) = &self.inner.condition {
114            visitor.visit_expression(expr)?;
115        }
116        if let Some(expr) = &self.inner.final_expr {
117            visitor.visit_expression(expr)?;
118        }
119        visitor.visit_statement(&self.inner.body)
120    }
121
122    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
123    where
124        V: VisitorMut<'a>,
125    {
126        if let Some(fli) = &mut self.inner.init {
127            visitor.visit_for_loop_initializer_mut(fli)?;
128        }
129        if let Some(expr) = &mut self.inner.condition {
130            visitor.visit_expression_mut(expr)?;
131        }
132        if let Some(expr) = &mut self.inner.final_expr {
133            visitor.visit_expression_mut(expr)?;
134        }
135        visitor.visit_statement_mut(&mut self.inner.body)
136    }
137}
138
139/// Inner structure to avoid multiple indirections in the heap.
140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
141#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
142#[derive(Clone, Debug, PartialEq)]
143pub(crate) struct InnerForLoop {
144    pub(crate) init: Option<ForLoopInitializer>,
145    pub(crate) condition: Option<Expression>,
146    pub(crate) final_expr: Option<Expression>,
147    pub(crate) body: Statement,
148    pub(crate) contains_direct_eval: bool,
149}
150
151impl InnerForLoop {
152    /// Creates a new inner for loop.
153    #[inline]
154    fn new(
155        init: Option<ForLoopInitializer>,
156        condition: Option<Expression>,
157        final_expr: Option<Expression>,
158        body: Statement,
159    ) -> Self {
160        let mut contains_direct_eval = contains(&body, ContainsSymbol::DirectEval);
161        if let Some(init) = &init {
162            contains_direct_eval |= contains(init, ContainsSymbol::DirectEval);
163        }
164        if let Some(condition) = &condition {
165            contains_direct_eval |= contains(condition, ContainsSymbol::DirectEval);
166        }
167        if let Some(final_expr) = &final_expr {
168            contains_direct_eval |= contains(final_expr, ContainsSymbol::DirectEval);
169        }
170        Self {
171            init,
172            condition,
173            final_expr,
174            body,
175            contains_direct_eval,
176        }
177    }
178
179    /// Gets the initialization node.
180    #[inline]
181    const fn init(&self) -> Option<&ForLoopInitializer> {
182        self.init.as_ref()
183    }
184
185    /// Gets the loop condition node.
186    #[inline]
187    const fn condition(&self) -> Option<&Expression> {
188        self.condition.as_ref()
189    }
190
191    /// Gets the final expression node.
192    #[inline]
193    const fn final_expr(&self) -> Option<&Expression> {
194        self.final_expr.as_ref()
195    }
196
197    /// Gets the body of the for loop.
198    #[inline]
199    const fn body(&self) -> &Statement {
200        &self.body
201    }
202}
203
204/// A [`ForLoop`] initializer, as defined by the [spec].
205///
206/// A `ForLoop` initializer differs a lot from an
207/// [`IterableLoopInitializer`][super::IterableLoopInitializer], since it can contain any arbitrary
208/// expression instead of only accessors and patterns. Additionally, it can also contain many variable
209/// declarations instead of only one.
210///
211/// [spec]: https://tc39.es/ecma262/#prod-ForStatement
212#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
213#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
214#[derive(Clone, Debug, PartialEq)]
215pub enum ForLoopInitializer {
216    /// An expression initializer.
217    Expression(Expression),
218    /// A var declaration initializer.
219    Var(VarDeclaration),
220    /// A lexical declaration initializer.
221    Lexical(ForLoopInitializerLexical),
222}
223
224/// A lexical declaration initializer for a `ForLoop`.
225#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
226#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
227#[derive(Clone, Debug, PartialEq)]
228pub struct ForLoopInitializerLexical {
229    pub(crate) declaration: LexicalDeclaration,
230
231    #[cfg_attr(feature = "serde", serde(skip))]
232    pub(crate) scope: Scope,
233}
234
235impl ForLoopInitializerLexical {
236    /// Creates a new lexical declaration initializer.
237    #[inline]
238    #[must_use]
239    pub fn new(declaration: LexicalDeclaration, scope: Scope) -> Self {
240        Self { declaration, scope }
241    }
242
243    /// Returns the declaration of the lexical initializer.
244    #[inline]
245    #[must_use]
246    pub const fn declaration(&self) -> &LexicalDeclaration {
247        &self.declaration
248    }
249
250    /// Returns the scope of the lexical initializer.
251    #[inline]
252    #[must_use]
253    pub const fn scope(&self) -> &Scope {
254        &self.scope
255    }
256}
257
258impl ToInternedString for ForLoopInitializer {
259    fn to_interned_string(&self, interner: &Interner) -> String {
260        match self {
261            Self::Var(var) => var.to_interned_string(interner),
262            Self::Lexical(lex) => lex.declaration.to_interned_string(interner),
263            Self::Expression(expr) => expr.to_interned_string(interner),
264        }
265    }
266}
267
268impl From<Expression> for ForLoopInitializer {
269    #[inline]
270    fn from(expr: Expression) -> Self {
271        Self::Expression(expr)
272    }
273}
274
275impl From<LexicalDeclaration> for ForLoopInitializer {
276    #[inline]
277    fn from(list: LexicalDeclaration) -> Self {
278        Self::Lexical(ForLoopInitializerLexical {
279            declaration: list,
280            scope: Scope::default(),
281        })
282    }
283}
284
285impl From<VarDeclaration> for ForLoopInitializer {
286    #[inline]
287    fn from(list: VarDeclaration) -> Self {
288        Self::Var(list)
289    }
290}
291
292impl VisitWith for ForLoopInitializer {
293    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
294    where
295        V: Visitor<'a>,
296    {
297        match self {
298            Self::Expression(expr) => visitor.visit_expression(expr),
299            Self::Var(vd) => visitor.visit_var_declaration(vd),
300            Self::Lexical(ld) => visitor.visit_lexical_declaration(&ld.declaration),
301        }
302    }
303
304    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
305    where
306        V: VisitorMut<'a>,
307    {
308        match self {
309            Self::Expression(expr) => visitor.visit_expression_mut(expr),
310            Self::Var(vd) => visitor.visit_var_declaration_mut(vd),
311            Self::Lexical(ld) => visitor.visit_lexical_declaration_mut(&mut ld.declaration),
312        }
313    }
314}