boa_ast/statement/iteration/
for_loop.rs1use 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#[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 #[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 #[inline]
47 #[must_use]
48 pub const fn init(&self) -> Option<&ForLoopInitializer> {
49 self.inner.init()
50 }
51
52 #[inline]
54 #[must_use]
55 pub const fn condition(&self) -> Option<&Expression> {
56 self.inner.condition()
57 }
58
59 #[inline]
61 #[must_use]
62 pub const fn final_expr(&self) -> Option<&Expression> {
63 self.inner.final_expr()
64 }
65
66 #[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#[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 #[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 #[inline]
181 const fn init(&self) -> Option<&ForLoopInitializer> {
182 self.init.as_ref()
183 }
184
185 #[inline]
187 const fn condition(&self) -> Option<&Expression> {
188 self.condition.as_ref()
189 }
190
191 #[inline]
193 const fn final_expr(&self) -> Option<&Expression> {
194 self.final_expr.as_ref()
195 }
196
197 #[inline]
199 const fn body(&self) -> &Statement {
200 &self.body
201 }
202}
203
204#[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 Expression(Expression),
218 Var(VarDeclaration),
220 Lexical(ForLoopInitializerLexical),
222}
223
224#[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 #[inline]
238 #[must_use]
239 pub fn new(declaration: LexicalDeclaration, scope: Scope) -> Self {
240 Self { declaration, scope }
241 }
242
243 #[inline]
245 #[must_use]
246 pub const fn declaration(&self) -> &LexicalDeclaration {
247 &self.declaration
248 }
249
250 #[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}