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 index ae5d271ee02..4d528d0bfa2 100644 --- a/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs +++ b/boa/src/syntax/ast/node/declaration/async_function_decl/mod.rs @@ -22,7 +22,7 @@ use serde::{Deserialize, Serialize}; #[cfg_attr(feature = "deser", derive(Serialize, Deserialize))] #[derive(Clone, Debug, Trace, Finalize, PartialEq)] pub struct AsyncFunctionDecl { - name: Option>, + name: Box, parameters: Box<[FormalParameter]>, body: StatementList, } @@ -31,7 +31,7 @@ impl AsyncFunctionDecl { /// Creates a new async function declaration. pub(in crate::syntax) fn new(name: N, parameters: P, body: B) -> Self where - N: Into>>, + N: Into>, P: Into>, B: Into, { @@ -43,8 +43,8 @@ impl AsyncFunctionDecl { } /// Gets the name of the async function declaration. - pub fn name(&self) -> Option<&str> { - self.name.as_deref() + pub fn name(&self) -> &str { + &self.name } /// Gets the list of parameters of the async function declaration. @@ -63,10 +63,7 @@ impl AsyncFunctionDecl { f: &mut fmt::Formatter<'_>, indentation: usize, ) -> fmt::Result { - match &self.name { - Some(name) => write!(f, "async function {}(", name)?, - None => write!(f, "async function (")?, - } + write!(f, "async function {}(", self.name())?; join_nodes(f, &self.parameters)?; if self.body().is_empty() { f.write_str(") {}") diff --git a/boa/src/syntax/ast/node/object/mod.rs b/boa/src/syntax/ast/node/object/mod.rs index f806bea00bd..b54a93acfd0 100644 --- a/boa/src/syntax/ast/node/object/mod.rs +++ b/boa/src/syntax/ast/node/object/mod.rs @@ -168,14 +168,16 @@ impl Executable for Object { } &MethodDefinitionKind::Generator => { // TODO: Implement generator method definition execution. - obj.set_property( + obj.__define_own_property__( name, PropertyDescriptor::builder() .value(JsValue::undefined()) .writable(true) .enumerable(true) - .configurable(true), - ); + .configurable(true) + .build(), + context, + )?; } } } 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 index 846d2f63ad1..0d8db84141e 100644 --- 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 @@ -2,13 +2,10 @@ mod tests; use crate::syntax::{ - ast::{node::AsyncFunctionDecl, Keyword, Punctuator}, - lexer::TokenKind, + ast::{node::AsyncFunctionDecl, Keyword}, parser::{ - function::FormalParameters, - function::FunctionBody, - statement::{BindingIdentifier, LexError, Position}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + statement::declaration::hoistable::parse_function_like_declaration, AllowAwait, + AllowDefault, AllowYield, Cursor, ParseError, TokenParser, }, }; use std::io::Read; @@ -54,91 +51,19 @@ where 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); - }; - - if let Some(name) = &name { - // 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 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 function declaration")? - .span() - .end(); - - 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")?; - - // 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 AsyncFunctionBody 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. - // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors - { - 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), - }, - ))); - } - } - } - - Ok(AsyncFunctionDecl::new(name, params.parameters, body)) + let result = parse_function_like_declaration( + "async function declaration", + self.is_default.0, + self.allow_yield.0, + self.allow_await.0, + false, + true, + false, + true, + cursor, + )?; + + Ok(AsyncFunctionDecl::new(result.0, result.1, result.2)) } } diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs index a0f6a6889a1..24cc7071610 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/function_decl/mod.rs @@ -2,12 +2,10 @@ mod tests; use crate::syntax::{ - ast::{node::FunctionDecl, Keyword, Punctuator}, + ast::{node::FunctionDecl, Keyword}, parser::{ - function::FormalParameters, - function::FunctionBody, - statement::{BindingIdentifier, LexError, Position}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + statement::declaration::hoistable::parse_function_like_declaration, AllowAwait, + AllowDefault, AllowYield, Cursor, ParseError, TokenParser, }, }; use std::io::Read; @@ -57,71 +55,18 @@ where fn parse(self, cursor: &mut Cursor) -> Result { cursor.expect(Keyword::Function, "function declaration")?; - // TODO: If self.is_default, then this can be empty. - let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?; - - // 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 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, "generator declaration")? - .span() - .end(); - - let params = FormalParameters::new(false, false).parse(cursor)?; - - cursor.expect(Punctuator::CloseParen, "function declaration")?; - cursor.expect(Punctuator::OpenBlock, "function declaration")?; - - let body = FunctionBody::new(self.allow_yield, self.allow_await).parse(cursor)?; - - cursor.expect(Punctuator::CloseBlock, "function declaration")?; - - // 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 FunctionBody 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. - // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors - { - 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), - }, - ))); - } - } - } - - Ok(FunctionDecl::new(name, params.parameters, body)) + let result = parse_function_like_declaration( + "function declaration", + self.is_default.0, + self.allow_yield.0, + self.allow_await.0, + false, + false, + self.allow_yield.0, + self.allow_await.0, + cursor, + )?; + + Ok(FunctionDecl::new(result.0, result.1, result.2)) } } diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs index 64ec4d68d81..35963eaf756 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/generator_decl/mod.rs @@ -4,10 +4,8 @@ mod tests; use crate::syntax::{ ast::{node::declaration::generator_decl::GeneratorDecl, Keyword, Punctuator}, parser::{ - function::FormalParameters, - function::FunctionBody, - statement::{BindingIdentifier, LexError, Position}, - AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, TokenParser, + statement::declaration::hoistable::parse_function_like_declaration, AllowAwait, + AllowDefault, AllowYield, Cursor, ParseError, TokenParser, }, }; use std::io::Read; @@ -53,70 +51,18 @@ where cursor.expect(Keyword::Function, "generator declaration")?; cursor.expect(Punctuator::Mul, "generator declaration")?; - // TODO: If self.is_default, then this can be empty. - let name = BindingIdentifier::new(self.allow_yield, self.allow_await).parse(cursor)?; - - // 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 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, "generator declaration")? - .span() - .end(); - - let params = FormalParameters::new(true, false).parse(cursor)?; - - cursor.expect(Punctuator::CloseParen, "generator declaration")?; - cursor.expect(Punctuator::OpenBlock, "generator declaration")?; - - let body = FunctionBody::new(true, false).parse(cursor)?; - - cursor.expect(Punctuator::CloseBlock, "generator declaration")?; - - // 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, - ))); - } - - // Early Error: It is a Syntax Error if any element of the BoundNames of FormalParameters - // 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), - }, - ))); - } - } - } - - Ok(GeneratorDecl::new(name, params.parameters, body)) + let result = parse_function_like_declaration( + "generator declaration", + self.is_default.0, + self.allow_yield.0, + self.allow_await.0, + true, + false, + true, + false, + cursor, + )?; + + Ok(GeneratorDecl::new(result.0, result.1, result.2)) } } diff --git a/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs b/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs index 417163f3ed2..5a91916e12d 100644 --- a/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs +++ b/boa/src/syntax/parser/statement/declaration/hoistable/mod.rs @@ -18,9 +18,12 @@ use generator_decl::GeneratorDeclaration; use crate::{ syntax::{ + ast::node::{FormalParameter, StatementList}, ast::{Keyword, Node, Punctuator}, - lexer::TokenKind, + lexer::{Position, TokenKind}, parser::{ + function::{FormalParameters, FunctionBody}, + statement::{BindingIdentifier, LexError}, AllowAwait, AllowDefault, AllowYield, Cursor, ParseError, ParseResult, TokenParser, }, }, @@ -89,3 +92,98 @@ where } } } + +// This is a helper function to not duplicate code in the individual hoistable deceleration parser functions. +#[inline] +#[allow(clippy::too_many_arguments, clippy::type_complexity)] +fn parse_function_like_declaration( + error_context: &'static str, + is_default: bool, + name_allow_yield: bool, + name_allow_await: bool, + parameters_allow_yield: bool, + parameters_allow_await: bool, + body_allow_yield: bool, + body_allow_await: bool, + cursor: &mut Cursor, +) -> Result<(Box, Box<[FormalParameter]>, StatementList), ParseError> { + let next_token = cursor.peek(0)?; + let name = if let Some(token) = next_token { + match token.kind() { + TokenKind::Punctuator(Punctuator::OpenParen) => { + if !is_default { + return Err(ParseError::unexpected(token.clone(), error_context)); + } + "default".into() + } + _ => BindingIdentifier::new(name_allow_yield, name_allow_await).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 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, error_context)? + .span() + .end(); + + let params = + FormalParameters::new(parameters_allow_yield, parameters_allow_await).parse(cursor)?; + + cursor.expect(Punctuator::CloseParen, error_context)?; + cursor.expect(Punctuator::OpenBlock, error_context)?; + + let body = FunctionBody::new(body_allow_yield, body_allow_await).parse(cursor)?; + + cursor.expect(Punctuator::CloseBlock, error_context)?; + + // 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 FunctionBody 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. + // https://tc39.es/ecma262/#sec-function-definitions-static-semantics-early-errors + { + 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), + }, + ))); + } + } + } + + Ok((name, params.parameters, body)) +}