From 586a68970b1b5698183983fec9c3333bd0c45cbf Mon Sep 17 00:00:00 2001 From: Paul Lancaster Date: Tue, 13 Oct 2020 18:47:58 +0100 Subject: [PATCH] Async/Await parse (#836) * Add async keyword * Created AsyncFunctionDecl/Expr nodes * AsyncFunctionDecl/Expr parser created (not impl) * Add async to HoistableDeclaration * Added expect_no_lineterminator, updated some cursor docs * AsyncFunctionDecl parsing * AsyncFunctionExpr parsing * Await expression parsing * Added some async function expr parsing tests * Reposition declaration parsing ready to add tests * Moved function decl tests * Added some failing async func decl tests * mdn/spec links@ * Function expression parsing into own folder * Added function expression test * Fixed async function decl parse * Async/await run returns undefined rather than panic * Clippy :( -> :) * Missing docs * Update boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs Co-authored-by: Halid Odat * Update boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs Co-authored-by: Halid Odat * Update boa/src/syntax/parser/statement/mod.rs Co-authored-by: Halid Odat * Update boa/src/syntax/parser/statement/declaration/mod.rs Co-authored-by: Halid Odat * Update boa/src/syntax/ast/node/await_expr/mod.rs Co-authored-by: Halid Odat * Revert "Update boa/src/syntax/parser/statement/declaration/mod.rs" This reverts commit 46b291753039af2fee23e9390d6b64b632b703ab. * Revert "Update boa/src/syntax/parser/statement/mod.rs" This reverts commit 4c0c1238ec439a1eedc29780bd57c63a8fa6264f. * Update boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs Co-authored-by: Iban Eguia * Update boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs Co-authored-by: Iban Eguia * Addressed review comments * Correct allow_yield parameters for async func * Correct other usages of allow_yield Co-authored-by: Halid Odat Co-authored-by: Iban Eguia --- boa/src/syntax/ast/keyword.rs | 12 +++ boa/src/syntax/ast/node/await_expr/mod.rs | 61 +++++++++++ .../declaration/async_function_decl/mod.rs | 101 ++++++++++++++++++ .../declaration/async_function_expr/mod.rs | 98 +++++++++++++++++ boa/src/syntax/ast/node/declaration/mod.rs | 4 + boa/src/syntax/ast/node/mod.rs | 23 +++- boa/src/syntax/parser/cursor/mod.rs | 11 +- .../expression/assignment/arrow_function.rs | 2 +- .../parser/expression/assignment/mod.rs | 2 +- .../syntax/parser/expression/await_expr.rs | 58 ++++++++++ boa/src/syntax/parser/expression/mod.rs | 2 + .../primary/async_function_expression/mod.rs | 79 ++++++++++++++ .../async_function_expression/tests.rs | 65 +++++++++++ .../mod.rs} | 3 + .../primary/function_expression/tests.rs | 58 ++++++++++ .../syntax/parser/expression/primary/mod.rs | 8 +- .../primary/object_initializer/mod.rs | 11 ++ boa/src/syntax/parser/expression/unary.rs | 4 +- .../hoistable/async_function_decl/mod.rs | 89 +++++++++++++++ .../hoistable/async_function_decl/tests.rs | 24 +++++ .../function_decl/mod.rs} | 73 ++----------- .../hoistable/function_decl/tests.rs | 24 +++++ .../statement/declaration/hoistable/mod.rs | 82 ++++++++++++++ .../statement/declaration/hoistable/tests.rs | 1 + .../parser/statement/declaration/mod.rs | 2 +- .../parser/statement/declaration/tests.rs | 28 +---- boa/src/syntax/parser/statement/mod.rs | 6 +- boa/src/syntax/parser/statement/throw/mod.rs | 2 +- 28 files changed, 824 insertions(+), 109 deletions(-) create mode 100644 boa/src/syntax/ast/node/await_expr/mod.rs create mode 100644 boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs create mode 100644 boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs create mode 100644 boa/src/syntax/parser/expression/await_expr.rs create mode 100644 boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs create mode 100644 boa/src/syntax/parser/expression/primary/async_function_expression/tests.rs rename boa/src/syntax/parser/expression/primary/{function_expression.rs => function_expression/mod.rs} (98%) create mode 100644 boa/src/syntax/parser/expression/primary/function_expression/tests.rs create mode 100644 boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs create mode 100644 boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs rename boa/src/syntax/parser/statement/declaration/{hoistable.rs => hoistable/function_decl/mod.rs} (50%) create mode 100644 boa/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs create mode 100644 boa/src/syntax/parser/statement/declaration/hoistable/mod.rs create mode 100644 boa/src/syntax/parser/statement/declaration/hoistable/tests.rs diff --git a/boa/src/syntax/ast/keyword.rs b/boa/src/syntax/ast/keyword.rs index 566d25b322..925b8c9c9c 100644 --- a/boa/src/syntax/ast/keyword.rs +++ b/boa/src/syntax/ast/keyword.rs @@ -36,6 +36,16 @@ pub enum Keyword { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await Await, + /// The `async` keyword. + /// + /// 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/Statements/async_function + Async, + /// The `break` keyword. /// /// More information: @@ -454,6 +464,7 @@ impl Keyword { pub fn as_str(self) -> &'static str { match self { Self::Await => "await", + Self::Async => "async", Self::Break => "break", Self::Case => "case", Self::Catch => "catch", @@ -526,6 +537,7 @@ impl FromStr for Keyword { fn from_str(s: &str) -> Result { match s { "await" => Ok(Self::Await), + "async" => Ok(Self::Async), "break" => Ok(Self::Break), "case" => Ok(Self::Case), "catch" => Ok(Self::Catch), diff --git a/boa/src/syntax/ast/node/await_expr/mod.rs b/boa/src/syntax/ast/node/await_expr/mod.rs new file mode 100644 index 0000000000..168b3e37d9 --- /dev/null +++ b/boa/src/syntax/ast/node/await_expr/mod.rs @@ -0,0 +1,61 @@ +//! Await expression node. + +use super::Node; +use crate::{exec::Executable, BoaProfiler, Context, Result, Value}; +use gc::{Finalize, Trace}; +use std::fmt; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// An await expression is used within an async function to pause execution and wait for a +/// promise to resolve. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Trace, Finalize, PartialEq)] +pub struct AwaitExpr { + expr: Box, +} + +impl Executable for AwaitExpr { + fn run(&self, _: &mut Context) -> Result { + let _timer = BoaProfiler::global().start_event("AwaitExpression", "exec"); + // TODO: Implement AwaitExpr + Ok(Value::Undefined) + } +} + +impl AwaitExpr { + /// Implements the display formatting with indentation. + pub(super) fn display(&self, f: &mut fmt::Formatter<'_>, indentation: usize) -> fmt::Result { + writeln!(f, "await ")?; + self.expr.display(f, indentation) + } +} + +impl From for AwaitExpr +where + T: Into>, +{ + fn from(e: T) -> Self { + Self { expr: e.into() } + } +} + +impl fmt::Display for AwaitExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.display(f, 0) + } +} + +impl From for Node { + fn from(awaitexpr: AwaitExpr) -> Self { + Self::AwaitExpr(awaitexpr) + } +} diff --git a/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs new file mode 100644 index 0000000000..91ee9edc50 --- /dev/null +++ b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs @@ -0,0 +1,101 @@ +//! Async Function Declaration. + +use crate::{ + exec::Executable, + syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + BoaProfiler, Context, Result, Value, +}; +use gc::{Finalize, Trace}; +use std::fmt; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// An async function is used to specify an action (or series of actions) to perform asynchronously. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-async-function-prototype-properties +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Trace, Finalize, PartialEq)] +pub struct AsyncFunctionDecl { + name: Option>, + parameters: Box<[FormalParameter]>, + body: StatementList, +} + +impl AsyncFunctionDecl { + /// Creates a new async function declaration. + pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self + where + N: Into>>, + P: Into>, + B: Into, + { + Self { + name: name.into(), + parameters: parameters.into(), + body: body.into(), + } + } + + /// Gets the name of the async function declaration. + pub fn name(&self) -> Option<&str> { + self.name.as_deref() + } + + /// 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.statements() + } + + /// Implements the display formatting with indentation. + pub(in crate::syntax::ast::node) fn display( + &self, + f: &mut fmt::Formatter<'_>, + indentation: usize, + ) -> fmt::Result { + match &self.name { + Some(name) => { + write!(f, "async function {}(", name)?; + } + None => { + write!(f, "async function (")?; + } + } + join_nodes(f, &self.parameters)?; + f.write_str(") {{")?; + + self.body.display(f, indentation + 1)?; + + writeln!(f, "}}") + } +} + +impl Executable for AsyncFunctionDecl { + fn run(&self, _: &mut Context) -> Result { + let _timer = BoaProfiler::global().start_event("AsyncFunctionDecl", "exec"); + // TODO: Implement AsyncFunctionDecl + Ok(Value::undefined()) + } +} + +impl From for Node { + fn from(decl: AsyncFunctionDecl) -> Self { + Self::AsyncFunctionDecl(decl) + } +} + +impl fmt::Display for AsyncFunctionDecl { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.display(f, 0) + } +} diff --git a/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs b/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs new file mode 100644 index 0000000000..ac00989ee8 --- /dev/null +++ b/boa/src/syntax/ast/node/declaration/async_function_expr/mod.rs @@ -0,0 +1,98 @@ +//! Async Function Expression. + +use crate::{ + exec::Executable, + syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList}, + Context, Result, Value, +}; +use gc::{Finalize, Trace}; +use std::fmt; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +/// An async function expression is very similar to an async function declaration except used within +/// a wider expression (for example during an assignment). +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Debug, Trace, Finalize, PartialEq)] +pub struct AsyncFunctionExpr { + name: Option>, + parameters: Box<[FormalParameter]>, + body: StatementList, +} + +impl AsyncFunctionExpr { + /// Creates a new function expression + pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self + where + N: Into>>, + P: Into>, + B: Into, + { + Self { + name: name.into(), + parameters: parameters.into(), + body: body.into(), + } + } + + /// Gets the name of the function declaration. + pub fn name(&self) -> Option<&str> { + self.name.as_ref().map(Box::as_ref) + } + + /// Gets the list of parameters of the function declaration. + pub fn parameters(&self) -> &[FormalParameter] { + &self.parameters + } + + /// Gets the body of the function declaration. + pub fn body(&self) -> &[Node] { + self.body.statements() + } + + /// Implements the display formatting with indentation. + pub(in crate::syntax::ast::node) fn display( + &self, + f: &mut fmt::Formatter<'_>, + indentation: usize, + ) -> fmt::Result { + f.write_str("function")?; + if let Some(ref name) = self.name { + write!(f, " {}", name)?; + } + f.write_str("(")?; + join_nodes(f, &self.parameters)?; + f.write_str(") {{")?; + + self.body.display(f, indentation + 1)?; + + writeln!(f, "}}") + } +} + +impl Executable for AsyncFunctionExpr { + fn run(&self, _: &mut Context) -> Result { + // TODO: Implement AsyncFunctionExpr + Ok(Value::Undefined) + } +} + +impl fmt::Display for AsyncFunctionExpr { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.display(f, 0) + } +} + +impl From for Node { + fn from(expr: AsyncFunctionExpr) -> Self { + Self::AsyncFunctionExpr(expr) + } +} diff --git a/boa/src/syntax/ast/node/declaration/mod.rs b/boa/src/syntax/ast/node/declaration/mod.rs index 9e5d181a81..a256a75e87 100644 --- a/boa/src/syntax/ast/node/declaration/mod.rs +++ b/boa/src/syntax/ast/node/declaration/mod.rs @@ -1,6 +1,8 @@ //! Declaration nodes pub mod arrow_function_decl; +pub mod async_function_decl; +pub mod async_function_expr; pub mod const_decl_list; pub mod function_decl; pub mod function_expr; @@ -9,6 +11,8 @@ pub mod var_decl_list; pub use self::{ arrow_function_decl::ArrowFunctionDecl, + async_function_decl::AsyncFunctionDecl, + async_function_expr::AsyncFunctionExpr, const_decl_list::{ConstDecl, ConstDeclList}, function_decl::FunctionDecl, function_expr::FunctionExpr, diff --git a/boa/src/syntax/ast/node/mod.rs b/boa/src/syntax/ast/node/mod.rs index 5180c1ad70..70515bf144 100644 --- a/boa/src/syntax/ast/node/mod.rs +++ b/boa/src/syntax/ast/node/mod.rs @@ -1,6 +1,7 @@ //! This module implements the `Node` structure, which composes the AST. pub mod array; +pub mod await_expr; pub mod block; pub mod break_node; pub mod call; @@ -21,13 +22,14 @@ pub mod try_node; pub use self::{ array::ArrayDecl, + await_expr::AwaitExpr, block::Block, break_node::Break, call::Call, conditional::{ConditionalOp, If}, declaration::{ - ArrowFunctionDecl, ConstDecl, ConstDeclList, FunctionDecl, FunctionExpr, LetDecl, - LetDeclList, VarDecl, VarDeclList, + ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, ConstDecl, ConstDeclList, + FunctionDecl, FunctionExpr, LetDecl, LetDeclList, VarDecl, VarDeclList, }, field::{GetConstField, GetField}, identifier::Identifier, @@ -65,6 +67,15 @@ pub enum Node { /// An assignment operator node. [More information](./operator/struct.Assign.html). Assign(Assign), + /// An async function declaration node. [More information](./declaration/struct.AsyncFunctionDecl.html). + AsyncFunctionDecl(AsyncFunctionDecl), + + /// An async function expression node. [More information](./declaration/struct.AsyncFunctionExpr.html). + AsyncFunctionExpr(AsyncFunctionExpr), + + /// An await expression node. [More information](./await_expr/struct.AwaitExpression.html). + AwaitExpr(AwaitExpr), + /// A binary operator node. [More information](./operator/struct.BinOp.html). BinOp(BinOp), @@ -104,7 +115,7 @@ pub enum Node { /// A function declaration node. [More information](./declaration/struct.FunctionDecl.html). FunctionDecl(FunctionDecl), - /// A function expressino node. [More information](./declaration/struct.FunctionExpr.html). + /// A function expression node. [More information](./declaration/struct.FunctionExpr.html). FunctionExpr(FunctionExpr), /// Provides access to an object types' constant properties. [More information](./declaration/struct.GetConstField.html). @@ -243,6 +254,9 @@ impl Node { Self::Assign(ref op) => Display::fmt(op, f), Self::LetDeclList(ref decl) => Display::fmt(decl, f), Self::ConstDeclList(ref decl) => Display::fmt(decl, f), + Self::AsyncFunctionDecl(ref decl) => decl.display(f, indentation), + Self::AsyncFunctionExpr(ref expr) => expr.display(f, indentation), + Self::AwaitExpr(ref expr) => expr.display(f, indentation), } } } @@ -251,6 +265,9 @@ impl Executable for Node { fn run(&self, interpreter: &mut Context) -> Result { let _timer = BoaProfiler::global().start_event("Executable", "exec"); match *self { + Node::AsyncFunctionDecl(ref decl) => decl.run(interpreter), + Node::AsyncFunctionExpr(ref function_expr) => function_expr.run(interpreter), + Node::AwaitExpr(ref expr) => expr.run(interpreter), Node::Call(ref call) => call.run(interpreter), Node::Const(Const::Null) => Ok(Value::null()), Node::Const(Const::Num(num)) => Ok(Value::rational(num)), diff --git a/boa/src/syntax/parser/cursor/mod.rs b/boa/src/syntax/parser/cursor/mod.rs index b33851f7fd..7db28eb998 100644 --- a/boa/src/syntax/parser/cursor/mod.rs +++ b/boa/src/syntax/parser/cursor/mod.rs @@ -67,10 +67,6 @@ where } /// Returns an error if the next token is not of kind `kind`. - /// - /// Note: it will consume the next token only if the next token is the expected type. - /// - /// If skip_line_terminators is true then line terminators will be discarded. #[inline] pub(super) fn expect(&mut self, kind: K, context: &'static str) -> Result where @@ -134,15 +130,16 @@ where /// /// It expects that the token stream does not end here. /// - /// This is just syntatic sugar for a .peek(skip_n, false) call followed by a check that the result is not a line terminator or None. + /// This is just syntatic sugar for a .peek(skip_n) call followed by a check that the result is not a line terminator or None. #[inline] pub(super) fn peek_expect_no_lineterminator( &mut self, skip_n: usize, + context: &'static str, ) -> Result<&Token, ParseError> { if let Some(t) = self.buffered_lexer.peek(skip_n, false)? { if t.kind() == &TokenKind::LineTerminator { - Err(ParseError::unexpected(t.clone(), None)) + Err(ParseError::unexpected(t.clone(), context)) } else { Ok(t) } @@ -156,8 +153,6 @@ where /// When the next token is a `kind` token, get the token, otherwise return `None`. /// /// No next token also returns None. - /// - /// If skip_line_terminators is true then line terminators will be discarded. #[inline] pub(super) fn next_if(&mut self, kind: K) -> Result, ParseError> where diff --git a/boa/src/syntax/parser/expression/assignment/arrow_function.rs b/boa/src/syntax/parser/expression/assignment/arrow_function.rs index a9e36151b3..299e41497c 100644 --- a/boa/src/syntax/parser/expression/assignment/arrow_function.rs +++ b/boa/src/syntax/parser/expression/assignment/arrow_function.rs @@ -86,7 +86,7 @@ where Box::new([FormalParameter::new(param, None, false)]) }; - cursor.peek_expect_no_lineterminator(0)?; + cursor.peek_expect_no_lineterminator(0, "arrow function")?; cursor.expect(TokenKind::Punctuator(Punctuator::Arrow), "arrow function")?; let body = ConciseBody::new(self.allow_in).parse(cursor)?; diff --git a/boa/src/syntax/parser/expression/assignment/mod.rs b/boa/src/syntax/parser/expression/assignment/mod.rs index 6fccf62e02..3eebbfa3ce 100644 --- a/boa/src/syntax/parser/expression/assignment/mod.rs +++ b/boa/src/syntax/parser/expression/assignment/mod.rs @@ -88,7 +88,7 @@ where TokenKind::Identifier(_) | TokenKind::Keyword(Keyword::Yield) | TokenKind::Keyword(Keyword::Await) => { - if let Ok(tok) = cursor.peek_expect_no_lineterminator(1) { + if let Ok(tok) = cursor.peek_expect_no_lineterminator(1, "assignment expression") { if tok.kind() == &TokenKind::Punctuator(Punctuator::Arrow) { return ArrowFunction::new( self.allow_in, diff --git a/boa/src/syntax/parser/expression/await_expr.rs b/boa/src/syntax/parser/expression/await_expr.rs new file mode 100644 index 0000000000..2ecf34a6fe --- /dev/null +++ b/boa/src/syntax/parser/expression/await_expr.rs @@ -0,0 +1,58 @@ +//! Await expression parsing. +//! +//! More information: +//! - [MDN documentation][mdn] +//! - [ECMAScript specification][spec] +//! +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await +//! [spec]: https://tc39.es/ecma262/#prod-AwaitExpression + +use super::unary::UnaryExpression; + +use crate::syntax::{ + ast::{node::AwaitExpr, Keyword}, + lexer::TokenKind, + parser::{AllowYield, Cursor, ParseError, TokenParser}, +}; +use std::io::Read; + +/// Parses an await expression. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/await +/// [spec]: https://tc39.es/ecma262/#prod-AwaitExpression +#[derive(Debug, Clone, Copy)] +pub(in crate::syntax::parser) struct AwaitExpression { + allow_yield: AllowYield, +} + +impl AwaitExpression { + /// Creates a new `AwaitExpression` parser. + pub(in crate::syntax::parser) fn new(allow_yield: Y) -> Self + where + Y: Into, + { + Self { + allow_yield: allow_yield.into(), + } + } +} + +impl TokenParser for AwaitExpression +where + R: Read, +{ + type Output = AwaitExpr; + + fn parse(self, cursor: &mut Cursor) -> Result { + cursor.expect( + TokenKind::Keyword(Keyword::Await), + "Await expression parsing", + )?; + let expr = UnaryExpression::new(self.allow_yield, true).parse(cursor)?; + Ok(expr.into()) + } +} diff --git a/boa/src/syntax/parser/expression/mod.rs b/boa/src/syntax/parser/expression/mod.rs index 66ef2a99de..68ff35d7d2 100644 --- a/boa/src/syntax/parser/expression/mod.rs +++ b/boa/src/syntax/parser/expression/mod.rs @@ -15,6 +15,8 @@ mod tests; mod unary; mod update; +pub(in crate::syntax::parser) mod await_expr; + use self::assignment::ExponentiationExpression; pub(super) use self::{assignment::AssignmentExpression, primary::Initializer}; use super::{AllowAwait, AllowIn, AllowYield, Cursor, ParseResult, TokenParser}; diff --git a/boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs b/boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs new file mode 100644 index 0000000000..3bc2a5e271 --- /dev/null +++ b/boa/src/syntax/parser/expression/primary/async_function_expression/mod.rs @@ -0,0 +1,79 @@ +#[cfg(test)] +mod tests; + +use crate::{ + syntax::{ + ast::{node::AsyncFunctionExpr, Keyword, Punctuator}, + lexer::TokenKind, + parser::{ + function::{FormalParameters, FunctionBody}, + statement::BindingIdentifier, + AllowYield, Cursor, ParseError, TokenParser, + }, + }, + BoaProfiler, +}; + +use std::io::Read; + +/// Async Function expression parsing. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/async_function +/// [spec]: https://tc39.es/ecma262/#prod-AsyncFunctionExpression +#[derive(Debug, Clone, Copy)] +pub(super) struct AsyncFunctionExpression { + allow_yield: AllowYield, +} + +impl AsyncFunctionExpression { + /// Creates a new `AsyncFunctionExpression` parser. + pub(super) fn new(allow_yield: Y) -> Self + where + Y: Into, + { + Self { + allow_yield: allow_yield.into(), + } + } +} + +impl TokenParser for AsyncFunctionExpression +where + R: Read, +{ + type Output = AsyncFunctionExpr; + + fn parse(self, cursor: &mut Cursor) -> Result { + let _timer = BoaProfiler::global().start_event("AsyncFunctionExpression", "Parsing"); + cursor.peek_expect_no_lineterminator(0, "async function expression")?; + cursor.expect(Keyword::Function, "async function expression")?; + + let tok = cursor.peek(0)?; + + let name = if let Some(token) = tok { + match token.kind() { + TokenKind::Punctuator(Punctuator::OpenParen) => None, + _ => Some(BindingIdentifier::new(self.allow_yield, true).parse(cursor)?), + } + } else { + return Err(ParseError::AbruptEnd); + }; + + cursor.expect(Punctuator::OpenParen, "async function expression")?; + + let params = FormalParameters::new(false, true).parse(cursor)?; + + cursor.expect(Punctuator::CloseParen, "async function expression")?; + cursor.expect(Punctuator::OpenBlock, "async function expression")?; + + let body = FunctionBody::new(false, true).parse(cursor)?; + + cursor.expect(Punctuator::CloseBlock, "async function expression")?; + + Ok(AsyncFunctionExpr::new(name, params, body)) + } +} diff --git a/boa/src/syntax/parser/expression/primary/async_function_expression/tests.rs b/boa/src/syntax/parser/expression/primary/async_function_expression/tests.rs new file mode 100644 index 0000000000..450aec94d9 --- /dev/null +++ b/boa/src/syntax/parser/expression/primary/async_function_expression/tests.rs @@ -0,0 +1,65 @@ +use crate::syntax::{ + ast::{ + node::{AsyncFunctionExpr, ConstDecl, ConstDeclList, Return, StatementList}, + Const, + }, + parser::tests::check_parser, +}; + +/// Checks async expression parsing. +#[test] +fn check_async_expression() { + check_parser( + "const add = async function() { + return 1; + }; + ", + vec![ConstDeclList::from(vec![ConstDecl::new( + "add", + Some( + AsyncFunctionExpr::new::>, _, StatementList>( + None, + [], + vec![Return::new::<_, _, Option>>(Const::from(1), None).into()].into(), + ), + ), + )]) + .into()], + ); +} + +#[test] +fn check_nested_async_expression() { + check_parser( + "const a = async function() { + const b = async function() { + return 1; + }; + }; + ", + vec![ConstDeclList::from(vec![ConstDecl::new( + "a", + Some( + AsyncFunctionExpr::new::>, _, StatementList>( + None, + [], + vec![ConstDeclList::from(vec![ConstDecl::new( + "b", + Some( + AsyncFunctionExpr::new::>, _, StatementList>( + None, + [], + vec![Return::new::<_, _, Option>>(Const::from(1), None) + .into()] + .into(), + ), + ), + )]) + .into()] + .into(), + ), + ), + )]) + .into()], + ); +} diff --git a/boa/src/syntax/parser/expression/primary/function_expression.rs b/boa/src/syntax/parser/expression/primary/function_expression/mod.rs similarity index 98% rename from boa/src/syntax/parser/expression/primary/function_expression.rs rename to boa/src/syntax/parser/expression/primary/function_expression/mod.rs index 25840ada6f..645a588866 100644 --- a/boa/src/syntax/parser/expression/primary/function_expression.rs +++ b/boa/src/syntax/parser/expression/primary/function_expression/mod.rs @@ -7,6 +7,9 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/function //! [spec]: https://tc39.es/ecma262/#prod-FunctionExpression +#[cfg(test)] +mod tests; + use crate::{ syntax::{ ast::{node::FunctionExpr, Keyword, Punctuator}, diff --git a/boa/src/syntax/parser/expression/primary/function_expression/tests.rs b/boa/src/syntax/parser/expression/primary/function_expression/tests.rs new file mode 100644 index 0000000000..f86a54579b --- /dev/null +++ b/boa/src/syntax/parser/expression/primary/function_expression/tests.rs @@ -0,0 +1,58 @@ +use crate::syntax::{ + ast::{ + node::{ConstDecl, ConstDeclList, FunctionExpr, Return, StatementList}, + Const, + }, + parser::tests::check_parser, +}; + +/// Checks async expression parsing. +#[test] +fn check_function_expression() { + check_parser( + "const add = function() { + return 1; + }; + ", + vec![ConstDeclList::from(vec![ConstDecl::new( + "add", + Some(FunctionExpr::new::>, _, StatementList>( + None, + [], + vec![Return::new::<_, _, Option>>(Const::from(1), None).into()].into(), + )), + )]) + .into()], + ); +} + +#[test] +fn check_nested_function_expression() { + check_parser( + "const a = function() { + const b = function() { + return 1; + }; + }; + ", + vec![ConstDeclList::from(vec![ConstDecl::new( + "a", + Some(FunctionExpr::new::>, _, StatementList>( + None, + [], + vec![ConstDeclList::from(vec![ConstDecl::new( + "b", + Some(FunctionExpr::new::>, _, StatementList>( + None, + [], + vec![Return::new::<_, _, Option>>(Const::from(1), None).into()] + .into(), + )), + )]) + .into()] + .into(), + )), + )]) + .into()], + ); +} diff --git a/boa/src/syntax/parser/expression/primary/mod.rs b/boa/src/syntax/parser/expression/primary/mod.rs index 50cc95cc8a..7f55346663 100644 --- a/boa/src/syntax/parser/expression/primary/mod.rs +++ b/boa/src/syntax/parser/expression/primary/mod.rs @@ -8,14 +8,15 @@ //! [spec]: https://tc39.es/ecma262/#prod-PrimaryExpression mod array_initializer; +mod async_function_expression; mod function_expression; mod object_initializer; #[cfg(test)] mod tests; use self::{ - array_initializer::ArrayLiteral, function_expression::FunctionExpression, - object_initializer::ObjectLiteral, + array_initializer::ArrayLiteral, async_function_expression::AsyncFunctionExpression, + function_expression::FunctionExpression, object_initializer::ObjectLiteral, }; use super::Expression; use crate::{ @@ -77,6 +78,9 @@ where TokenKind::Keyword(Keyword::Function) => { FunctionExpression.parse(cursor).map(Node::from) } + TokenKind::Keyword(Keyword::Async) => AsyncFunctionExpression::new(self.allow_yield) + .parse(cursor) + .map(Node::from), TokenKind::Punctuator(Punctuator::OpenParen) => { cursor.set_goal(InputElement::RegExp); let expr = diff --git a/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs b/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs index 7ba38ac3ab..5138be9251 100644 --- a/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs +++ b/boa/src/syntax/parser/expression/primary/object_initializer/mod.rs @@ -141,6 +141,17 @@ where return Ok(node::PropertyDefinition::property(prop_name, val)); } + // TODO GeneratorMethod + // https://tc39.es/ecma262/#prod-GeneratorMethod + + if prop_name.as_str() == "async" { + // TODO - AsyncMethod. + // https://tc39.es/ecma262/#prod-AsyncMethod + + // TODO - AsyncGeneratorMethod + // https://tc39.es/ecma262/#prod-AsyncGeneratorMethod + } + if cursor .next_if(TokenKind::Punctuator(Punctuator::OpenParen))? .is_some() diff --git a/boa/src/syntax/parser/expression/unary.rs b/boa/src/syntax/parser/expression/unary.rs index 6ed9f5378c..ac8da4227e 100644 --- a/boa/src/syntax/parser/expression/unary.rs +++ b/boa/src/syntax/parser/expression/unary.rs @@ -33,14 +33,14 @@ use std::io::Read; /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Expressions_and_Operators#Unary /// [spec]: https://tc39.es/ecma262/#prod-UnaryExpression #[derive(Debug, Clone, Copy)] -pub(super) struct UnaryExpression { +pub(in crate::syntax::parser) struct UnaryExpression { allow_yield: AllowYield, allow_await: AllowAwait, } impl UnaryExpression { /// Creates a new `UnaryExpression` parser. - pub(super) fn new(allow_yield: Y, allow_await: A) -> Self + pub(in crate::syntax::parser) fn new(allow_yield: Y, allow_await: A) -> Self where Y: Into, A: Into, diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs new file mode 100644 index 0000000000..049b458b3b --- /dev/null +++ b/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/mod.rs @@ -0,0 +1,89 @@ +#[cfg(test)] +mod tests; + +use crate::syntax::{ + ast::{node::AsyncFunctionDecl, Keyword, Punctuator}, + lexer::TokenKind, + parser::{ + function::FormalParameters, function::FunctionBody, statement::BindingIdentifier, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + }, +}; +use std::io::Read; + +/// Async Function declaration parsing. +/// +/// More information: +/// - [MDN documentation][mdn] +/// - [ECMAScript specification][spec] +/// +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function +/// [spec]: https://www.ecma-international.org/ecma-262/11.0/index.html#prod-AsyncFunctionDeclaration +#[derive(Debug, Clone, Copy)] +pub(super) struct AsyncFunctionDeclaration { + allow_yield: AllowYield, + allow_await: AllowAwait, + is_default: AllowDefault, +} + +impl AsyncFunctionDeclaration { + /// Creates a new `FunctionDeclaration` parser. + pub(super) fn new(allow_yield: Y, allow_await: A, is_default: D) -> Self + where + Y: Into, + A: Into, + D: Into, + { + Self { + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + is_default: is_default.into(), + } + } +} + +impl TokenParser for AsyncFunctionDeclaration +where + R: Read, +{ + type Output = AsyncFunctionDecl; + + fn parse(self, cursor: &mut Cursor) -> Result { + cursor.expect(Keyword::Async, "async function declaration")?; + cursor.peek_expect_no_lineterminator(0, "async function declaration")?; + cursor.expect(Keyword::Function, "async function declaration")?; + let tok = cursor.peek(0)?; + + let name = if let Some(token) = tok { + match token.kind() { + TokenKind::Punctuator(Punctuator::OpenParen) => { + if !self.is_default.0 { + return Err(ParseError::unexpected( + token.clone(), + " in async function declaration", + )); + } + None + } + _ => { + Some(BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?) + } + } + } else { + return Err(ParseError::AbruptEnd); + }; + + cursor.expect(Punctuator::OpenParen, "async function declaration")?; + + let params = FormalParameters::new(false, true).parse(cursor)?; + + cursor.expect(Punctuator::CloseParen, "async function declaration")?; + cursor.expect(Punctuator::OpenBlock, "async function declaration")?; + + let body = FunctionBody::new(false, true).parse(cursor)?; + + cursor.expect(Punctuator::CloseBlock, "async function declaration")?; + + Ok(AsyncFunctionDecl::new(name, params, body)) + } +} diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs b/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs new file mode 100644 index 0000000000..a7648b74e5 --- /dev/null +++ b/boa/src/syntax/parser/statement/declaration/hoistable/async_function_decl/tests.rs @@ -0,0 +1,24 @@ +use crate::syntax::{ast::node::AsyncFunctionDecl, parser::tests::check_parser}; + +/// Async function declaration parsing. +#[test] +fn async_function_declaration() { + check_parser( + "async function hello() {}", + vec![AsyncFunctionDecl::new(Box::from("hello"), vec![], vec![]).into()], + ); +} + +/// Async function declaration parsing with keywords. +#[test] +fn async_function_declaration_keywords() { + check_parser( + "async function yield() {}", + vec![AsyncFunctionDecl::new(Box::from("yield"), vec![], vec![]).into()], + ); + + check_parser( + "async function await() {}", + vec![AsyncFunctionDecl::new(Box::from("await"), vec![], vec![]).into()], + ); +} diff --git a/boa/src/syntax/parser/statement/declaration/hoistable.rs b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs similarity index 50% rename from boa/src/syntax/parser/statement/declaration/hoistable.rs rename to boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs index 1f18c09976..f40045b5fd 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs @@ -1,67 +1,15 @@ -//! Hoistable declaration parsing. -//! -//! More information: -//! - [ECMAScript specification][spec] -//! -//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration - -use crate::{ - syntax::{ - ast::{node::FunctionDecl, Keyword, Node, Punctuator}, - parser::{ - function::FormalParameters, function::FunctionBody, statement::BindingIdentifier, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, - }, +#[cfg(test)] +mod tests; + +use crate::syntax::{ + ast::{node::FunctionDecl, Keyword, Punctuator}, + parser::{ + function::FormalParameters, function::FunctionBody, statement::BindingIdentifier, + AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, }, - BoaProfiler, }; - use std::io::Read; -/// Hoistable declaration parsing. -/// -/// More information: -/// - [ECMAScript specification][spec] -/// -/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration -#[derive(Debug, Clone, Copy)] -pub(super) struct HoistableDeclaration { - allow_yield: AllowYield, - allow_await: AllowAwait, - is_default: AllowDefault, -} - -impl HoistableDeclaration { - /// Creates a new `HoistableDeclaration` parser. - pub(super) fn new(allow_yield: Y, allow_await: A, is_default: D) -> Self - where - Y: Into, - A: Into, - D: Into, - { - Self { - allow_yield: allow_yield.into(), - allow_await: allow_await.into(), - is_default: is_default.into(), - } - } -} - -impl TokenParser for HoistableDeclaration -where - R: Read, -{ - type Output = Node; - - fn parse(self, cursor: &mut Cursor) -> ParseResult { - let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing"); - // TODO: check for generators and async functions + generators - FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default) - .parse(cursor) - .map(Node::from) - } -} - /// Function declaration parsing. /// /// More information: @@ -70,8 +18,9 @@ where /// /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/function /// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration + #[derive(Debug, Clone, Copy)] -struct FunctionDeclaration { +pub(super) struct FunctionDeclaration { allow_yield: AllowYield, allow_await: AllowAwait, is_default: AllowDefault, @@ -79,7 +28,7 @@ struct FunctionDeclaration { impl FunctionDeclaration { /// Creates a new `FunctionDeclaration` parser. - fn new(allow_yield: Y, allow_await: A, is_default: D) -> Self + pub(super) fn new(allow_yield: Y, allow_await: A, is_default: D) -> Self where Y: Into, A: Into, diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs new file mode 100644 index 0000000000..c9cff722c9 --- /dev/null +++ b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/tests.rs @@ -0,0 +1,24 @@ +use crate::syntax::{ast::node::FunctionDecl, parser::tests::check_parser}; + +/// Function declaration parsing. +#[test] +fn function_declaration() { + check_parser( + "function hello() {}", + vec![FunctionDecl::new(Box::from("hello"), vec![], vec![]).into()], + ); +} + +/// Function declaration parsing with keywords. +#[test] +fn function_declaration_keywords() { + check_parser( + "function yield() {}", + vec![FunctionDecl::new(Box::from("yield"), vec![], vec![]).into()], + ); + + check_parser( + "function await() {}", + vec![FunctionDecl::new(Box::from("await"), vec![], vec![]).into()], + ); +} diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs new file mode 100644 index 0000000000..bfb104d67d --- /dev/null +++ b/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs @@ -0,0 +1,82 @@ +//! Hoistable declaration parsing. +//! +//! More information: +//! - [ECMAScript specification][spec] +//! +//! [spec]: https://tc39.es/ecma262/#prod-HoistableDeclaration + +#[cfg(test)] +mod tests; + +mod async_function_decl; +mod function_decl; + +use async_function_decl::AsyncFunctionDeclaration; +use function_decl::FunctionDeclaration; + +use crate::{ + syntax::{ + ast::{Keyword, Node}, + lexer::TokenKind, + parser::{ + AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, + }, + }, + BoaProfiler, +}; +use std::io::Read; + +/// Hoistable declaration parsing. +/// +/// More information: +/// - [ECMAScript specification][spec] +/// +/// [spec]: https://tc39.es/ecma262/#prod-FunctionDeclaration +#[derive(Debug, Clone, Copy)] +pub(super) struct HoistableDeclaration { + allow_yield: AllowYield, + allow_await: AllowAwait, + is_default: AllowDefault, +} + +impl HoistableDeclaration { + /// Creates a new `HoistableDeclaration` parser. + pub(super) fn new(allow_yield: Y, allow_await: A, is_default: D) -> Self + where + Y: Into, + A: Into, + D: Into, + { + Self { + allow_yield: allow_yield.into(), + allow_await: allow_await.into(), + is_default: is_default.into(), + } + } +} + +impl TokenParser for HoistableDeclaration +where + R: Read, +{ + type Output = Node; + + fn parse(self, cursor: &mut Cursor) -> ParseResult { + let _timer = BoaProfiler::global().start_event("HoistableDeclaration", "Parsing"); + let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; + + match tok.kind() { + TokenKind::Keyword(Keyword::Function) => { + FunctionDeclaration::new(self.allow_yield, self.allow_await, self.is_default) + .parse(cursor) + .map(Node::from) + } + TokenKind::Keyword(Keyword::Async) => { + AsyncFunctionDeclaration::new(self.allow_yield, self.allow_await, false) + .parse(cursor) + .map(Node::from) + } + _ => unreachable!("unknown token found: {:?}", tok), + } + } +} diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/tests.rs b/boa/src/syntax/parser/statement/declaration/hoistable/tests.rs new file mode 100644 index 0000000000..8b13789179 --- /dev/null +++ b/boa/src/syntax/parser/statement/declaration/hoistable/tests.rs @@ -0,0 +1 @@ + diff --git a/boa/src/syntax/parser/statement/declaration/mod.rs b/boa/src/syntax/parser/statement/declaration/mod.rs index d1c1c9dd98..518748d2e7 100644 --- a/boa/src/syntax/parser/statement/declaration/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/mod.rs @@ -63,7 +63,7 @@ where let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; match tok.kind() { - TokenKind::Keyword(Keyword::Function) => { + TokenKind::Keyword(Keyword::Function) | TokenKind::Keyword(Keyword::Async) => { HoistableDeclaration::new(self.allow_yield, self.allow_await, false).parse(cursor) } TokenKind::Keyword(Keyword::Const) | TokenKind::Keyword(Keyword::Let) => { diff --git a/boa/src/syntax/parser/statement/declaration/tests.rs b/boa/src/syntax/parser/statement/declaration/tests.rs index 5ed9bd2d14..0088413917 100644 --- a/boa/src/syntax/parser/statement/declaration/tests.rs +++ b/boa/src/syntax/parser/statement/declaration/tests.rs @@ -1,9 +1,6 @@ use crate::syntax::{ ast::{ - node::{ - ConstDecl, ConstDeclList, FunctionDecl, LetDecl, LetDeclList, Node, VarDecl, - VarDeclList, - }, + node::{ConstDecl, ConstDeclList, LetDecl, LetDeclList, Node, VarDecl, VarDeclList}, Const, }, parser::tests::{check_invalid, check_parser}, @@ -169,26 +166,3 @@ fn multiple_const_declaration() { .into()], ); } - -/// Function declaration parsing. -#[test] -fn function_declaration() { - check_parser( - "function hello() {}", - vec![FunctionDecl::new(Box::from("hello"), vec![], vec![]).into()], - ); -} - -/// Function declaration parsing with keywords. -#[test] -fn function_declaration_keywords() { - check_parser( - "function yield() {}", - vec![FunctionDecl::new(Box::from("yield"), vec![], vec![]).into()], - ); - - check_parser( - "function await() {}", - vec![FunctionDecl::new(Box::from("await"), vec![], vec![]).into()], - ); -} diff --git a/boa/src/syntax/parser/statement/mod.rs b/boa/src/syntax/parser/statement/mod.rs index 1fab174adf..928741ceb1 100644 --- a/boa/src/syntax/parser/statement/mod.rs +++ b/boa/src/syntax/parser/statement/mod.rs @@ -42,6 +42,7 @@ use crate::{ syntax::{ ast::{node, Keyword, Node, Punctuator}, lexer::{Error as LexError, InputElement, TokenKind}, + parser::expression::await_expr::AwaitExpression, }, BoaProfiler, }; @@ -110,6 +111,9 @@ where let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; match tok.kind() { + TokenKind::Keyword(Keyword::Await) => AwaitExpression::new(self.allow_yield) + .parse(cursor) + .map(Node::from), TokenKind::Keyword(Keyword::If) => { IfStatement::new(self.allow_yield, self.allow_await, self.allow_return) .parse(cursor) @@ -386,7 +390,7 @@ where let tok = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; match *tok.kind() { - TokenKind::Keyword(Keyword::Function) => { + TokenKind::Keyword(Keyword::Function) | TokenKind::Keyword(Keyword::Async) => { if strict_mode && self.in_block { return Err(ParseError::lex(LexError::Syntax( "Function declaration in blocks not allowed in strict mode".into(), diff --git a/boa/src/syntax/parser/statement/throw/mod.rs b/boa/src/syntax/parser/statement/throw/mod.rs index 0056351271..14f3c711ec 100644 --- a/boa/src/syntax/parser/statement/throw/mod.rs +++ b/boa/src/syntax/parser/statement/throw/mod.rs @@ -50,7 +50,7 @@ where let _timer = BoaProfiler::global().start_event("ThrowStatement", "Parsing"); cursor.expect(Keyword::Throw, "throw statement")?; - cursor.peek_expect_no_lineterminator(0)?; + cursor.peek_expect_no_lineterminator(0, "throw statement")?; let expr = Expression::new(true, self.allow_yield, self.allow_await).parse(cursor)?; if let Some(tok) = cursor.peek(0)? {