Skip to main content

boa_ast/
source.rs

1use std::ops::ControlFlow;
2
3use boa_interner::{Interner, Sym, ToIndentedString};
4
5use crate::{
6    ModuleItemList, StatementList,
7    scope::Scope,
8    scope_analyzer::{
9        EvalDeclarationBindings, analyze_binding_escapes, collect_bindings,
10        eval_declaration_instantiation_scope, optimize_scope_indices,
11    },
12    visitor::{VisitWith, Visitor, VisitorMut},
13};
14
15/// A Script source.
16///
17/// More information:
18///  - [ECMAScript reference][spec]
19///
20/// [spec]: https://tc39.es/ecma262/#sec-scripts
21#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22#[derive(Clone, Debug, Default)]
23pub struct Script {
24    statements: StatementList,
25}
26
27impl Script {
28    /// Creates a new `ScriptNode`.
29    #[must_use]
30    pub const fn new(statements: StatementList) -> Self {
31        Self { statements }
32    }
33
34    /// Gets the list of statements of this `ScriptNode`.
35    #[must_use]
36    pub const fn statements(&self) -> &StatementList {
37        &self.statements
38    }
39
40    /// Gets a mutable reference to the list of statements of this `ScriptNode`.
41    pub fn statements_mut(&mut self) -> &mut StatementList {
42        &mut self.statements
43    }
44
45    /// Gets the strict mode.
46    #[inline]
47    #[must_use]
48    pub const fn strict(&self) -> bool {
49        self.statements.strict()
50    }
51
52    /// Analyze the scope of the script.
53    ///
54    /// # Errors
55    /// Any scope or binding errors that happened during the analysis.
56    pub fn analyze_scope(
57        &mut self,
58        scope: &Scope,
59        interner: &Interner,
60    ) -> Result<(), &'static str> {
61        collect_bindings(self, self.strict(), false, scope, interner)?;
62        analyze_binding_escapes(self, false, scope.clone(), interner)?;
63        optimize_scope_indices(self, scope);
64        Ok(())
65    }
66
67    /// Analyze the scope of the script in eval mode.
68    ///
69    /// # Errors
70    ///
71    /// Returns an error if the scope analysis fails with a syntax error.
72    pub fn analyze_scope_eval(
73        &mut self,
74        strict: bool,
75        variable_scope: &Scope,
76        lexical_scope: &Scope,
77        annex_b_function_names: &[Sym],
78        interner: &Interner,
79    ) -> Result<EvalDeclarationBindings, String> {
80        let bindings = eval_declaration_instantiation_scope(
81            self,
82            strict,
83            variable_scope,
84            lexical_scope,
85            annex_b_function_names,
86            interner,
87        )?;
88
89        if let Err(reason) = collect_bindings(self, strict, true, lexical_scope, interner) {
90            return Err(format!("Failed to analyze scope: {reason}"));
91        }
92        if let Err(reason) = analyze_binding_escapes(self, true, lexical_scope.clone(), interner) {
93            return Err(format!("Failed to analyze scope: {reason}"));
94        }
95
96        variable_scope.escape_all_bindings();
97        lexical_scope.escape_all_bindings();
98        variable_scope.reorder_binding_indices();
99        lexical_scope.reorder_binding_indices();
100        optimize_scope_indices(self, lexical_scope);
101
102        Ok(bindings)
103    }
104}
105
106impl VisitWith for Script {
107    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
108    where
109        V: Visitor<'a>,
110    {
111        self.statements.visit_with(visitor)
112    }
113
114    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
115    where
116        V: VisitorMut<'a>,
117    {
118        self.statements.visit_with_mut(visitor)
119    }
120}
121
122impl ToIndentedString for Script {
123    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
124        self.statements.to_indented_string(interner, indentation)
125    }
126}
127
128impl PartialEq for Script {
129    fn eq(&self, other: &Self) -> bool {
130        self.statements == other.statements
131    }
132}
133
134#[cfg(feature = "arbitrary")]
135impl<'a> arbitrary::Arbitrary<'a> for Script {
136    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
137        let statements = StatementList::arbitrary(u)?;
138        Ok(Self { statements })
139    }
140}
141
142/// A Module source.
143///
144/// More information:
145///  - [ECMAScript reference][spec]
146///
147/// [spec]: https://tc39.es/ecma262/#sec-modules
148#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
149#[derive(Clone, Debug, Default, PartialEq)]
150pub struct Module {
151    pub(crate) items: ModuleItemList,
152
153    #[cfg_attr(feature = "serde", serde(skip))]
154    pub(crate) scope: Scope,
155}
156
157impl Module {
158    /// Creates a new `ModuleNode`.
159    #[must_use]
160    pub fn new(items: ModuleItemList) -> Self {
161        Self {
162            items,
163            scope: Scope::default(),
164        }
165    }
166
167    /// Gets the list of items of this `ModuleNode`.
168    #[must_use]
169    pub const fn items(&self) -> &ModuleItemList {
170        &self.items
171    }
172
173    /// Gets the scope of this `ModuleNode`.
174    #[inline]
175    #[must_use]
176    pub const fn scope(&self) -> &Scope {
177        &self.scope
178    }
179
180    /// Analyze the scope of the module.
181    ///
182    /// # Errors
183    /// Any scope or binding errors that happened during the analysis.
184    pub fn analyze_scope(
185        &mut self,
186        scope: &Scope,
187        interner: &Interner,
188    ) -> Result<(), &'static str> {
189        collect_bindings(self, true, false, scope, interner)?;
190        analyze_binding_escapes(self, false, scope.clone(), interner)?;
191        optimize_scope_indices(self, &self.scope.clone());
192
193        Ok(())
194    }
195}
196
197impl VisitWith for Module {
198    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
199    where
200        V: Visitor<'a>,
201    {
202        self.items.visit_with(visitor)
203    }
204
205    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
206    where
207        V: VisitorMut<'a>,
208    {
209        self.items.visit_with_mut(visitor)
210    }
211}