Skip to main content

boa_ast/statement/iteration/
for_in_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...in` loop statement, as defined by the [spec].
12///
13/// [`for...in`][forin] statements loop over all enumerable string properties of an object, including
14/// inherited properties.
15///
16/// [forin]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in
17/// [spec]: https://tc39.es/ecma262/#prod-ForInOfStatement
18#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
19#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
20#[derive(Clone, Debug, PartialEq)]
21pub struct ForInLoop {
22    pub(crate) initializer: IterableLoopInitializer,
23    pub(crate) target: Expression,
24    pub(crate) body: Box<Statement>,
25    pub(crate) target_contains_direct_eval: bool,
26    pub(crate) contains_direct_eval: bool,
27
28    #[cfg_attr(feature = "serde", serde(skip))]
29    pub(crate) target_scope: Option<Scope>,
30
31    #[cfg_attr(feature = "serde", serde(skip))]
32    pub(crate) scope: Option<Scope>,
33}
34
35impl ForInLoop {
36    /// Creates a new `ForInLoop`.
37    #[inline]
38    #[must_use]
39    pub fn new(initializer: IterableLoopInitializer, target: Expression, body: Statement) -> Self {
40        let target_contains_direct_eval = contains(&target, ContainsSymbol::DirectEval);
41        let contains_direct_eval = contains(&initializer, ContainsSymbol::DirectEval)
42            || contains(&body, ContainsSymbol::DirectEval);
43        Self {
44            initializer,
45            target,
46            body: body.into(),
47            target_contains_direct_eval,
48            contains_direct_eval,
49            target_scope: None,
50            scope: None,
51        }
52    }
53
54    /// Gets the initializer of the for...in loop.
55    #[inline]
56    #[must_use]
57    pub const fn initializer(&self) -> &IterableLoopInitializer {
58        &self.initializer
59    }
60
61    /// Gets the target object of the for...in loop.
62    #[inline]
63    #[must_use]
64    pub const fn target(&self) -> &Expression {
65        &self.target
66    }
67
68    /// Gets the body of the for...in loop.
69    #[inline]
70    #[must_use]
71    pub const fn body(&self) -> &Statement {
72        &self.body
73    }
74
75    /// Returns the target scope of the for...in loop.
76    #[inline]
77    #[must_use]
78    pub const fn target_scope(&self) -> Option<&Scope> {
79        self.target_scope.as_ref()
80    }
81
82    /// Returns the scope of the for...in loop.
83    #[inline]
84    #[must_use]
85    pub const fn scope(&self) -> Option<&Scope> {
86        self.scope.as_ref()
87    }
88}
89
90impl ToIndentedString for ForInLoop {
91    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
92        let mut buf = format!(
93            "for ({} in {}) ",
94            self.initializer.to_interned_string(interner),
95            self.target.to_interned_string(interner)
96        );
97        buf.push_str(&self.body().to_indented_string(interner, indentation));
98
99        buf
100    }
101}
102
103impl From<ForInLoop> for Statement {
104    #[inline]
105    fn from(for_in: ForInLoop) -> Self {
106        Self::ForInLoop(for_in)
107    }
108}
109
110impl VisitWith for ForInLoop {
111    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
112    where
113        V: Visitor<'a>,
114    {
115        visitor.visit_iterable_loop_initializer(&self.initializer)?;
116        visitor.visit_expression(&self.target)?;
117        visitor.visit_statement(&self.body)
118    }
119
120    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
121    where
122        V: VisitorMut<'a>,
123    {
124        visitor.visit_iterable_loop_initializer_mut(&mut self.initializer)?;
125        visitor.visit_expression_mut(&mut self.target)?;
126        visitor.visit_statement_mut(&mut self.body)
127    }
128}