Browse Source

Implement Async Generator Parsing (#1669)

Co-authored-by: Iban Eguia <razican@protonmail.ch>
pull/1686/head
Kevin 3 years ago committed by GitHub
parent
commit
33e22058cd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 32
      boa/src/bytecompiler.rs
  2. 94
      boa/src/syntax/ast/node/declaration/async_generator_decl/mod.rs
  3. 105
      boa/src/syntax/ast/node/declaration/async_generator_expr/mod.rs
  4. 5
      boa/src/syntax/ast/node/declaration/mod.rs
  5. 31
      boa/src/syntax/ast/node/mod.rs
  6. 30
      boa/src/syntax/ast/node/object/mod.rs
  7. 129
      boa/src/syntax/parser/expression/primary/async_generator_expression/mod.rs
  8. 82
      boa/src/syntax/parser/expression/primary/async_generator_expression/test.rs
  9. 12
      boa/src/syntax/parser/expression/primary/generator_expression/mod.rs
  10. 20
      boa/src/syntax/parser/expression/primary/mod.rs
  11. 135
      boa/src/syntax/parser/expression/primary/object_initializer/mod.rs
  12. 72
      boa/src/syntax/parser/expression/primary/object_initializer/tests.rs
  13. 6
      boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs
  14. 105
      boa/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs
  15. 9
      boa/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs
  16. 15
      boa/src/syntax/parser/statement/declaration/hoistable/mod.rs

32
boa/src/bytecompiler.rs

@ -672,6 +672,38 @@ impl ByteCompiler {
}
}
}
MethodDefinitionKind::Async => {
// TODO: Implement async
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertyByName, &[index])
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertyByValue);
}
}
}
MethodDefinitionKind::AsyncGenerator => {
// TODO: Implement async generators
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::Swap);
match name {
PropertyName::Literal(name) => {
let index = self.get_or_insert_name(name);
self.emit(Opcode::SetPropertyByName, &[index])
}
PropertyName::Computed(name_node) => {
self.compile_stmt(name_node, true);
self.emit_opcode(Opcode::Swap);
self.emit_opcode(Opcode::SetPropertyByValue);
}
}
}
}
}
// TODO: Spread Object

94
boa/src/syntax/ast/node/declaration/async_generator_decl/mod.rs

@ -0,0 +1,94 @@
//! Async Generator Declaration
use crate::{
exec::Executable,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
BoaProfiler, Context, JsResult, JsValue,
};
use gc::{Finalize, Trace};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
/// The 'async function*' defines an async generator function
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct AsyncGeneratorDecl {
name: Box<str>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl AsyncGeneratorDecl {
/// Creates a new async generator declaration.
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Box<str>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the async function declaration.
pub fn name(&self) -> &str {
&self.name
}
/// Gets the list of parameters of the async function declaration.
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the async function declaration.
pub fn body(&self) -> &[Node] {
self.body.items()
}
/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
write!(f, "async function* {}(", self.name())?;
join_nodes(f, &self.parameters)?;
if self.body().is_empty() {
f.write_str(") {}")
} else {
f.write_str(") {\n")?;
self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
}
}
impl Executable for AsyncGeneratorDecl {
fn run(&self, _: &mut Context) -> JsResult<JsValue> {
let _timer = BoaProfiler::global().start_event("AsyncGeneratorDecl", "exec");
//TODO: Implement AsyncGeneratorDecl
Ok(JsValue::undefined())
}
}
impl From<AsyncGeneratorDecl> for Node {
fn from(decl: AsyncGeneratorDecl) -> Self {
Self::AsyncGeneratorDecl(decl)
}
}
impl fmt::Display for AsyncGeneratorDecl {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}

105
boa/src/syntax/ast/node/declaration/async_generator_expr/mod.rs

@ -0,0 +1,105 @@
//! Async Generator Expression
use crate::{
exec::Executable,
gc::{Finalize, Trace},
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, JsResult, JsValue,
};
use std::fmt;
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};
/// The `async function*` keyword can be used to define a generator function inside an expression.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
#[cfg_attr(feature = "deser", derive(Serialize, Deserialize))]
#[derive(Clone, Debug, Trace, Finalize, PartialEq)]
pub struct AsyncGeneratorExpr {
name: Option<Box<str>>,
parameters: Box<[FormalParameter]>,
body: StatementList,
}
impl AsyncGeneratorExpr {
/// Creates a new async generator expression
pub(in crate::syntax) fn new<N, P, B>(name: N, parameters: P, body: B) -> Self
where
N: Into<Option<Box<str>>>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
Self {
name: name.into(),
parameters: parameters.into(),
body: body.into(),
}
}
/// Gets the name of the async generator expression
pub fn name(&self) -> Option<&str> {
self.name.as_ref().map(Box::as_ref)
}
/// Gets the list of parameters of the async generator expression
pub fn parameters(&self) -> &[FormalParameter] {
&self.parameters
}
/// Gets the body of the async generator expression
pub fn body(&self) -> &StatementList {
&self.body
}
pub(in crate::syntax::ast::node) fn display(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
f.write_str("async function*")?;
if let Some(ref name) = self.name {
write!(f, " {}", name)?;
}
f.write_str("(")?;
join_nodes(f, &self.parameters)?;
f.write_str(") ")?;
self.display_block(f, indentation)
}
pub(in crate::syntax::ast::node) fn display_block(
&self,
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
if self.body().items().is_empty() {
f.write_str("{}")
} else {
f.write_str("{\n")?;
self.body.display(f, indentation + 1)?;
write!(f, "{}}}", " ".repeat(indentation))
}
}
}
impl Executable for AsyncGeneratorExpr {
fn run(&self, _context: &mut Context) -> JsResult<JsValue> {
//TODO: Implement AsyncGeneratorFunction
Ok(JsValue::undefined())
}
}
impl fmt::Display for AsyncGeneratorExpr {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.display(f, 0)
}
}
impl From<AsyncGeneratorExpr> for Node {
fn from(expr: AsyncGeneratorExpr) -> Self {
Self::AsyncGeneratorExpr(expr)
}
}

5
boa/src/syntax/ast/node/declaration/mod.rs

@ -15,6 +15,8 @@ use serde::{Deserialize, Serialize};
pub mod arrow_function_decl;
pub mod async_function_decl;
pub mod async_function_expr;
pub mod async_generator_decl;
pub mod async_generator_expr;
pub mod function_decl;
pub mod function_expr;
pub mod generator_decl;
@ -22,7 +24,8 @@ pub mod generator_expr;
pub use self::{
arrow_function_decl::ArrowFunctionDecl, async_function_decl::AsyncFunctionDecl,
async_function_expr::AsyncFunctionExpr, function_decl::FunctionDecl,
async_function_expr::AsyncFunctionExpr, async_generator_decl::AsyncGeneratorDecl,
async_generator_expr::AsyncGeneratorExpr, function_decl::FunctionDecl,
function_expr::FunctionExpr,
};

31
boa/src/syntax/ast/node/mod.rs

@ -30,6 +30,7 @@ pub use self::{
call::Call,
conditional::{ConditionalOp, If},
declaration::{
async_generator_decl::AsyncGeneratorDecl, async_generator_expr::AsyncGeneratorExpr,
generator_decl::GeneratorDecl, generator_expr::GeneratorExpr, ArrowFunctionDecl,
AsyncFunctionDecl, AsyncFunctionExpr, Declaration, DeclarationList, FunctionDecl,
FunctionExpr,
@ -82,6 +83,12 @@ pub enum Node {
/// An async function expression node. [More information](./declaration/struct.AsyncFunctionExpr.html).
AsyncFunctionExpr(AsyncFunctionExpr),
/// An async generator expression node.
AsyncGeneratorExpr(AsyncGeneratorExpr),
/// An async generator declaration node.
AsyncGeneratorDecl(AsyncGeneratorDecl),
/// An await expression node. [More information](./await_expr/struct.AwaitExpression.html).
AwaitExpr(AwaitExpr),
@ -317,6 +324,8 @@ impl Node {
Self::Yield(ref y) => Display::fmt(y, f),
Self::GeneratorDecl(ref decl) => Display::fmt(decl, f),
Self::GeneratorExpr(ref expr) => expr.display(f, indentation),
Self::AsyncGeneratorExpr(ref expr) => expr.display(f, indentation),
Self::AsyncGeneratorDecl(ref decl) => decl.display(f, indentation),
}
}
}
@ -327,6 +336,8 @@ impl Executable for Node {
match *self {
Node::AsyncFunctionDecl(ref decl) => decl.run(context),
Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(context),
Node::AsyncGeneratorExpr(ref expr) => expr.run(context),
Node::AsyncGeneratorDecl(ref decl) => decl.run(context),
Node::AwaitExpr(ref expr) => expr.run(context),
Node::Call(ref call) => call.run(context),
Node::Const(Const::Null) => Ok(JsValue::null()),
@ -625,6 +636,26 @@ pub enum MethodDefinitionKind {
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#generator_methods
Generator,
/// Async generators can be used to define a method
///
/// More information
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_generator_methods
AsyncGenerator,
/// Async function can be used to define a method
///
/// More information
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncMethod
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Method_definitions#async_methods
Async,
}
unsafe impl Trace for MethodDefinitionKind {

30
boa/src/syntax/ast/node/object/mod.rs

@ -72,7 +72,10 @@ impl Object {
match &kind {
MethodDefinitionKind::Get => write!(f, "get ")?,
MethodDefinitionKind::Set => write!(f, "set ")?,
MethodDefinitionKind::Ordinary | MethodDefinitionKind::Generator => (),
MethodDefinitionKind::Ordinary
| MethodDefinitionKind::Generator
| MethodDefinitionKind::Async
| MethodDefinitionKind::AsyncGenerator => (),
}
write!(f, "{}(", key)?;
join_nodes(f, node.parameters())?;
@ -179,6 +182,31 @@ impl Executable for Object {
context,
)?;
}
&MethodDefinitionKind::AsyncGenerator => {
// TODO: Implement async generator method definition execution.
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.value(JsValue::undefined())
.writable(true)
.enumerable(true)
.configurable(true)
.build(),
context,
)?;
}
&MethodDefinitionKind::Async => {
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.value(JsValue::undefined())
.writable(true)
.enumerable(true)
.configurable(true)
.build(),
context,
)?;
}
}
}
// [spec]: https://tc39.es/ecma262/#sec-runtime-semantics-propertydefinitionevaluation

129
boa/src/syntax/parser/expression/primary/async_generator_expression/mod.rs

@ -0,0 +1,129 @@
//! Async Generator Expression Parser
//!
//! Implements TokenParser for AsyncGeneratorExpression and outputs
//! an AsyncGeneratorExpr ast node
//!
//! More information:
//! - [ECMAScript specification][spec]
//!
//! [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
#[cfg(test)]
mod test;
use crate::{
syntax::{
ast::{node::AsyncGeneratorExpr, Keyword, Punctuator},
lexer::{Error as LexError, Position, TokenKind},
parser::{
function::{FormalParameters, FunctionBody},
statement::BindingIdentifier,
Cursor, ParseError, TokenParser,
},
},
BoaProfiler,
};
use std::io::Read;
/// Async Generator Expression Parsing
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorExpression
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncGeneratorExpression;
impl<R> TokenParser<R> for AsyncGeneratorExpression
where
R: Read,
{
//The below needs to be implemented in ast::node
type Output = AsyncGeneratorExpr;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
let _timer = BoaProfiler::global().start_event("AsyncGeneratorExpression", "Parsing");
cursor.peek_expect_no_lineterminator(0, "async generator expression")?;
cursor.expect(Keyword::Function, "async generator expression")?;
cursor.expect(
TokenKind::Punctuator(Punctuator::Mul),
"async generator expression",
)?;
let name = if let Some(token) = cursor.peek(0)? {
match token.kind() {
TokenKind::Punctuator(Punctuator::OpenParen) => None,
_ => Some(BindingIdentifier::new(true, true).parse(cursor)?),
}
} else {
return Err(ParseError::AbruptEnd);
};
// Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict
// mode code, it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments".
if let Some(name) = &name {
if cursor.strict_mode() && ["eval", "arguments"].contains(&name.as_ref()) {
return Err(ParseError::lex(LexError::Syntax(
"Unexpected eval or arguments in strict mode".into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
let params_start_position = cursor
.expect(Punctuator::OpenParen, "async generator expression")?
.span()
.end();
let params = FormalParameters::new(true, true).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "async generator expression")?;
cursor.expect(Punctuator::OpenBlock, "async generator expression")?;
let body = FunctionBody::new(true, true).parse(cursor)?;
cursor.expect(Punctuator::CloseBlock, "async generator expression")?;
// Early Error: If the source code matching FormalParameters is strict mode code,
// the Early Error rules for UniqueFormalParameters : FormalParameters are applied.
if (cursor.strict_mode() || body.strict()) && params.has_duplicates {
return Err(ParseError::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
)));
}
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of GeneratorBody is true
// and IsSimpleParameterList of FormalParameters is false.
if body.strict() && !params.is_simple {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list".into(),
params_start_position,
)));
}
// It is a Syntax Error if any element of the BoundNames of FormalParameters
// also occurs in the LexicallyDeclaredNames of FunctionBody.
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.parameters.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name()).into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
//implement the below AsyncGeneratorExpr in ast::node
Ok(AsyncGeneratorExpr::new(name, params.parameters, body))
}
}

82
boa/src/syntax/parser/expression/primary/async_generator_expression/test.rs

@ -0,0 +1,82 @@
use crate::syntax::{
ast::{
node::{AsyncGeneratorExpr, Declaration, DeclarationList, Return, StatementList},
Const,
},
parser::tests::check_parser,
};
///checks async generator expression parsing
#[test]
fn check_async_generator_expr() {
check_parser(
"const add = async function*(){
return 1;
};
",
vec![DeclarationList::Const(
vec![Declaration::new_with_identifier(
"add",
Some(
AsyncGeneratorExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![Return::new::<_, _, Option<Box<str>>>(Const::from(1), None).into()]
.into(),
)
.into(),
),
)]
.into(),
)
.into()],
);
}
#[test]
fn check_nested_async_generator_expr() {
check_parser(
"const a = async function*() {
const b = async function*() {
return 1;
};
};
",
vec![DeclarationList::Const(
vec![Declaration::new_with_identifier(
"a",
Some(
AsyncGeneratorExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![DeclarationList::Const(
vec![Declaration::new_with_identifier(
"b",
Some(
AsyncGeneratorExpr::new::<Option<Box<str>>, _, StatementList>(
None,
[],
vec![Return::new::<_, _, Option<Box<str>>>(
Const::from(1),
None,
)
.into()]
.into(),
)
.into(),
),
)]
.into(),
)
.into()]
.into(),
)
.into(),
),
)]
.into(),
)
.into()],
);
}

12
boa/src/syntax/parser/expression/primary/generator_expression/mod.rs

@ -12,7 +12,7 @@ mod tests;
use crate::{
syntax::{
ast::{node::GeneratorExpr, Keyword, Punctuator},
ast::{node::GeneratorExpr, Punctuator},
lexer::{Error as LexError, Position, TokenKind},
parser::{
function::{FormalParameters, FunctionBody},
@ -52,15 +52,11 @@ where
let name = if let Some(token) = cursor.peek(0)? {
match token.kind() {
TokenKind::Identifier(_)
| TokenKind::Keyword(Keyword::Yield)
| TokenKind::Keyword(Keyword::Await) => {
Some(BindingIdentifier::new(true, false).parse(cursor)?)
}
_ => None,
TokenKind::Punctuator(Punctuator::OpenParen) => None,
_ => Some(BindingIdentifier::new(true, false).parse(cursor)?),
}
} else {
None
return Err(ParseError::AbruptEnd);
};
// Early Error: If BindingIdentifier is present and the source code matching BindingIdentifier is strict mode code,

20
boa/src/syntax/parser/expression/primary/mod.rs

@ -9,6 +9,7 @@
mod array_initializer;
mod async_function_expression;
mod async_generator_expression;
mod function_expression;
mod generator_expression;
mod object_initializer;
@ -18,8 +19,8 @@ mod tests;
use self::{
array_initializer::ArrayLiteral, async_function_expression::AsyncFunctionExpression,
function_expression::FunctionExpression, generator_expression::GeneratorExpression,
object_initializer::ObjectLiteral,
async_generator_expression::AsyncGeneratorExpression, function_expression::FunctionExpression,
generator_expression::GeneratorExpression, object_initializer::ObjectLiteral,
};
use super::Expression;
use crate::{
@ -77,6 +78,8 @@ where
fn parse(self, cursor: &mut Cursor<R>) -> ParseResult {
let _timer = BoaProfiler::global().start_event("PrimaryExpression", "Parsing");
// TODO: tok currently consumes the token instead of peeking, so the token
// isn't passed and consumed by parsers according to spec (EX: GeneratorExpression)
let tok = cursor.next()?.ok_or(ParseError::AbruptEnd)?;
match tok.kind() {
@ -89,9 +92,16 @@ where
FunctionExpression.parse(cursor).map(Node::from)
}
}
TokenKind::Keyword(Keyword::Async) => AsyncFunctionExpression::new(self.allow_yield)
.parse(cursor)
.map(Node::from),
TokenKind::Keyword(Keyword::Async) => {
let mul_peek = cursor.peek(1)?.ok_or(ParseError::AbruptEnd)?;
if mul_peek.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
AsyncGeneratorExpression.parse(cursor).map(Node::from)
} else {
AsyncFunctionExpression::new(self.allow_yield)
.parse(cursor)
.map(Node::from)
}
}
TokenKind::Punctuator(Punctuator::OpenParen) => {
cursor.set_goal(InputElement::RegExp);
let expr =

135
boa/src/syntax/parser/expression/primary/object_initializer/mod.rs

@ -193,6 +193,140 @@ where
return Ok(node::PropertyDefinition::SpreadObject(node));
}
//Async [AsyncMethod, AsyncGeneratorMethod] object methods
if cursor.next_if(Keyword::Async)?.is_some() {
cursor.peek_expect_no_lineterminator(0, "Async object methods")?;
let mul_check = cursor.next_if(Punctuator::Mul)?;
let property_name =
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor)?;
if mul_check.is_some() {
// MethodDefinition[?Yield, ?Await] -> AsyncGeneratorMethod[?Yield, ?Await]
let params_start_position = cursor
.expect(Punctuator::OpenParen, "async generator method definition")?
.span()
.start();
let params = FormalParameters::new(true, true).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "async generator method definition")?;
// Early Error: UniqueFormalParameters : FormalParameters
// NOTE: does not appear to formally be in ECMAScript specs for method
if params.has_duplicates {
return Err(ParseError::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
)));
}
cursor.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"async generator method definition",
)?;
let body = FunctionBody::new(true, true).parse(cursor)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"async generator method definition",
)?;
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
// and IsSimpleParameterList of UniqueFormalParameters is false.
if body.strict() && !params.is_simple {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
params_start_position,
)));
}
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.parameters.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name())
.into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
return Ok(node::PropertyDefinition::method_definition(
MethodDefinitionKind::AsyncGenerator,
property_name,
FunctionExpr::new(None, params.parameters, body),
));
} else {
// MethodDefinition[?Yield, ?Await] -> AsyncMethod[?Yield, ?Await]
let params_start_position = cursor
.expect(Punctuator::OpenParen, "async method definition")?
.span()
.start();
let params = FormalParameters::new(false, true).parse(cursor)?;
cursor.expect(Punctuator::CloseParen, "async method definition")?;
// Early Error: UniqueFormalParameters : FormalParameters
// NOTE: does not appear to be in ECMAScript specs
if params.has_duplicates {
return Err(ParseError::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
)));
}
cursor.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"async method definition",
)?;
let body = FunctionBody::new(true, true).parse(cursor)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"async method definition",
)?;
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
// and IsSimpleParameterList of UniqueFormalParameters is false.
if body.strict() && !params.is_simple {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
params_start_position,
)));
}
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
// occurs in the LexicallyDeclaredNames of GeneratorBody.
{
let lexically_declared_names = body.lexically_declared_names();
for param in params.parameters.as_ref() {
if lexically_declared_names.contains(param.name()) {
return Err(ParseError::lex(LexError::Syntax(
format!("Redeclaration of formal parameter `{}`", param.name())
.into(),
match cursor.peek(0)? {
Some(token) => token.span().end(),
None => Position::new(1, 1),
},
)));
}
}
}
return Ok(node::PropertyDefinition::method_definition(
MethodDefinitionKind::Async,
property_name,
FunctionExpr::new(None, params.parameters, body),
));
}
}
// MethodDefinition[?Yield, ?Await] -> GeneratorMethod[?Yield, ?Await]
if cursor.next_if(Punctuator::Mul)?.is_some() {
let property_name =
@ -206,6 +340,7 @@ where
cursor.expect(Punctuator::CloseParen, "generator method definition")?;
// Early Error: UniqueFormalParameters : FormalParameters
// NOTE: does not appear to be in ECMAScript specs for GeneratorMethod
if params.has_duplicates {
return Err(ParseError::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),

72
boa/src/syntax/parser/expression/primary/object_initializer/tests.rs

@ -6,7 +6,7 @@ use crate::syntax::{
},
Const,
},
parser::tests::check_parser,
parser::tests::{check_invalid, check_parser},
};
/// Checks object literal parsing.
@ -294,3 +294,73 @@ fn check_object_spread() {
.into()],
);
}
#[test]
fn check_async_method() {
let object_properties = vec![PropertyDefinition::method_definition(
MethodDefinitionKind::Async,
"dive",
FunctionExpr::new(None, vec![], vec![]),
)];
check_parser(
"const x = {
async dive() {}
};
",
vec![DeclarationList::Const(
vec![Declaration::new_with_identifier(
"x",
Some(Object::from(object_properties).into()),
)]
.into(),
)
.into()],
);
}
#[test]
fn check_async_generator_method() {
let object_properties = vec![PropertyDefinition::method_definition(
MethodDefinitionKind::AsyncGenerator,
"vroom",
FunctionExpr::new(None, vec![], vec![]),
)];
check_parser(
"const x = {
async* vroom() {}
};
",
vec![DeclarationList::Const(
vec![Declaration::new_with_identifier(
"x",
Some(Object::from(object_properties).into()),
)]
.into(),
)
.into()],
);
}
#[test]
fn check_async_method_lineterminator() {
check_invalid(
"const x = {
async
dive(){}
};
",
)
}
#[test]
fn check_async_gen_method_lineterminator() {
check_invalid(
"const x = {
async
* vroom() {}
};
",
)
}

6
boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs

@ -75,9 +75,9 @@ where
type Output = AsyncFunctionDecl;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
cursor.expect(Keyword::Async, "async function declaration")?;
cursor.peek_expect_no_lineterminator(0, "async function declaration")?;
cursor.expect(Keyword::Function, "async function declaration")?;
cursor.expect(Keyword::Async, "async hoistable declaration")?;
cursor.peek_expect_no_lineterminator(0, "async hoistable declaration")?;
cursor.expect(Keyword::Function, "async hoistable declaration")?;
let result = parse_callable_declaration(&self, cursor)?;

105
boa/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/mod.rs

@ -0,0 +1,105 @@
//! Async Generator Declaration parsing
//!
//! Implements TokenParser for AsyncGeneratorDeclaration and outputs an
//! AsyncGeneratorDecl ast node
//!
#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{node::AsyncGeneratorDecl, Keyword, Punctuator},
parser::{
statement::declaration::hoistable::{parse_callable_declaration, CallableDeclaration},
AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser,
},
};
use std::io::Read;
/// Async Generator Declaration Parser
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-AsyncGeneratorDeclaration
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncGeneratorDeclaration {
allow_yield: AllowYield,
allow_await: AllowAwait,
is_default: AllowDefault,
}
impl AsyncGeneratorDeclaration {
/// Creates a new `AsyncGeneratorDeclaration` parser.
pub(super) fn new<Y, A, D>(allow_yield: Y, allow_await: A, is_default: D) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
D: Into<AllowDefault>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
is_default: is_default.into(),
}
}
}
impl CallableDeclaration for AsyncGeneratorDeclaration {
#[inline]
fn error_context(&self) -> &'static str {
"async generator declaration"
}
#[inline]
fn is_default(&self) -> bool {
self.is_default.0
}
#[inline]
fn name_allow_yield(&self) -> bool {
self.allow_yield.0
}
#[inline]
fn name_allow_await(&self) -> bool {
self.allow_await.0
}
#[inline]
fn parameters_allow_yield(&self) -> bool {
true
}
#[inline]
fn parameters_allow_await(&self) -> bool {
true
}
#[inline]
fn body_allow_yield(&self) -> bool {
true
}
#[inline]
fn body_allow_await(&self) -> bool {
true
}
}
impl<R> TokenParser<R> for AsyncGeneratorDeclaration
where
R: Read,
{
type Output = AsyncGeneratorDecl;
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> {
cursor.expect(Keyword::Async, "async hoistable declaration")?;
cursor.peek_expect_no_lineterminator(0, "async hoistable declaration")?;
cursor.expect(Keyword::Function, "async hoistable declaration")?;
cursor.expect(Punctuator::Mul, "async generator declaration")?;
let result = parse_callable_declaration(&self, cursor)?;
Ok(AsyncGeneratorDecl::new(result.0, result.1, result.2))
}
}

9
boa/src/syntax/parser/statement/declaration/hoistable/async_generator_decl/tests.rs

@ -0,0 +1,9 @@
use crate::syntax::{ast::node::AsyncGeneratorDecl, parser::tests::check_parser};
#[test]
fn async_generator_function_declaration() {
check_parser(
"async function* gen() {}",
vec![AsyncGeneratorDecl::new(Box::from("gen"), vec![], vec![]).into()],
);
}

15
boa/src/syntax/parser/statement/declaration/hoistable/mod.rs

@ -9,10 +9,12 @@
mod tests;
mod async_function_decl;
mod async_generator_decl;
mod function_decl;
mod generator_decl;
use async_function_decl::AsyncFunctionDeclaration;
use async_generator_decl::AsyncGeneratorDeclaration;
pub(in crate::syntax::parser) use function_decl::FunctionDeclaration;
use generator_decl::GeneratorDeclaration;
@ -84,9 +86,20 @@ where
}
}
TokenKind::Keyword(Keyword::Async) => {
AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false)
let next_token = cursor.peek(2)?.ok_or(ParseError::AbruptEnd)?;
if let TokenKind::Punctuator(Punctuator::Mul) = next_token.kind() {
AsyncGeneratorDeclaration::new(
self.allow_yield,
self.allow_await,
self.is_default,
)
.parse(cursor)
.map(Node::from)
} else {
AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false)
.parse(cursor)
.map(Node::from)
}
}
_ => unreachable!("unknown token found: {:?}", tok),
}

Loading…
Cancel
Save