boa/syntax/ast/node/iteration/for_loop/
mod.rs

1use crate::{
2    environment::declarative_environment_record::DeclarativeEnvironmentRecord,
3    exec::{Executable, InterpreterState},
4    gc::{Finalize, Trace},
5    syntax::ast::node::Node,
6    BoaProfiler, Context, JsResult, JsValue,
7};
8use std::fmt;
9
10#[cfg(feature = "deser")]
11use serde::{Deserialize, Serialize};
12
13/// The `for` statement creates a loop that consists of three optional expressions.
14///
15/// A `for` loop repeats until a specified condition evaluates to `false`.
16/// The JavaScript for loop is similar to the Java and C for loop.
17///
18/// More information:
19///  - [ECMAScript reference][spec]
20///  - [MDN documentation][mdn]
21///
22/// [spec]: https://tc39.es/ecma262/#prod-ForDeclaration
23/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for
24#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
25#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
26pub struct ForLoop {
27    #[cfg_attr(feature = "deser", serde(flatten))]
28    inner: Box<InnerForLoop>,
29    label: Option<Box<str>>,
30}
31
32impl ForLoop {
33    /// Creates a new for loop AST node.
34    pub(in crate::syntax) fn new<I, C, E, B>(init: I, condition: C, final_expr: E, body: B) -> Self
35    where
36        I: Into<Option<Node>>,
37        C: Into<Option<Node>>,
38        E: Into<Option<Node>>,
39        B: Into<Node>,
40    {
41        Self {
42            inner: Box::new(InnerForLoop::new(init, condition, final_expr, body)),
43            label: None,
44        }
45    }
46
47    /// Gets the initialization node.
48    pub fn init(&self) -> Option<&Node> {
49        self.inner.init()
50    }
51
52    /// Gets the loop condition node.
53    pub fn condition(&self) -> Option<&Node> {
54        self.inner.condition()
55    }
56
57    /// Gets the final expression node.
58    pub fn final_expr(&self) -> Option<&Node> {
59        self.inner.final_expr()
60    }
61
62    /// Gets the body of the for loop.
63    pub fn body(&self) -> &Node {
64        self.inner.body()
65    }
66
67    pub(in crate::syntax::ast::node) fn display(
68        &self,
69        f: &mut fmt::Formatter<'_>,
70        indentation: usize,
71    ) -> fmt::Result {
72        if let Some(ref label) = self.label {
73            write!(f, "{}: ", label)?;
74        }
75        f.write_str("for (")?;
76        if let Some(init) = self.init() {
77            fmt::Display::fmt(init, f)?;
78        }
79        f.write_str("; ")?;
80        if let Some(condition) = self.condition() {
81            fmt::Display::fmt(condition, f)?;
82        }
83        f.write_str("; ")?;
84        if let Some(final_expr) = self.final_expr() {
85            fmt::Display::fmt(final_expr, f)?;
86        }
87        write!(f, ") ")?;
88        self.inner.body().display(f, indentation)
89    }
90
91    pub fn label(&self) -> Option<&str> {
92        self.label.as_ref().map(Box::as_ref)
93    }
94
95    pub fn set_label(&mut self, label: Box<str>) {
96        self.label = Some(label);
97    }
98}
99
100impl Executable for ForLoop {
101    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
102        // Create the block environment.
103        let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
104        {
105            let env = context.get_current_environment();
106            context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
107        }
108
109        if let Some(init) = self.init() {
110            init.run(context)?;
111        }
112
113        while self
114            .condition()
115            .map(|cond| cond.run(context).map(|v| v.to_boolean()))
116            .transpose()?
117            .unwrap_or(true)
118        {
119            let result = self.body().run(context)?;
120
121            match context.executor().get_current_state() {
122                InterpreterState::Break(label) => {
123                    handle_state_with_labels!(self, label, context, break);
124                    break;
125                }
126                InterpreterState::Continue(label) => {
127                    handle_state_with_labels!(self, label, context, continue);
128                }
129
130                InterpreterState::Return => {
131                    return Ok(result);
132                }
133                InterpreterState::Executing => {
134                    // Continue execution.
135                }
136            }
137
138            if let Some(final_expr) = self.final_expr() {
139                final_expr.run(context)?;
140            }
141        }
142
143        // pop the block env
144        let _ = context.pop_environment();
145
146        Ok(JsValue::undefined())
147    }
148}
149
150impl fmt::Display for ForLoop {
151    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152        self.display(f, 0)
153    }
154}
155
156impl From<ForLoop> for Node {
157    fn from(for_loop: ForLoop) -> Self {
158        Self::ForLoop(for_loop)
159    }
160}
161
162/// Inner structure to avoid multiple indirections in the heap.
163#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
164#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
165struct InnerForLoop {
166    init: Option<Node>,
167    condition: Option<Node>,
168    final_expr: Option<Node>,
169    body: Node,
170}
171
172impl InnerForLoop {
173    /// Creates a new inner for loop.
174    fn new<I, C, E, B>(init: I, condition: C, final_expr: E, body: B) -> Self
175    where
176        I: Into<Option<Node>>,
177        C: Into<Option<Node>>,
178        E: Into<Option<Node>>,
179        B: Into<Node>,
180    {
181        Self {
182            init: init.into(),
183            condition: condition.into(),
184            final_expr: final_expr.into(),
185            body: body.into(),
186        }
187    }
188
189    /// Gets the initialization node.
190    fn init(&self) -> Option<&Node> {
191        self.init.as_ref()
192    }
193
194    /// Gets the loop condition node.
195    fn condition(&self) -> Option<&Node> {
196        self.condition.as_ref()
197    }
198
199    /// Gets the final expression node.
200    fn final_expr(&self) -> Option<&Node> {
201        self.final_expr.as_ref()
202    }
203
204    /// Gets the body of the for loop.
205    fn body(&self) -> &Node {
206        &self.body
207    }
208}