Skip to main content

boa_ast/statement/iteration/
for_of_loop.rs

1use crate::operations::{ContainsSymbol, contains};
2use crate::scope::Scope;
3use crate::visitor::{VisitWith, Visitor, VisitorMut};
4use crate::{
5    expression::Expression,
6    statement::{Statement, iteration::IterableLoopInitializer},
7};
8use boa_interner::{Interner, ToIndentedString, ToInternedString};
9use core::ops::ControlFlow;
10
11/// A `for...of` loop statement, as defined by the [spec].
12///
13/// [`for..of`][forof] statements loop over a sequence of values obtained from an iterable object (Array,
14/// String, Map, generators).
15///
16/// This type combines `for..of` and [`for await...of`][forawait] statements in a single structure,
17/// since `for await...of` is essentially the same statement but with async iterable objects
18/// as the source of iteration.
19///
20/// [forof]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of
21/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
22/// [forawait]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
23#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
24#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
25#[derive(Clone, Debug, PartialEq)]
26pub struct ForOfLoop {
27    pub(crate) init: IterableLoopInitializer,
28    pub(crate) iterable: Expression,
29    pub(crate) body: Box<Statement>,
30    r#await: bool,
31    pub(crate) iterable_contains_direct_eval: bool,
32    pub(crate) contains_direct_eval: bool,
33
34    #[cfg_attr(feature = "serde", serde(skip))]
35    pub(crate) iterable_scope: Option<Scope>,
36
37    #[cfg_attr(feature = "serde", serde(skip))]
38    pub(crate) scope: Option<Scope>,
39}
40
41impl ForOfLoop {
42    /// Creates a new "for of" loop AST node.
43    #[inline]
44    #[must_use]
45    pub fn new(
46        init: IterableLoopInitializer,
47        iterable: Expression,
48        body: Statement,
49        r#await: bool,
50    ) -> Self {
51        let iterable_contains_direct_eval = contains(&iterable, ContainsSymbol::DirectEval);
52        let contains_direct_eval = contains(&init, ContainsSymbol::DirectEval)
53            || contains(&body, ContainsSymbol::DirectEval);
54        Self {
55            init,
56            iterable,
57            body: body.into(),
58            iterable_contains_direct_eval,
59            contains_direct_eval,
60            r#await,
61            iterable_scope: None,
62            scope: None,
63        }
64    }
65
66    /// Gets the initializer of the for...of loop.
67    #[inline]
68    #[must_use]
69    pub const fn initializer(&self) -> &IterableLoopInitializer {
70        &self.init
71    }
72
73    /// Gets the iterable expression of the for...of loop.
74    #[inline]
75    #[must_use]
76    pub const fn iterable(&self) -> &Expression {
77        &self.iterable
78    }
79
80    /// Gets the body to execute in the for...of loop.
81    #[inline]
82    #[must_use]
83    pub const fn body(&self) -> &Statement {
84        &self.body
85    }
86
87    /// Returns true if this "for...of" loop is an "for await...of" loop.
88    #[inline]
89    #[must_use]
90    pub const fn r#await(&self) -> bool {
91        self.r#await
92    }
93
94    /// Return the iterable scope of the for...of loop.
95    #[inline]
96    #[must_use]
97    pub const fn iterable_scope(&self) -> Option<&Scope> {
98        self.iterable_scope.as_ref()
99    }
100
101    /// Return the scope of the for...of loop.
102    #[inline]
103    #[must_use]
104    pub const fn scope(&self) -> Option<&Scope> {
105        self.scope.as_ref()
106    }
107}
108
109impl ToIndentedString for ForOfLoop {
110    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
111        format!(
112            "for ({} of {}) {}",
113            self.init.to_interned_string(interner),
114            self.iterable.to_interned_string(interner),
115            self.body().to_indented_string(interner, indentation)
116        )
117    }
118}
119
120impl From<ForOfLoop> for Statement {
121    #[inline]
122    fn from(for_of: ForOfLoop) -> Self {
123        Self::ForOfLoop(for_of)
124    }
125}
126
127impl VisitWith for ForOfLoop {
128    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
129    where
130        V: Visitor<'a>,
131    {
132        visitor.visit_iterable_loop_initializer(&self.init)?;
133        visitor.visit_expression(&self.iterable)?;
134        visitor.visit_statement(&self.body)
135    }
136
137    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
138    where
139        V: VisitorMut<'a>,
140    {
141        visitor.visit_iterable_loop_initializer_mut(&mut self.init)?;
142        visitor.visit_expression_mut(&mut self.iterable)?;
143        visitor.visit_statement_mut(&mut self.body)
144    }
145}