Skip to main content

boa_ast/function/
ordinary_function.rs

1use super::{FormalParameterList, FunctionBody};
2use crate::{
3    Declaration, LinearSpan, LinearSpanIgnoreEq, Span, Spanned, block_to_string,
4    expression::{Expression, Identifier},
5    join_nodes,
6    operations::{ContainsSymbol, contains},
7    scope::{FunctionScopes, Scope},
8    scope_analyzer::{
9        analyze_binding_escapes, collect_bindings, optimize_scope_indices_function_constructor,
10    },
11    visitor::{VisitWith, Visitor, VisitorMut},
12};
13use boa_interner::{Interner, ToIndentedString};
14use core::{fmt::Write as _, ops::ControlFlow};
15
16/// A function declaration.
17///
18/// More information:
19///  - [ECMAScript reference][spec]
20///  - [MDN documentation][mdn]
21///
22/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration
23/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
24#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
25#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
26#[derive(Clone, Debug, PartialEq)]
27pub struct FunctionDeclaration {
28    name: Identifier,
29    pub(crate) parameters: FormalParameterList,
30    pub(crate) body: FunctionBody,
31    pub(crate) contains_direct_eval: bool,
32
33    #[cfg_attr(feature = "serde", serde(skip))]
34    pub(crate) scopes: FunctionScopes,
35    linear_span: LinearSpanIgnoreEq,
36}
37
38impl FunctionDeclaration {
39    /// Creates a new function declaration.
40    #[inline]
41    #[must_use]
42    pub fn new(
43        name: Identifier,
44        parameters: FormalParameterList,
45        body: FunctionBody,
46        linear_span: LinearSpan,
47    ) -> Self {
48        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
49            || contains(&body, ContainsSymbol::DirectEval);
50        Self {
51            name,
52            parameters,
53            body,
54            contains_direct_eval,
55            scopes: FunctionScopes::default(),
56            linear_span: linear_span.into(),
57        }
58    }
59
60    /// Gets the name of the function declaration.
61    #[inline]
62    #[must_use]
63    pub const fn name(&self) -> Identifier {
64        self.name
65    }
66
67    /// Gets the list of parameters of the function declaration.
68    #[inline]
69    #[must_use]
70    pub const fn parameters(&self) -> &FormalParameterList {
71        &self.parameters
72    }
73
74    /// Gets the body of the function declaration.
75    #[inline]
76    #[must_use]
77    pub const fn body(&self) -> &FunctionBody {
78        &self.body
79    }
80
81    /// Gets the scopes of the function declaration.
82    #[inline]
83    #[must_use]
84    pub const fn scopes(&self) -> &FunctionScopes {
85        &self.scopes
86    }
87
88    /// Gets linear span of the function declaration.
89    #[inline]
90    #[must_use]
91    pub const fn linear_span(&self) -> LinearSpan {
92        self.linear_span.0
93    }
94
95    /// Returns `true` if the function declaration contains a direct call to `eval`.
96    #[inline]
97    #[must_use]
98    pub const fn contains_direct_eval(&self) -> bool {
99        self.contains_direct_eval
100    }
101}
102
103impl ToIndentedString for FunctionDeclaration {
104    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
105        format!(
106            "function {}({}) {}",
107            interner.resolve_expect(self.name.sym()),
108            join_nodes(interner, self.parameters.as_ref()),
109            block_to_string(&self.body.statements, interner, indentation)
110        )
111    }
112}
113
114impl VisitWith for FunctionDeclaration {
115    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
116    where
117        V: Visitor<'a>,
118    {
119        visitor.visit_identifier(&self.name)?;
120        visitor.visit_formal_parameter_list(&self.parameters)?;
121        visitor.visit_function_body(&self.body)
122    }
123
124    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
125    where
126        V: VisitorMut<'a>,
127    {
128        visitor.visit_identifier_mut(&mut self.name)?;
129        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
130        visitor.visit_function_body_mut(&mut self.body)
131    }
132}
133
134impl From<FunctionDeclaration> for Declaration {
135    #[inline]
136    fn from(f: FunctionDeclaration) -> Self {
137        Self::FunctionDeclaration(f)
138    }
139}
140
141/// A function expression.
142///
143/// More information:
144///  - [ECMAScript reference][spec]
145///  - [MDN documentation][mdn]
146///
147/// [spec]: https://tc39.es/ecma262/#prod-FunctionExpression
148/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions
149#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
150#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
151#[derive(Clone, Debug)]
152pub struct FunctionExpression {
153    pub(crate) name: Option<Identifier>,
154    pub(crate) parameters: FormalParameterList,
155    pub(crate) body: FunctionBody,
156    pub(crate) has_binding_identifier: bool,
157    pub(crate) contains_direct_eval: bool,
158
159    #[cfg_attr(feature = "serde", serde(skip))]
160    pub(crate) name_scope: Option<Scope>,
161
162    #[cfg_attr(feature = "serde", serde(skip))]
163    pub(crate) scopes: FunctionScopes,
164
165    span: Span,
166
167    linear_span: Option<LinearSpan>,
168}
169
170impl PartialEq for FunctionExpression {
171    fn eq(&self, other: &Self) -> bool {
172        // all fields except for `linear_span`
173        self.name == other.name
174            && self.parameters == other.parameters
175            && self.body == other.body
176            && self.has_binding_identifier == other.has_binding_identifier
177            && self.contains_direct_eval == other.contains_direct_eval
178            && self.name_scope == other.name_scope
179            && self.scopes == other.scopes
180            && self.span == other.span
181    }
182}
183
184impl Spanned for FunctionExpression {
185    #[inline]
186    fn span(&self) -> Span {
187        self.span
188    }
189}
190
191impl FunctionExpression {
192    /// Creates a new function expression.
193    #[inline]
194    #[must_use]
195    pub fn new(
196        name: Option<Identifier>,
197        parameters: FormalParameterList,
198        body: FunctionBody,
199        linear_span: Option<LinearSpan>,
200        has_binding_identifier: bool,
201        span: Span,
202    ) -> Self {
203        let contains_direct_eval = contains(&parameters, ContainsSymbol::DirectEval)
204            || contains(&body, ContainsSymbol::DirectEval);
205        Self {
206            name,
207            parameters,
208            body,
209            has_binding_identifier,
210            name_scope: None,
211            contains_direct_eval,
212            scopes: FunctionScopes::default(),
213            #[allow(clippy::redundant_closure_for_method_calls)]
214            linear_span,
215            span,
216        }
217    }
218
219    /// Gets the name of the function expression.
220    #[inline]
221    #[must_use]
222    pub const fn name(&self) -> Option<Identifier> {
223        self.name
224    }
225
226    /// Gets the list of parameters of the function expression.
227    #[inline]
228    #[must_use]
229    pub const fn parameters(&self) -> &FormalParameterList {
230        &self.parameters
231    }
232
233    /// Gets the body of the function expression.
234    #[inline]
235    #[must_use]
236    pub const fn body(&self) -> &FunctionBody {
237        &self.body
238    }
239
240    /// Returns whether the function expression has a binding identifier.
241    #[inline]
242    #[must_use]
243    pub const fn has_binding_identifier(&self) -> bool {
244        self.has_binding_identifier
245    }
246
247    /// Gets the name scope of the function expression.
248    #[inline]
249    #[must_use]
250    pub const fn name_scope(&self) -> Option<&Scope> {
251        self.name_scope.as_ref()
252    }
253
254    /// Gets the scopes of the function expression.
255    #[inline]
256    #[must_use]
257    pub const fn scopes(&self) -> &FunctionScopes {
258        &self.scopes
259    }
260
261    /// Gets linear span of the function declaration.
262    #[inline]
263    #[must_use]
264    pub const fn linear_span(&self) -> Option<LinearSpan> {
265        self.linear_span
266    }
267
268    /// Returns `true` if the function expression contains a direct call to `eval`.
269    #[inline]
270    #[must_use]
271    pub const fn contains_direct_eval(&self) -> bool {
272        self.contains_direct_eval
273    }
274
275    /// Analyze the scope of the function expression.
276    ///
277    /// # Errors
278    /// Any scope or binding errors that happened during the analysis.
279    pub fn analyze_scope(
280        &mut self,
281        strict: bool,
282        scope: &Scope,
283        interner: &Interner,
284    ) -> Result<(), &'static str> {
285        collect_bindings(self, strict, false, scope, interner)?;
286        analyze_binding_escapes(self, false, scope.clone(), interner)?;
287        optimize_scope_indices_function_constructor(self, scope);
288        Ok(())
289    }
290}
291
292impl ToIndentedString for FunctionExpression {
293    fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
294        let mut buf = "function".to_owned();
295        if self.has_binding_identifier
296            && let Some(name) = self.name
297        {
298            let _ = write!(buf, " {}", interner.resolve_expect(name.sym()));
299        }
300        let _ = write!(
301            buf,
302            "({}) {}",
303            join_nodes(interner, self.parameters.as_ref()),
304            block_to_string(&self.body.statements, interner, indentation)
305        );
306
307        buf
308    }
309}
310
311impl From<FunctionExpression> for Expression {
312    #[inline]
313    fn from(expr: FunctionExpression) -> Self {
314        Self::FunctionExpression(expr)
315    }
316}
317
318impl VisitWith for FunctionExpression {
319    fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
320    where
321        V: Visitor<'a>,
322    {
323        if let Some(ident) = &self.name {
324            visitor.visit_identifier(ident)?;
325        }
326        visitor.visit_formal_parameter_list(&self.parameters)?;
327        visitor.visit_function_body(&self.body)
328    }
329
330    fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
331    where
332        V: VisitorMut<'a>,
333    {
334        if let Some(ident) = &mut self.name {
335            visitor.visit_identifier_mut(ident)?;
336        }
337        visitor.visit_formal_parameter_list_mut(&mut self.parameters)?;
338        visitor.visit_function_body_mut(&mut self.body)
339    }
340}