boa/syntax/ast/node/statement_list/
mod.rs

1//! Statement list node.
2
3use crate::{
4    context::StrictType,
5    exec::{Executable, InterpreterState},
6    gc::{empty_trace, Finalize, Trace},
7    syntax::ast::node::{Declaration, Node},
8    BoaProfiler, Context, JsResult, JsValue,
9};
10use std::{collections::HashSet, fmt, ops::Deref, rc::Rc};
11
12#[cfg(feature = "deser")]
13use serde::{Deserialize, Serialize};
14
15#[cfg(test)]
16mod tests;
17
18/// List of statements.
19///
20/// Similar to `Node::Block` but without the braces.
21///
22/// More information:
23///  - [ECMAScript reference][spec]
24///
25/// [spec]: https://tc39.es/ecma262/#prod-StatementList
26#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
27#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
28pub struct StatementList {
29    #[cfg_attr(feature = "deser", serde(flatten))]
30    items: Box<[Node]>,
31    strict: bool,
32}
33
34impl StatementList {
35    /// Gets the list of items.
36    #[inline]
37    pub fn items(&self) -> &[Node] {
38        &self.items
39    }
40
41    /// Get the strict mode.
42    #[inline]
43    pub fn strict(&self) -> bool {
44        self.strict
45    }
46
47    /// Set the strict mode.
48    #[inline]
49    pub fn set_strict(&mut self, strict: bool) {
50        self.strict = strict;
51    }
52
53    /// Implements the display formatting with indentation.
54    pub(in crate::syntax::ast::node) fn display(
55        &self,
56        f: &mut fmt::Formatter<'_>,
57        indentation: usize,
58    ) -> fmt::Result {
59        // Print statements
60        for node in self.items.iter() {
61            // We rely on the node to add the correct indent.
62            node.display(f, indentation)?;
63
64            match node {
65                Node::Block(_) | Node::If(_) | Node::Switch(_) | Node::WhileLoop(_) => {}
66                _ => write!(f, ";")?,
67            }
68            writeln!(f)?;
69        }
70        Ok(())
71    }
72
73    pub fn lexically_declared_names(&self) -> HashSet<&str> {
74        let mut set = HashSet::new();
75        for stmt in self.items() {
76            if let Node::LetDeclList(decl_list) | Node::ConstDeclList(decl_list) = stmt {
77                for decl in decl_list.as_ref() {
78                    // It is a Syntax Error if the LexicallyDeclaredNames of StatementList contains any duplicate entries.
79                    // https://tc39.es/ecma262/#sec-block-static-semantics-early-errors
80                    match decl {
81                        Declaration::Identifier { ident, .. } => {
82                            if !set.insert(ident.as_ref()) {
83                                unreachable!("Redeclaration of {}", ident.as_ref());
84                            }
85                        }
86                        Declaration::Pattern(p) => {
87                            for ident in p.idents() {
88                                if !set.insert(ident) {
89                                    unreachable!("Redeclaration of {}", ident);
90                                }
91                            }
92                        }
93                    }
94                }
95            }
96        }
97        set
98    }
99
100    pub fn function_declared_names(&self) -> HashSet<&str> {
101        let mut set = HashSet::new();
102        for stmt in self.items() {
103            if let Node::FunctionDecl(decl) = stmt {
104                set.insert(decl.name());
105            }
106        }
107        set
108    }
109
110    pub fn var_declared_names(&self) -> HashSet<&str> {
111        let mut set = HashSet::new();
112        for stmt in self.items() {
113            if let Node::VarDeclList(decl_list) = stmt {
114                for decl in decl_list.as_ref() {
115                    match decl {
116                        Declaration::Identifier { ident, .. } => {
117                            set.insert(ident.as_ref());
118                        }
119                        Declaration::Pattern(p) => {
120                            for ident in p.idents() {
121                                set.insert(ident.as_ref());
122                            }
123                        }
124                    }
125                }
126            }
127        }
128        set
129    }
130}
131
132impl Executable for StatementList {
133    fn run(&self, context: &mut Context) -> JsResult<JsValue> {
134        let _timer = BoaProfiler::global().start_event("StatementList", "exec");
135
136        // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
137        // The return value is uninitialized, which means it defaults to Value::Undefined
138        let mut obj = JsValue::default();
139        context
140            .executor()
141            .set_current_state(InterpreterState::Executing);
142
143        let strict_before = context.strict_type();
144
145        match context.strict_type() {
146            StrictType::Off if self.strict => context.set_strict(StrictType::Function),
147            StrictType::Function if !self.strict => context.set_strict_mode_off(),
148            _ => {}
149        }
150
151        for (i, item) in self.items().iter().enumerate() {
152            let val = match item.run(context) {
153                Ok(val) => val,
154                Err(e) => {
155                    context.set_strict(strict_before);
156                    return Err(e);
157                }
158            };
159            match context.executor().get_current_state() {
160                InterpreterState::Return => {
161                    // Early return.
162                    obj = val;
163                    break;
164                }
165                InterpreterState::Break(_label) => {
166                    // Early break.
167                    break;
168                }
169                InterpreterState::Continue(_label) => {
170                    break;
171                }
172                InterpreterState::Executing => {
173                    // Continue execution
174                }
175            }
176            if i + 1 == self.items().len() {
177                obj = val;
178            }
179        }
180
181        context.set_strict(strict_before);
182
183        Ok(obj)
184    }
185}
186
187impl<T> From<T> for StatementList
188where
189    T: Into<Box<[Node]>>,
190{
191    fn from(stm: T) -> Self {
192        Self {
193            items: stm.into(),
194            strict: false,
195        }
196    }
197}
198
199impl fmt::Display for StatementList {
200    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
201        self.display(f, 0)
202    }
203}
204
205// List of statements wrapped with Rc. We need this for self mutating functions.
206// Since we need to cheaply clone the function body and drop the borrow of the function object to
207// mutably borrow the function object and call this cloned function body
208#[derive(Clone, Debug, Finalize, PartialEq)]
209pub struct RcStatementList(Rc<StatementList>);
210
211impl Deref for RcStatementList {
212    type Target = StatementList;
213    fn deref(&self) -> &Self::Target {
214        &self.0
215    }
216}
217
218impl From<StatementList> for RcStatementList {
219    #[inline]
220    fn from(statementlist: StatementList) -> Self {
221        Self(Rc::from(statementlist))
222    }
223}
224
225// SAFETY: This is safe for types not containing any `Trace` types.
226unsafe impl Trace for RcStatementList {
227    empty_trace!();
228}