diff --git a/boa_ast/src/operations.rs b/boa_ast/src/operations.rs index e2aa9141ce7..7919d76e122 100644 --- a/boa_ast/src/operations.rs +++ b/boa_ast/src/operations.rs @@ -47,6 +47,8 @@ pub enum ContainsSymbol { This, /// A method definition. MethodDefinition, + /// The BindingIdentifier "eval" or "arguments". + EvalOrArguments, } /// Returns `true` if the node contains the given symbol. @@ -66,6 +68,15 @@ where impl<'ast> Visitor<'ast> for ContainsVisitor { type BreakTy = (); + fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow { + if self.0 == ContainsSymbol::EvalOrArguments + && (node.sym() == Sym::EVAL || node.sym() == Sym::ARGUMENTS) + { + return ControlFlow::Break(()); + } + ControlFlow::Continue(()) + } + fn visit_function(&mut self, _: &'ast Function) -> ControlFlow { ControlFlow::Continue(()) } diff --git a/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs b/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs index 12d8d7f40d2..4c759a764ae 100644 --- a/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/async_function_expression/mod.rs @@ -6,7 +6,7 @@ use crate::{ parser::{ expression::BindingIdentifier, function::{FormalParameters, FunctionBody}, - name_in_lexically_declared_names, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, + name_in_lexically_declared_names, Cursor, OrAbrupt, ParseResult, TokenParser, }, Error, }; @@ -14,7 +14,7 @@ use boa_ast::{ expression::Identifier, function::AsyncFunction, operations::{bound_names, contains, top_level_lexically_declared_names, ContainsSymbol}, - Keyword, Position, Punctuator, + Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -31,20 +31,15 @@ use std::io::Read; #[derive(Debug, Clone, Copy)] pub(super) struct AsyncFunctionExpression { name: Option, - allow_yield: AllowYield, } impl AsyncFunctionExpression { /// Creates a new `AsyncFunctionExpression` parser. - pub(super) fn new(name: N, allow_yield: Y) -> Self + pub(super) fn new(name: N) -> Self where N: Into>, - Y: Into, { - Self { - name: name.into(), - allow_yield: allow_yield.into(), - } + Self { name: name.into() } } } @@ -63,26 +58,20 @@ where interner, )?; - let (name, has_binding_identifier) = match cursor.peek(0, interner).or_abrupt()?.kind() { - TokenKind::Punctuator(Punctuator::OpenParen) => (self.name, false), - _ => ( - Some(BindingIdentifier::new(self.allow_yield, true).parse(cursor, interner)?), - true, - ), - }; - - // 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() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { - return Err(Error::lex(LexError::Syntax( - "Unexpected eval or arguments in strict mode".into(), - cursor - .peek(0, interner)? - .map_or_else(|| Position::new(1, 1), |token| token.span().end()), - ))); + let token = cursor.peek(0, interner).or_abrupt()?; + let (name, name_span) = match token.kind() { + TokenKind::Identifier(_) + | TokenKind::Keyword(( + Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of, + _, + )) => { + let span = token.span(); + let name = BindingIdentifier::new(false, true).parse(cursor, interner)?; + + (Some(name), span) } - } + _ => (None, token.span()), + }; let params_start_position = cursor .expect(Punctuator::OpenParen, "async function expression", interner)? @@ -124,6 +113,28 @@ where ))); } + // 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() || body.strict()) + && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) + { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + name_span.start(), + ))); + } + } + + // Catch early error for BindingIdentifier, because strictness of the functions body is also + // relevant for the function parameters. + if body.strict() && contains(¶ms, ContainsSymbol::EvalOrArguments) { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".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 @@ -133,7 +144,7 @@ where params_start_position, )?; - let function = AsyncFunction::new(name, params, body, has_binding_identifier); + let function = AsyncFunction::new(name.or(self.name), params, body, name.is_some()); if contains(&function, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( diff --git a/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs b/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs index 660452da602..bc76a065536 100644 --- a/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs @@ -23,7 +23,7 @@ use boa_ast::{ expression::Identifier, function::AsyncGenerator, operations::{bound_names, contains, top_level_lexically_declared_names, ContainsSymbol}, - Keyword, Position, Punctuator, + Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -72,26 +72,20 @@ where interner, )?; - let (name, has_binding_identifier) = match cursor.peek(0, interner).or_abrupt()?.kind() { - TokenKind::Punctuator(Punctuator::OpenParen) => (self.name, false), - _ => ( - Some(BindingIdentifier::new(true, true).parse(cursor, interner)?), - true, - ), - }; - - // 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() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { - return Err(Error::lex(LexError::Syntax( - "Unexpected eval or arguments in strict mode".into(), - cursor - .peek(0, interner)? - .map_or_else(|| Position::new(1, 1), |token| token.span().end()), - ))); + let token = cursor.peek(0, interner).or_abrupt()?; + let (name, name_span) = match token.kind() { + TokenKind::Identifier(_) + | TokenKind::Keyword(( + Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of, + _, + )) => { + let span = token.span(); + let name = BindingIdentifier::new(true, true).parse(cursor, interner)?; + + (Some(name), span) } - } + _ => (None, token.span()), + }; let params_start_position = cursor .expect( @@ -157,6 +151,28 @@ where ))); } + // 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() || body.strict()) + && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) + { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + name_span.start(), + ))); + } + } + + // Catch early error for BindingIdentifier, because strictness of the functions body is also + // relevant for the function parameters. + if body.strict() && contains(¶ms, ContainsSymbol::EvalOrArguments) { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + params_start_position, + ))); + } + // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of FunctionBody. name_in_lexically_declared_names( @@ -165,7 +181,7 @@ where params_start_position, )?; - let function = AsyncGenerator::new(name, params, body, has_binding_identifier); + let function = AsyncGenerator::new(name.or(self.name), params, body, name.is_some()); if contains(&function, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( diff --git a/boa_parser/src/parser/expression/primary/function_expression/mod.rs b/boa_parser/src/parser/expression/primary/function_expression/mod.rs index f7e7e554df7..e7c05d6f791 100644 --- a/boa_parser/src/parser/expression/primary/function_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/function_expression/mod.rs @@ -23,7 +23,7 @@ use boa_ast::{ expression::Identifier, function::Function, operations::{bound_names, contains, top_level_lexically_declared_names, ContainsSymbol}, - Keyword, Position, Punctuator, + Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -61,28 +61,19 @@ where fn parse(self, cursor: &mut Cursor, interner: &mut Interner) -> ParseResult { let _timer = Profiler::global().start_event("FunctionExpression", "Parsing"); - let (name, has_binding_identifier) = match cursor.peek(0, interner).or_abrupt()?.kind() { + let token = cursor.peek(0, interner).or_abrupt()?; + let (name, name_span) = match token.kind() { TokenKind::Identifier(_) | TokenKind::Keyword(( Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of, _, )) => { + let span = token.span(); let name = BindingIdentifier::new(false, false).parse(cursor, interner)?; - // 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() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { - return Err(Error::lex(LexError::Syntax( - "Unexpected eval or arguments in strict mode".into(), - cursor - .peek(0, interner)? - .map_or_else(|| Position::new(1, 1), |token| token.span().end()), - ))); - } - - (Some(name), true) + (Some(name), span) } - _ => (self.name, false), + _ => (None, token.span()), }; let params_start_position = cursor @@ -117,6 +108,28 @@ where ))); } + // 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() || body.strict()) + && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) + { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + name_span.start(), + ))); + } + } + + // Catch early error for BindingIdentifier, because strictness of the functions body is also + // relevant for the function parameters. + if body.strict() && contains(¶ms, ContainsSymbol::EvalOrArguments) { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".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 @@ -127,7 +140,7 @@ where )?; let function = - Function::new_with_binding_identifier(name, params, body, has_binding_identifier); + Function::new_with_binding_identifier(name.or(self.name), params, body, name.is_some()); if contains(&function, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( diff --git a/boa_parser/src/parser/expression/primary/generator_expression/mod.rs b/boa_parser/src/parser/expression/primary/generator_expression/mod.rs index 8f9c920afb8..f482de57828 100644 --- a/boa_parser/src/parser/expression/primary/generator_expression/mod.rs +++ b/boa_parser/src/parser/expression/primary/generator_expression/mod.rs @@ -23,7 +23,7 @@ use boa_ast::{ expression::Identifier, function::Generator, operations::{bound_names, contains, top_level_lexically_declared_names, ContainsSymbol}, - Position, Punctuator, + Keyword, Punctuator, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -67,27 +67,20 @@ where interner, )?; - let (name, has_binding_identifier) = match cursor.peek(0, interner).or_abrupt()?.kind() { - TokenKind::Punctuator(Punctuator::OpenParen) => (self.name, false), - _ => ( - Some(BindingIdentifier::new(true, false).parse(cursor, interner)?), - true, - ), - }; - - // If BindingIdentifier is present and the source text matched by BindingIdentifier is strict mode code, - // it is a Syntax Error if the StringValue of BindingIdentifier is "eval" or "arguments". - // https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors - if let Some(name) = name { - if cursor.strict_mode() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { - return Err(Error::lex(LexError::Syntax( - "Unexpected eval or arguments in strict mode".into(), - cursor - .peek(0, interner)? - .map_or_else(|| Position::new(1, 1), |token| token.span().end()), - ))); + let token = cursor.peek(0, interner).or_abrupt()?; + let (name, name_span) = match token.kind() { + TokenKind::Identifier(_) + | TokenKind::Keyword(( + Keyword::Yield | Keyword::Await | Keyword::Async | Keyword::Of, + _, + )) => { + let span = token.span(); + let name = BindingIdentifier::new(true, false).parse(cursor, interner)?; + + (Some(name), span) } - } + _ => (None, token.span()), + }; let params_start_position = cursor .expect(Punctuator::OpenParen, "generator expression", interner)? @@ -123,6 +116,28 @@ where ))); } + // 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() || body.strict()) + && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) + { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + name_span.start(), + ))); + } + } + + // Catch early error for BindingIdentifier, because strictness of the functions body is also + // relevant for the function parameters. + if body.strict() && contains(¶ms, ContainsSymbol::EvalOrArguments) { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + params_start_position, + ))); + } + // It is a Syntax Error if any element of the BoundNames of FormalParameters // also occurs in the LexicallyDeclaredNames of GeneratorBody. // https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors @@ -141,7 +156,7 @@ where ))); } - let function = Generator::new(name, params, body, has_binding_identifier); + let function = Generator::new(name.or(self.name), params, body, name.is_some()); if contains(&function, ContainsSymbol::Super) { return Err(Error::lex(LexError::Syntax( diff --git a/boa_parser/src/parser/expression/primary/mod.rs b/boa_parser/src/parser/expression/primary/mod.rs index 33a142dc75d..b46f4c35cbe 100644 --- a/boa_parser/src/parser/expression/primary/mod.rs +++ b/boa_parser/src/parser/expression/primary/mod.rs @@ -147,7 +147,7 @@ where .parse(cursor, interner) .map(Into::into) } - _ => AsyncFunctionExpression::new(self.name, self.allow_yield) + _ => AsyncFunctionExpression::new(self.name) .parse(cursor, interner) .map(Into::into), } diff --git a/boa_parser/src/parser/statement/declaration/hoistable/mod.rs b/boa_parser/src/parser/statement/declaration/hoistable/mod.rs index 96d15679a12..737baa9be66 100644 --- a/boa_parser/src/parser/statement/declaration/hoistable/mod.rs +++ b/boa_parser/src/parser/statement/declaration/hoistable/mod.rs @@ -34,7 +34,7 @@ use boa_ast::{ expression::Identifier, function::FormalParameterList, operations::{bound_names, contains, top_level_lexically_declared_names, ContainsSymbol}, - Declaration, Keyword, Position, Punctuator, StatementList, + Declaration, Keyword, Punctuator, StatementList, }; use boa_interner::{Interner, Sym}; use boa_profiler::Profiler; @@ -143,13 +143,14 @@ fn parse_callable_declaration( cursor: &mut Cursor, interner: &mut Interner, ) -> ParseResult<(Identifier, FormalParameterList, StatementList)> { - let next_token = cursor.peek(0, interner).or_abrupt()?; - let name = match next_token.kind() { + let token = cursor.peek(0, interner).or_abrupt()?; + let name_span = token.span(); + let name = match token.kind() { TokenKind::Punctuator(Punctuator::OpenParen) => { if !c.is_default() { return Err(Error::unexpected( - next_token.to_string(interner), - next_token.span(), + token.to_string(interner), + token.span(), c.error_context(), )); } @@ -159,17 +160,6 @@ fn parse_callable_declaration( .parse(cursor, interner)?, }; - // 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() && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) { - return Err(Error::lex(LexError::Syntax( - "Unexpected eval or arguments in strict mode".into(), - cursor - .peek(0, interner)? - .map_or_else(|| Position::new(1, 1), |token| token.span().end()), - ))); - } - let params_start_position = cursor .expect(Punctuator::OpenParen, c.error_context(), interner)? .span() @@ -204,6 +194,25 @@ fn parse_callable_declaration( ))); } + // 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() || body.strict()) && [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym()) + { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".into(), + name_span.start(), + ))); + } + + // Early Error for BindingIdentifier, because the strictness of the functions body is also + // relevant for the function parameters. + if body.strict() && contains(¶ms, ContainsSymbol::EvalOrArguments) { + return Err(Error::lex(LexError::Syntax( + "unexpected identifier 'eval' or 'arguments' in strict mode".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