Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Merged by Bors] - Add early errors for 'eval' or 'arguments' in parameters #2515

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions boa_ast/src/operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -66,6 +68,15 @@ where
impl<'ast> Visitor<'ast> for ContainsVisitor {
type BreakTy = ();

fn visit_identifier(&mut self, node: &'ast Identifier) -> ControlFlow<Self::BreakTy> {
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<Self::BreakTy> {
ControlFlow::Continue(())
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@ 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,
};
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;
Expand All @@ -31,20 +31,15 @@ use std::io::Read;
#[derive(Debug, Clone, Copy)]
pub(super) struct AsyncFunctionExpression {
name: Option<Identifier>,
allow_yield: AllowYield,
}

impl AsyncFunctionExpression {
/// Creates a new `AsyncFunctionExpression` parser.
pub(super) fn new<N, Y>(name: N, allow_yield: Y) -> Self
pub(super) fn new<N>(name: N) -> Self
where
N: Into<Option<Identifier>>,
Y: Into<AllowYield>,
{
Self {
name: name.into(),
allow_yield: allow_yield.into(),
}
Self { name: name.into() }
}
}

Expand All @@ -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)?
Expand Down Expand Up @@ -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(&params, 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
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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(&params, 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(
Expand All @@ -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(
Expand Down
45 changes: 29 additions & 16 deletions boa_parser/src/parser/expression/primary/function_expression/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -61,28 +61,19 @@ where
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
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
Expand Down Expand Up @@ -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(&params, 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
Expand All @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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)?
Expand Down Expand Up @@ -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(&params, 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
Expand All @@ -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(
Expand Down
Loading