Skip to main content

boa_ast/function/
async_function.rs

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