Skip to content

Commit

Permalink
Implement async arrow functions (#2393)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
raskad committed Nov 4, 2022
1 parent 49a5867 commit dc3b09a
Show file tree
Hide file tree
Showing 39 changed files with 957 additions and 225 deletions.
8 changes: 7 additions & 1 deletion boa_ast/src/expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ use self::{
};

use super::{
function::FormalParameterList,
function::{ArrowFunction, AsyncFunction, AsyncGenerator, Class, Function, Generator},
function::{AsyncArrowFunction, FormalParameterList},
Statement,
};

Expand Down Expand Up @@ -88,6 +88,9 @@ pub enum Expression {
/// See [`ArrowFunction`].
ArrowFunction(ArrowFunction),

/// See [`AsyncArrowFunction`].
AsyncArrowFunction(AsyncArrowFunction),

/// See [`Generator`].
Generator(Generator),

Expand Down Expand Up @@ -168,6 +171,7 @@ impl Expression {
Self::ObjectLiteral(o) => o.to_indented_string(interner, indentation),
Self::Spread(sp) => sp.to_interned_string(interner),
Self::Function(f) => f.to_indented_string(interner, indentation),
Self::AsyncArrowFunction(f) => f.to_indented_string(interner, indentation),
Self::ArrowFunction(arrf) => arrf.to_indented_string(interner, indentation),
Self::Class(cl) => cl.to_indented_string(interner, indentation),
Self::Generator(gen) => gen.to_indented_string(interner, indentation),
Expand Down Expand Up @@ -219,6 +223,7 @@ impl VisitWith for Expression {
Expression::Spread(sp) => visitor.visit_spread(sp),
Expression::Function(f) => visitor.visit_function(f),
Expression::ArrowFunction(af) => visitor.visit_arrow_function(af),
Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function(af),
Expression::Generator(g) => visitor.visit_generator(g),
Expression::AsyncFunction(af) => visitor.visit_async_function(af),
Expression::AsyncGenerator(ag) => visitor.visit_async_generator(ag),
Expand Down Expand Up @@ -256,6 +261,7 @@ impl VisitWith for Expression {
Expression::Spread(sp) => visitor.visit_spread_mut(sp),
Expression::Function(f) => visitor.visit_function_mut(f),
Expression::ArrowFunction(af) => visitor.visit_arrow_function_mut(af),
Expression::AsyncArrowFunction(af) => visitor.visit_async_arrow_function_mut(af),
Expression::Generator(g) => visitor.visit_generator_mut(g),
Expression::AsyncFunction(af) => visitor.visit_async_function_mut(af),
Expression::AsyncGenerator(ag) => visitor.visit_async_generator_mut(ag),
Expand Down
118 changes: 118 additions & 0 deletions boa_ast/src/function/async_arrow_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
use std::ops::ControlFlow;

use super::FormalParameterList;
use crate::try_break;
use crate::visitor::{VisitWith, Visitor, VisitorMut};
use crate::{
expression::{Expression, Identifier},
join_nodes, StatementList,
};
use boa_interner::{Interner, ToIndentedString};

/// An async arrow function expression, as defined by the [spec].
///
/// An [async arrow function][mdn] expression is a syntactically compact alternative to a regular function
/// expression. Arrow function expressions are ill suited as methods, and they cannot be used as
/// constructors. Arrow functions cannot be used as constructors and will throw an error when
/// used with new.
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncArrowFunction
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Debug, PartialEq)]
pub struct AsyncArrowFunction {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
}

impl AsyncArrowFunction {
/// Creates a new `AsyncArrowFunction` AST Expression.
#[inline]
#[must_use]
pub fn new(
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
) -> Self {
Self {
name,
parameters,
body,
}
}

/// Gets the name of the function declaration.
#[inline]
#[must_use]
pub fn name(&self) -> Option<Identifier> {
self.name
}

/// Sets the name of the function declaration.
//#[inline]
//#[must_use]
//pub fn set_name(&mut self, name: Option<Identifier>) {
// self.name = name;
//}

/// Gets the list of parameters of the arrow function.
#[inline]
#[must_use]
pub fn parameters(&self) -> &FormalParameterList {
&self.parameters
}

/// Gets the body of the arrow function.
#[inline]
#[must_use]
pub fn body(&self) -> &StatementList {
&self.body
}
}

impl ToIndentedString for AsyncArrowFunction {
fn to_indented_string(&self, interner: &Interner, indentation: usize) -> String {
let mut buf = format!("async ({}", join_nodes(interner, self.parameters.as_ref()));
if self.body().statements().is_empty() {
buf.push_str(") => {}");
} else {
buf.push_str(&format!(
") => {{\n{}{}}}",
self.body.to_indented_string(interner, indentation + 1),
" ".repeat(indentation)
));
}
buf
}
}

impl From<AsyncArrowFunction> for Expression {
fn from(decl: AsyncArrowFunction) -> Self {
Self::AsyncArrowFunction(decl)
}
}

impl VisitWith for AsyncArrowFunction {
fn visit_with<'a, V>(&'a self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: Visitor<'a>,
{
if let Some(ident) = &self.name {
try_break!(visitor.visit_identifier(ident));
}
try_break!(visitor.visit_formal_parameter_list(&self.parameters));
visitor.visit_statement_list(&self.body)
}

fn visit_with_mut<'a, V>(&'a mut self, visitor: &mut V) -> ControlFlow<V::BreakTy>
where
V: VisitorMut<'a>,
{
if let Some(ident) = &mut self.name {
try_break!(visitor.visit_identifier_mut(ident));
}
try_break!(visitor.visit_formal_parameter_list_mut(&mut self.parameters));
visitor.visit_statement_list_mut(&mut self.body)
}
}
10 changes: 10 additions & 0 deletions boa_ast/src/function/async_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ pub struct AsyncFunction {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
}

impl AsyncFunction {
Expand All @@ -35,11 +36,13 @@ impl AsyncFunction {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier,
}
}

Expand All @@ -63,6 +66,13 @@ impl AsyncFunction {
pub fn body(&self) -> &StatementList {
&self.body
}

/// Returns whether the function expression has a binding identifier.
#[inline]
#[must_use]
pub fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
}
}

impl ToIndentedString for AsyncFunction {
Expand Down
10 changes: 10 additions & 0 deletions boa_ast/src/function/async_generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ pub struct AsyncGenerator {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
}

impl AsyncGenerator {
Expand All @@ -34,11 +35,13 @@ impl AsyncGenerator {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier,
}
}

Expand All @@ -62,6 +65,13 @@ impl AsyncGenerator {
pub fn body(&self) -> &StatementList {
&self.body
}

/// Returns whether the function expression has a binding identifier.
#[inline]
#[must_use]
pub fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
}
}

impl ToIndentedString for AsyncGenerator {
Expand Down
10 changes: 10 additions & 0 deletions boa_ast/src/function/generator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct Generator {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
}

impl Generator {
Expand All @@ -36,11 +37,13 @@ impl Generator {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier,
}
}

Expand All @@ -64,6 +67,13 @@ impl Generator {
pub fn body(&self) -> &StatementList {
&self.body
}

/// Returns whether the function expression has a binding identifier.
#[inline]
#[must_use]
pub fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
}
}

impl ToIndentedString for Generator {
Expand Down
33 changes: 31 additions & 2 deletions boa_ast/src/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,13 @@
//!
//! - [`Function`]s.
//! - [`ArrowFunction`]s.
//! - [`AsyncArrowFunction`]s.
//! - [`Generator`]s.
//! - [`AsyncFunction`]s.
//! - [`AsyncGenerator`]s.
//!
//! All of them can be declared in either [declaration][decl] form or [expression][expr] form,
//! except from `ArrowFunction`s, which can only be declared in expression form.
//! except from `ArrowFunction`s and `AsyncArrowFunction`s, which can only be declared in expression form.
//!
//! This module also contains [`Class`]es, which are templates for creating objects. Classes
//! can also be declared in either declaration or expression form.
Expand All @@ -21,13 +22,15 @@
//! [expr]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function

mod arrow_function;
mod async_arrow_function;
mod async_function;
mod async_generator;
mod class;
mod generator;
mod parameters;

pub use arrow_function::ArrowFunction;
pub use async_arrow_function::AsyncArrowFunction;
pub use async_function::AsyncFunction;
pub use async_generator::AsyncGenerator;
pub use class::{Class, ClassElement};
Expand Down Expand Up @@ -59,10 +62,11 @@ pub struct Function {
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
}

impl Function {
/// Creates a new function expression
/// Creates a new function expression.
#[inline]
#[must_use]
pub fn new(
Expand All @@ -74,6 +78,24 @@ impl Function {
name,
parameters,
body,
has_binding_identifier: false,
}
}

/// Creates a new function expression with an expression binding identifier.
#[inline]
#[must_use]
pub fn new_with_binding_identifier(
name: Option<Identifier>,
parameters: FormalParameterList,
body: StatementList,
has_binding_identifier: bool,
) -> Self {
Self {
name,
parameters,
body,
has_binding_identifier,
}
}

Expand All @@ -97,6 +119,13 @@ impl Function {
pub fn body(&self) -> &StatementList {
&self.body
}

/// Returns whether the function expression has a binding identifier.
#[inline]
#[must_use]
pub fn has_binding_identifier(&self) -> bool {
self.has_binding_identifier
}
}

impl ToIndentedString for Function {
Expand Down
22 changes: 21 additions & 1 deletion boa_ast/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ use boa_interner::Sym;
use crate::{
expression::{access::SuperPropertyAccess, Await, Identifier, SuperCall, Yield},
function::{
ArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement, Function, Generator,
ArrowFunction, AsyncArrowFunction, AsyncFunction, AsyncGenerator, Class, ClassElement,
Function, Generator,
},
property::{MethodDefinition, PropertyDefinition},
visitor::{VisitWith, Visitor},
Expand Down Expand Up @@ -133,6 +134,25 @@ where
node.visit_with(self)
}

fn visit_async_arrow_function(
&mut self,
node: &'ast AsyncArrowFunction,
) -> ControlFlow<Self::BreakTy> {
if ![
ContainsSymbol::NewTarget,
ContainsSymbol::SuperProperty,
ContainsSymbol::SuperCall,
ContainsSymbol::Super,
ContainsSymbol::This,
]
.contains(&self.0)
{
return ControlFlow::Continue(());
}

node.visit_with(self)
}

fn visit_super_property_access(
&mut self,
node: &'ast SuperPropertyAccess,
Expand Down
Loading

0 comments on commit dc3b09a

Please sign in to comment.