Skip to content

Commit dc3b09a

Browse files
committed
Implement async arrow functions (#2393)
This Pull Request fixes #1805. It changes the following: - Implement async arrow function parsing and execution. - Handle special case when a function expressions binding identifier need to be bound in the function body. - Implement special silent ignored assignment for the above case. - Fix issue with getting the correct promise capability for function returns. - Complete function object `toString` todo. I will fix the two failing assignmenttargettype tests in a follow up PR.
1 parent 49a5867 commit dc3b09a

File tree

39 files changed

+957
-225
lines changed

39 files changed

+957
-225
lines changed

boa_ast/src/expression/mod.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ use self::{
1919
};
2020

2121
use super::{
22-
function::FormalParameterList,
2322
function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator},
23+
function::{AsyncArrowFunction, FormalParameterList},
2424
Statement,
2525
};
2626

@@ -88,6 +88,9 @@ pub enum Expression {
8888
/// See [`ArrowFunction`].
8989
ArrowFunction(ArrowFunction),
9090

91+
/// See [`AsyncArrowFunction`].
92+
AsyncArrowFunction(AsyncArrowFunction),
93+
9194
/// See [`Generator`].
9295
Generator(Generator),
9396

@@ -168,6 +171,7 @@ impl Expression {
168171
Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation),
169172
Self::Spread(sp) => sp.to_interned_string(interner),
170173
Self::Function(f) => f.to_indented_string(interner, indentation),
174+
Self::AsyncArrowFunction(f) => f.to_indented_string(interner, indentation),
171175
Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation),
172176
Self::Class(cl) => cl.to_indented_string(interner, indentation),
173177
Self::Generator(gen) => gen.to_indented_string(interner, indentation),
@@ -219,6 +223,7 @@ impl VisitWith for Expression {
219223
Expression::Spread(sp) => visitor.visit_spread(sp),
220224
Expression::Function(f) => visitor.visit_function(f),
221225
Expression::ArrowFunction(af) => visitor.visit_arrow_function(af),
226+
Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af),
222227
Expression::Generator(g) => visitor.visit_generator(g),
223228
Expression::AsyncFunction(af) => visitor.visit_async_function(af),
224229
Expression::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
@@ -256,6 +261,7 @@ impl VisitWith for Expression {
256261
Expression::Spread(sp) => visitor.visit_spread_mut(sp),
257262
Expression::Function(f) => visitor.visit_function_mut(f),
258263
Expression::ArrowFunction(af) => visitor.visit_arrow_function_mut(af),
264+
Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af),
259265
Expression::Generator(g) => visitor.visit_generator_mut(g),
260266
Expression::AsyncFunction(af) => visitor.visit_async_function_mut(af),
261267
Expression::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
use std::ops::ControlFlow;
2+
3+
use super::FormalParameterList;
4+
use crate::try_break;
5+
use crate::visitor::{VisitWith, Visitor, VisitorMut};
6+
use crate::{
7+
expression::{Expression, Identifier},
8+
join_nodes, StatementList,
9+
};
10+
use boa_interner::{Interner, ToIndentedString};
11+
12+
/// An async arrow function expression, as defined by the [spec].
13+
///
14+
/// An [async arrow function][mdn] expression is a syntactically compact alternative to a regular function
15+
/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as
16+
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
17+
/// used with new.
18+
///
19+
/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction
20+
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
21+
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
22+
#[derive(Clone, Debug, PartialEq)]
23+
pub struct AsyncArrowFunction {
24+
name: Option<Identifier>,
25+
parameters: FormalParameterList,
26+
body: StatementList,
27+
}
28+
29+
impl AsyncArrowFunction {
30+
/// Creates a new `AsyncArrowFunction` AST Expression.
31+
#[inline]
32+
#[must_use]
33+
pub fn new(
34+
name: Option<Identifier>,
35+
parameters: FormalParameterList,
36+
body: StatementList,
37+
) -> Self {
38+
Self {
39+
name,
40+
parameters,
41+
body,
42+
}
43+
}
44+
45+
/// Gets the name of the function declaration.
46+
#[inline]
47+
#[must_use]
48+
pub fn name(&self) -> Option<Identifier> {
49+
self.name
50+
}
51+
52+
/// Sets the name of the function declaration.
53+
//#[inline]
54+
//#[must_use]
55+
//pub fn set_name(&mut self, name: Option<Identifier>) {
56+
// self.name = name;
57+
//}
58+
59+
/// Gets the list of parameters of the arrow function.
60+
#[inline]
61+
#[must_use]
62+
pub fn parameters(&self) -> &FormalParameterList {
63+
&self.parameters
64+
}
65+
66+
/// Gets the body of the arrow function.
67+
#[inline]
68+
#[must_use]
69+
pub fn body(&self) -> &StatementList {
70+
&self.body
71+
}
72+
}
73+
74+
impl ToIndentedString for AsyncArrowFunction {
75+
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
76+
let mut buf = format!("async ({}", join_nodes(interner, self.parameters.as_ref()));
77+
if self.body().statements().is_empty() {
78+
buf.push_str(") => {}");
79+
} else {
80+
buf.push_str(&format!(
81+
") => {{\n{}{}}}",
82+
self.body.to_indented_string(interner, indentation + 1),
83+
" ".repeat(indentation)
84+
));
85+
}
86+
buf
87+
}
88+
}
89+
90+
impl From<AsyncArrowFunction> for Expression {
91+
fn from(decl: AsyncArrowFunction) -> Self {
92+
Self::AsyncArrowFunction(decl)
93+
}
94+
}
95+
96+
impl VisitWith for AsyncArrowFunction {
97+
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
98+
where
99+
V: Visitor<'a>,
100+
{
101+
if let Some(ident) = &self.name {
102+
try_break!(visitor.visit_identifier(ident));
103+
}
104+
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
105+
visitor.visit_statement_list(&self.body)
106+
}
107+
108+
fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
109+
where
110+
V: VisitorMut<'a>,
111+
{
112+
if let Some(ident) = &mut self.name {
113+
try_break!(visitor.visit_identifier_mut(ident));
114+
}
115+
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
116+
visitor.visit_statement_list_mut(&mut self.body)
117+
}
118+
}

boa_ast/src/function/async_function.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct AsyncFunction {
2525
name: Option<Identifier>,
2626
parameters: FormalParameterList,
2727
body: StatementList,
28+
has_binding_identifier: bool,
2829
}
2930

3031
impl AsyncFunction {
@@ -35,11 +36,13 @@ impl AsyncFunction {
3536
name: Option<Identifier>,
3637
parameters: FormalParameterList,
3738
body: StatementList,
39+
has_binding_identifier: bool,
3840
) -> Self {
3941
Self {
4042
name,
4143
parameters,
4244
body,
45+
has_binding_identifier,
4346
}
4447
}
4548

@@ -63,6 +66,13 @@ impl AsyncFunction {
6366
pub fn body(&self) -> &StatementList {
6467
&self.body
6568
}
69+
70+
/// Returns whether the function expression has a binding identifier.
71+
#[inline]
72+
#[must_use]
73+
pub fn has_binding_identifier(&self) -> bool {
74+
self.has_binding_identifier
75+
}
6676
}
6777

6878
impl ToIndentedString for AsyncFunction {

boa_ast/src/function/async_generator.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub struct AsyncGenerator {
2424
name: Option<Identifier>,
2525
parameters: FormalParameterList,
2626
body: StatementList,
27+
has_binding_identifier: bool,
2728
}
2829

2930
impl AsyncGenerator {
@@ -34,11 +35,13 @@ impl AsyncGenerator {
3435
name: Option<Identifier>,
3536
parameters: FormalParameterList,
3637
body: StatementList,
38+
has_binding_identifier: bool,
3739
) -> Self {
3840
Self {
3941
name,
4042
parameters,
4143
body,
44+
has_binding_identifier,
4245
}
4346
}
4447

@@ -62,6 +65,13 @@ impl AsyncGenerator {
6265
pub fn body(&self) -> &StatementList {
6366
&self.body
6467
}
68+
69+
/// Returns whether the function expression has a binding identifier.
70+
#[inline]
71+
#[must_use]
72+
pub fn has_binding_identifier(&self) -> bool {
73+
self.has_binding_identifier
74+
}
6575
}
6676

6777
impl ToIndentedString for AsyncGenerator {

boa_ast/src/function/generator.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub struct Generator {
2626
name: Option<Identifier>,
2727
parameters: FormalParameterList,
2828
body: StatementList,
29+
has_binding_identifier: bool,
2930
}
3031

3132
impl Generator {
@@ -36,11 +37,13 @@ impl Generator {
3637
name: Option<Identifier>,
3738
parameters: FormalParameterList,
3839
body: StatementList,
40+
has_binding_identifier: bool,
3941
) -> Self {
4042
Self {
4143
name,
4244
parameters,
4345
body,
46+
has_binding_identifier,
4447
}
4548
}
4649

@@ -64,6 +67,13 @@ impl Generator {
6467
pub fn body(&self) -> &StatementList {
6568
&self.body
6669
}
70+
71+
/// Returns whether the function expression has a binding identifier.
72+
#[inline]
73+
#[must_use]
74+
pub fn has_binding_identifier(&self) -> bool {
75+
self.has_binding_identifier
76+
}
6777
}
6878

6979
impl ToIndentedString for Generator {

boa_ast/src/function/mod.rs

Lines changed: 31 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,13 @@
55
//!
66
//! - [`Function`]s.
77
//! - [`ArrowFunction`]s.
8+
//! - [`AsyncArrowFunction`]s.
89
//! - [`Generator`]s.
910
//! - [`AsyncFunction`]s.
1011
//! - [`AsyncGenerator`]s.
1112
//!
1213
//! All of them can be declared in either [declaration][decl] form or [expression][expr] form,
13-
//! except from `ArrowFunction`s, which can only be declared in expression form.
14+
//! except from `ArrowFunction`s and `AsyncArrowFunction`s, which can only be declared in expression form.
1415
//!
1516
//! This module also contains [`Class`]es, which are templates for creating objects. Classes
1617
//! can also be declared in either declaration or expression form.
@@ -21,13 +22,15 @@
2122
//! [expr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function
2223
2324
mod arrow_function;
25+
mod async_arrow_function;
2426
mod async_function;
2527
mod async_generator;
2628
mod class;
2729
mod generator;
2830
mod parameters;
2931

3032
pub use arrow_function::ArrowFunction;
33+
pub use async_arrow_function::AsyncArrowFunction;
3134
pub use async_function::AsyncFunction;
3235
pub use async_generator::AsyncGenerator;
3336
pub use class::{Class, ClassElement};
@@ -59,10 +62,11 @@ pub struct Function {
5962
name: Option<Identifier>,
6063
parameters: FormalParameterList,
6164
body: StatementList,
65+
has_binding_identifier: bool,
6266
}
6367

6468
impl Function {
65-
/// Creates a new function expression
69+
/// Creates a new function expression.
6670
#[inline]
6771
#[must_use]
6872
pub fn new(
@@ -74,6 +78,24 @@ impl Function {
7478
name,
7579
parameters,
7680
body,
81+
has_binding_identifier: false,
82+
}
83+
}
84+
85+
/// Creates a new function expression with an expression binding identifier.
86+
#[inline]
87+
#[must_use]
88+
pub fn new_with_binding_identifier(
89+
name: Option<Identifier>,
90+
parameters: FormalParameterList,
91+
body: StatementList,
92+
has_binding_identifier: bool,
93+
) -> Self {
94+
Self {
95+
name,
96+
parameters,
97+
body,
98+
has_binding_identifier,
7799
}
78100
}
79101

@@ -97,6 +119,13 @@ impl Function {
97119
pub fn body(&self) -> &StatementList {
98120
&self.body
99121
}
122+
123+
/// Returns whether the function expression has a binding identifier.
124+
#[inline]
125+
#[must_use]
126+
pub fn has_binding_identifier(&self) -> bool {
127+
self.has_binding_identifier
128+
}
100129
}
101130

102131
impl ToIndentedString for Function {

boa_ast/src/operations.rs

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ use boa_interner::Sym;
99
use crate::{
1010
expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield},
1111
function::{
12-
ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator,
12+
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
13+
Function, Generator,
1314
},
1415
property::{MethodDefinition, PropertyDefinition},
1516
visitor::{VisitWith, Visitor},
@@ -133,6 +134,25 @@ where
133134
node.visit_with(self)
134135
}
135136

137+
fn visit_async_arrow_function(
138+
&mut self,
139+
node: &'ast AsyncArrowFunction,
140+
) -> ControlFlow<Self::BreakTy> {
141+
if ![
142+
ContainsSymbol::NewTarget,
143+
ContainsSymbol::SuperProperty,
144+
ContainsSymbol::SuperCall,
145+
ContainsSymbol::Super,
146+
ContainsSymbol::This,
147+
]
148+
.contains(&self.0)
149+
{
150+
return ControlFlow::Continue(());
151+
}
152+
153+
node.visit_with(self)
154+
}
155+
136156
fn visit_super_property_access(
137157
&mut self,
138158
node: &'ast SuperPropertyAccess,

0 commit comments

Comments
 (0)