Browse Source

Add early errors for escaped identifiers (#2546)

This Pull Request changes the following:

- Add early errors for escaped characters in object and class setters and getters.
- Add early errors for escaped characters in class `static`.
- Add early errors for escaped characters in `new.target`.
- Add early errors for legacy octal/decial escapes that are used in string literals before a `"use strict"` directive.
pull/2575/head
raskad 2 years ago
parent
commit
bd0652fe28
  1. 9
      boa_parser/src/lexer/identifier.rs
  2. 61
      boa_parser/src/lexer/string.rs
  3. 2
      boa_parser/src/lexer/template.rs
  4. 48
      boa_parser/src/lexer/tests.rs
  5. 40
      boa_parser/src/lexer/token.rs
  6. 10
      boa_parser/src/parser/expression/identifiers.rs
  7. 4
      boa_parser/src/parser/expression/left_hand_side/call.rs
  8. 16
      boa_parser/src/parser/expression/left_hand_side/member.rs
  9. 2
      boa_parser/src/parser/expression/left_hand_side/optional/mod.rs
  10. 2
      boa_parser/src/parser/expression/primary/mod.rs
  11. 35
      boa_parser/src/parser/expression/primary/object_initializer/mod.rs
  12. 68
      boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs
  13. 24
      boa_parser/src/parser/statement/mod.rs

9
boa_parser/src/lexer/identifier.rs

@ -1,6 +1,8 @@
//! This module implements lexing for identifiers (foo, myvar, etc.) used in ECMAScript. //! This module implements lexing for identifiers (foo, myvar, etc.) used in ECMAScript.
use crate::lexer::{Cursor, Error, StringLiteral, Token, TokenKind, Tokenizer}; use crate::lexer::{
token::ContainsEscapeSequence, Cursor, Error, StringLiteral, Token, TokenKind, Tokenizer,
};
use boa_ast::{Keyword, Position, Span}; use boa_ast::{Keyword, Position, Span};
use boa_interner::Interner; use boa_interner::Interner;
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -71,7 +73,10 @@ impl<R> Tokenizer<R> for Identifier {
Ok(Keyword::False) => TokenKind::BooleanLiteral(false), Ok(Keyword::False) => TokenKind::BooleanLiteral(false),
Ok(Keyword::Null) => TokenKind::NullLiteral, Ok(Keyword::Null) => TokenKind::NullLiteral,
Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)), Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)),
_ => TokenKind::identifier(interner.get_or_intern(identifier_name.as_str())), _ => TokenKind::Identifier((
interner.get_or_intern(identifier_name.as_str()),
ContainsEscapeSequence(contains_escaped_chars),
)),
}; };
Ok(Token::new(token_kind, Span::new(start_pos, cursor.pos()))) Ok(Token::new(token_kind, Span::new(start_pos, cursor.pos())))

61
boa_parser/src/lexer/string.rs

@ -1,6 +1,6 @@
//! Boa's lexing for ECMAScript string literals. //! Boa's lexing for ECMAScript string literals.
use crate::lexer::{Cursor, Error, Token, TokenKind, Tokenizer}; use crate::lexer::{token::EscapeSequence, Cursor, Error, Token, TokenKind, Tokenizer};
use boa_ast::{Position, Span}; use boa_ast::{Position, Span};
use boa_interner::Interner; use boa_interner::Interner;
use boa_profiler::Profiler; use boa_profiler::Profiler;
@ -88,11 +88,11 @@ impl<R> Tokenizer<R> for StringLiteral {
{ {
let _timer = Profiler::global().start_event("StringLiteral", "Lexing"); let _timer = Profiler::global().start_event("StringLiteral", "Lexing");
let (lit, span) = let (lit, span, escape_sequence) =
Self::take_string_characters(cursor, start_pos, self.terminator, cursor.strict_mode())?; Self::take_string_characters(cursor, start_pos, self.terminator, cursor.strict_mode())?;
Ok(Token::new( Ok(Token::new(
TokenKind::string_literal(interner.get_or_intern(&lit[..])), TokenKind::string_literal(interner.get_or_intern(&lit[..]), escape_sequence),
span, span,
)) ))
} }
@ -117,11 +117,13 @@ impl StringLiteral {
start_pos: Position, start_pos: Position,
terminator: StringTerminator, terminator: StringTerminator,
is_strict_mode: bool, is_strict_mode: bool,
) -> Result<(Vec<u16>, Span), Error> ) -> Result<(Vec<u16>, Span, Option<EscapeSequence>), Error>
where where
R: Read, R: Read,
{ {
let mut buf = Vec::new(); let mut buf = Vec::new();
let mut escape_sequence = None;
loop { loop {
let ch_start_pos = cursor.pos(); let ch_start_pos = cursor.pos();
let ch = cursor.next_char()?; let ch = cursor.next_char()?;
@ -133,12 +135,15 @@ impl StringLiteral {
let _timer = let _timer =
Profiler::global().start_event("StringLiteral - escape sequence", "Lexing"); Profiler::global().start_event("StringLiteral - escape sequence", "Lexing");
if let Some(escape_value) = Self::take_escape_sequence_or_line_continuation( if let Some((escape_value, escape)) =
cursor, Self::take_escape_sequence_or_line_continuation(
ch_start_pos, cursor,
is_strict_mode, ch_start_pos,
false, is_strict_mode,
)? { false,
)?
{
escape_sequence = escape_sequence.or(escape);
buf.push_code_point(escape_value); buf.push_code_point(escape_value);
} }
} }
@ -156,7 +161,7 @@ impl StringLiteral {
} }
} }
Ok((buf, Span::new(start_pos, cursor.pos()))) Ok((buf, Span::new(start_pos, cursor.pos()), escape_sequence))
} }
pub(super) fn take_escape_sequence_or_line_continuation<R>( pub(super) fn take_escape_sequence_or_line_continuation<R>(
@ -164,7 +169,7 @@ impl StringLiteral {
start_pos: Position, start_pos: Position,
is_strict_mode: bool, is_strict_mode: bool,
is_template_literal: bool, is_template_literal: bool,
) -> Result<Option<u32>, Error> ) -> Result<Option<(u32, Option<EscapeSequence>)>, Error>
where where
R: Read, R: Read,
{ {
@ -176,25 +181,25 @@ impl StringLiteral {
})?; })?;
let escape_value = match escape_ch { let escape_value = match escape_ch {
0x0062 /* b */ => Some(0x0008 /* <BS> */), 0x0062 /* b */ => Some((0x0008 /* <BS> */, None)),
0x0074 /* t */ => Some(0x0009 /* <HT> */), 0x0074 /* t */ => Some((0x0009 /* <HT> */, None)),
0x006E /* n */ => Some(0x000A /* <LF> */), 0x006E /* n */ => Some((0x000A /* <LF> */, None)),
0x0076 /* v */ => Some(0x000B /* <VT> */), 0x0076 /* v */ => Some((0x000B /* <VT> */, None)),
0x0066 /* f */ => Some(0x000C /* <FF> */), 0x0066 /* f */ => Some((0x000C /* <FF> */, None)),
0x0072 /* r */ => Some(0x000D /* <CR> */), 0x0072 /* r */ => Some((0x000D /* <CR> */, None)),
0x0022 /* " */ => Some(0x0022 /* " */), 0x0022 /* " */ => Some((0x0022 /* " */, None)),
0x0027 /* ' */ => Some(0x0027 /* ' */), 0x0027 /* ' */ => Some((0x0027 /* ' */, None)),
0x005C /* \ */ => Some(0x005C /* \ */), 0x005C /* \ */ => Some((0x005C /* \ */, None)),
0x0030 /* 0 */ if cursor 0x0030 /* 0 */ if cursor
.peek()? .peek()?
.filter(|next_byte| (b'0'..=b'9').contains(next_byte)) .filter(|next_byte| (b'0'..=b'9').contains(next_byte))
.is_none() => .is_none() =>
Some(0x0000 /* NULL */), Some((0x0000 /* NULL */, None)),
0x0078 /* x */ => { 0x0078 /* x */ => {
Some(Self::take_hex_escape_sequence(cursor, start_pos)?) Some((Self::take_hex_escape_sequence(cursor, start_pos)?, None))
} }
0x0075 /* u */ => { 0x0075 /* u */ => {
Some(Self::take_unicode_escape_sequence(cursor, start_pos)?) Some((Self::take_unicode_escape_sequence(cursor, start_pos)?, None))
} }
0x0038 /* 8 */ | 0x0039 /* 9 */ => { 0x0038 /* 8 */ | 0x0039 /* 9 */ => {
// Grammar: NonOctalDecimalEscapeSequence // Grammar: NonOctalDecimalEscapeSequence
@ -209,7 +214,7 @@ impl StringLiteral {
start_pos, start_pos,
)); ));
} }
Some(escape_ch) Some((escape_ch, Some(EscapeSequence::NonOctalDecimal)))
} }
_ if (0x0030..=0x0037 /* '0'..='7' */).contains(&escape_ch) => { _ if (0x0030..=0x0037 /* '0'..='7' */).contains(&escape_ch) => {
if is_template_literal { if is_template_literal {
@ -226,10 +231,10 @@ impl StringLiteral {
)); ));
} }
Some(Self::take_legacy_octal_escape_sequence( Some((Self::take_legacy_octal_escape_sequence(
cursor, cursor,
escape_ch.try_into().expect("an ascii char must not fail to convert"), escape_ch.try_into().expect("an ascii char must not fail to convert"),
)?) )?, Some(EscapeSequence::LegacyOctal)))
} }
_ if Self::is_line_terminator(escape_ch) => { _ if Self::is_line_terminator(escape_ch) => {
// Grammar: LineContinuation // Grammar: LineContinuation
@ -238,7 +243,7 @@ impl StringLiteral {
None None
} }
_ => { _ => {
Some(escape_ch) Some((escape_ch, None))
} }
}; };

2
boa_parser/src/lexer/template.rs

@ -60,7 +60,7 @@ impl TemplateString {
true, true,
)?; )?;
if let Some(escape_value) = escape_value { if let Some((escape_value, _)) = escape_value {
buf.push_code_point(escape_value); buf.push_code_point(escape_value);
} }
} }

48
boa_parser/src/lexer/tests.rs

@ -2,8 +2,9 @@
#![allow(clippy::indexing_slicing)] #![allow(clippy::indexing_slicing)]
use crate::lexer::{ use crate::lexer::{
template::TemplateString, token::Numeric, Cursor, Error, Interner, Lexer, Position, Punctuator, template::TemplateString,
Read, Span, TokenKind, token::{ContainsEscapeSequence, EscapeSequence, Numeric},
Cursor, Error, Interner, Lexer, Position, Punctuator, Read, Span, TokenKind,
}; };
use boa_ast::Keyword; use boa_ast::Keyword;
use boa_interner::Sym; use boa_interner::Sym;
@ -94,9 +95,18 @@ fn check_identifier() {
TokenKind::identifier( TokenKind::identifier(
interner.get_or_intern_static("x\u{200C}\u{200D}", utf16!("x\u{200C}\u{200D}")), interner.get_or_intern_static("x\u{200C}\u{200D}", utf16!("x\u{200C}\u{200D}")),
), ),
TokenKind::identifier(interner.get_or_intern_static("x", utf16!("x"))), TokenKind::Identifier((
TokenKind::identifier(interner.get_or_intern_static("xx", utf16!("xx"))), interner.get_or_intern_static("x", utf16!("x")),
TokenKind::identifier(interner.get_or_intern_static("xxx", utf16!("xxx"))), ContainsEscapeSequence(true),
)),
TokenKind::Identifier((
interner.get_or_intern_static("xx", utf16!("xx")),
ContainsEscapeSequence(true),
)),
TokenKind::Identifier((
interner.get_or_intern_static("xxx", utf16!("xxx")),
ContainsEscapeSequence(true),
)),
]; ];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);
@ -141,8 +151,8 @@ fn check_string() {
let a_sym = interner.get_or_intern_static("aaa", utf16!("aaa")); let a_sym = interner.get_or_intern_static("aaa", utf16!("aaa"));
let b_sym = interner.get_or_intern_static("bbb", utf16!("bbb")); let b_sym = interner.get_or_intern_static("bbb", utf16!("bbb"));
let expected = [ let expected = [
TokenKind::string_literal(a_sym), TokenKind::string_literal(a_sym, None),
TokenKind::string_literal(b_sym), TokenKind::string_literal(b_sym, None),
]; ];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);
@ -305,7 +315,7 @@ fn check_variable_definition_tokens() {
TokenKind::Keyword((Keyword::Let, false)), TokenKind::Keyword((Keyword::Let, false)),
TokenKind::identifier(a_sym), TokenKind::identifier(a_sym),
TokenKind::Punctuator(Punctuator::Assign), TokenKind::Punctuator(Punctuator::Assign),
TokenKind::string_literal(hello_sym), TokenKind::string_literal(hello_sym, None),
TokenKind::Punctuator(Punctuator::Semicolon), TokenKind::Punctuator(Punctuator::Semicolon),
]; ];
@ -943,7 +953,7 @@ fn string_unicode() {
let sym = interner.get_or_intern_static("中文", utf16!("中文")); let sym = interner.get_or_intern_static("中文", utf16!("中文"));
let expected = [ let expected = [
TokenKind::StringLiteral(sym), TokenKind::StringLiteral((sym, None)),
TokenKind::Punctuator(Punctuator::Semicolon), TokenKind::Punctuator(Punctuator::Semicolon),
]; ];
@ -957,7 +967,7 @@ fn string_unicode_escape_with_braces() {
let sym = let sym =
interner.get_or_intern_static("{\u{20ac}\u{a0}\u{a0}}", utf16!("{\u{20ac}\u{a0}\u{a0}}")); interner.get_or_intern_static("{\u{20ac}\u{a0}\u{a0}}", utf16!("{\u{20ac}\u{a0}\u{a0}}"));
let expected = [TokenKind::StringLiteral(sym)]; let expected = [TokenKind::StringLiteral((sym, None))];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);
@ -992,7 +1002,7 @@ fn string_unicode_escape_with_braces_2() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
let sym = interner.get_or_intern_static("\u{20ac}\u{a0}\u{a0}", utf16!("\u{20ac}\u{a0}\u{a0}")); let sym = interner.get_or_intern_static("\u{20ac}\u{a0}\u{a0}", utf16!("\u{20ac}\u{a0}\u{a0}"));
let expected = [TokenKind::StringLiteral(sym)]; let expected = [TokenKind::StringLiteral((sym, None))];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);
} }
@ -1005,7 +1015,7 @@ fn string_with_single_escape() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
let sym = interner.get_or_intern_static("Б", utf16!("Б")); let sym = interner.get_or_intern_static("Б", utf16!("Б"));
let expected = [TokenKind::StringLiteral(sym)]; let expected = [TokenKind::StringLiteral((sym, None))];
expect_tokens(&mut lexer, &expected, interner); expect_tokens(&mut lexer, &expected, interner);
} }
@ -1027,7 +1037,10 @@ fn string_legacy_octal_escape() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice()); let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice());
let expected_tokens = [TokenKind::StringLiteral(sym)]; let expected_tokens = [TokenKind::StringLiteral((
sym,
Some(EscapeSequence::LegacyOctal),
))];
expect_tokens(&mut lexer, &expected_tokens, interner); expect_tokens(&mut lexer, &expected_tokens, interner);
} }
@ -1057,7 +1070,7 @@ fn string_zero_escape() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice()); let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice());
let expected_tokens = [TokenKind::StringLiteral(sym)]; let expected_tokens = [TokenKind::StringLiteral((sym, None))];
expect_tokens(&mut lexer, &expected_tokens, interner); expect_tokens(&mut lexer, &expected_tokens, interner);
} }
@ -1072,7 +1085,10 @@ fn string_non_octal_decimal_escape() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice()); let sym = interner.get_or_intern(expected.encode_utf16().collect::<Vec<_>>().as_slice());
let expected_tokens = [TokenKind::StringLiteral(sym)]; let expected_tokens = [TokenKind::StringLiteral((
sym,
Some(EscapeSequence::NonOctalDecimal),
))];
expect_tokens(&mut lexer, &expected_tokens, interner); expect_tokens(&mut lexer, &expected_tokens, interner);
} }
@ -1101,7 +1117,7 @@ fn string_line_continuation() {
let interner = &mut Interner::default(); let interner = &mut Interner::default();
let sym = interner.get_or_intern_static("hello world", utf16!("hello world")); let sym = interner.get_or_intern_static("hello world", utf16!("hello world"));
let expected_tokens = [TokenKind::StringLiteral(sym)]; let expected_tokens = [TokenKind::StringLiteral((sym, None))];
expect_tokens(&mut lexer, &expected_tokens, interner); expect_tokens(&mut lexer, &expected_tokens, interner);
} }

40
boa_parser/src/lexer/token.rs

@ -99,7 +99,7 @@ pub enum TokenKind {
EOF, EOF,
/// An identifier. /// An identifier.
Identifier(Sym), Identifier((Sym, ContainsEscapeSequence)),
/// A private identifier. /// A private identifier.
PrivateIdentifier(Sym), PrivateIdentifier(Sym),
@ -117,7 +117,7 @@ pub enum TokenKind {
Punctuator(Punctuator), Punctuator(Punctuator),
/// A string literal. /// A string literal.
StringLiteral(Sym), StringLiteral((Sym, Option<EscapeSequence>)),
/// A part of a template literal without substitution. /// A part of a template literal without substitution.
TemplateNoSubstitution(TemplateString), TemplateNoSubstitution(TemplateString),
@ -175,7 +175,7 @@ impl TokenKind {
/// Creates an `Identifier` token type. /// Creates an `Identifier` token type.
#[must_use] #[must_use]
pub const fn identifier(ident: Sym) -> Self { pub const fn identifier(ident: Sym) -> Self {
Self::Identifier(ident) Self::Identifier((ident, ContainsEscapeSequence(false)))
} }
/// Creates a `NumericLiteral` token kind. /// Creates a `NumericLiteral` token kind.
@ -194,8 +194,8 @@ impl TokenKind {
/// Creates a `StringLiteral` token type. /// Creates a `StringLiteral` token type.
#[must_use] #[must_use]
pub const fn string_literal(lit: Sym) -> Self { pub const fn string_literal(lit: Sym, escape_sequence: Option<EscapeSequence>) -> Self {
Self::StringLiteral(lit) Self::StringLiteral((lit, escape_sequence))
} }
/// Creates a `TemplateMiddle` token type. /// Creates a `TemplateMiddle` token type.
@ -234,7 +234,7 @@ impl TokenKind {
match *self { match *self {
Self::BooleanLiteral(val) => val.to_string(), Self::BooleanLiteral(val) => val.to_string(),
Self::EOF => "end of file".to_owned(), Self::EOF => "end of file".to_owned(),
Self::Identifier(ident) => interner.resolve_expect(ident).to_string(), Self::Identifier((ident, _)) => interner.resolve_expect(ident).to_string(),
Self::PrivateIdentifier(ident) => format!("#{}", interner.resolve_expect(ident)), Self::PrivateIdentifier(ident) => format!("#{}", interner.resolve_expect(ident)),
Self::Keyword((word, _)) => word.to_string(), Self::Keyword((word, _)) => word.to_string(),
Self::NullLiteral => "null".to_owned(), Self::NullLiteral => "null".to_owned(),
@ -242,7 +242,7 @@ impl TokenKind {
Self::NumericLiteral(Numeric::Integer(num)) => num.to_string(), Self::NumericLiteral(Numeric::Integer(num)) => num.to_string(),
Self::NumericLiteral(Numeric::BigInt(ref num)) => format!("{num}n"), Self::NumericLiteral(Numeric::BigInt(ref num)) => format!("{num}n"),
Self::Punctuator(punc) => punc.to_string(), Self::Punctuator(punc) => punc.to_string(),
Self::StringLiteral(lit) => interner.resolve_expect(lit).to_string(), Self::StringLiteral((lit, _)) => interner.resolve_expect(lit).to_string(),
Self::TemplateNoSubstitution(ts) | Self::TemplateMiddle(ts) => { Self::TemplateNoSubstitution(ts) | Self::TemplateMiddle(ts) => {
interner.resolve_expect(ts.as_raw()).to_string() interner.resolve_expect(ts.as_raw()).to_string()
} }
@ -258,3 +258,29 @@ impl TokenKind {
} }
} }
} }
/// Indicates the type of an escape sequence.
#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum EscapeSequence {
/// A legacy escape sequence starting with `0` - `7`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-LegacyOctalEscapeSequence
LegacyOctal,
/// A octal escape sequence starting with `8` - `9`.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#prod-NonOctalDecimalEscapeSequence
NonOctalDecimal,
}
/// Indicates if an identifier contains an escape sequence.
#[cfg_attr(feature = "deser", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ContainsEscapeSequence(pub bool);

10
boa_parser/src/parser/expression/identifiers.rs

@ -65,7 +65,7 @@ where
let token = cursor.next(interner).or_abrupt()?; let token = cursor.next(interner).or_abrupt()?;
match token.kind() { match token.kind() {
TokenKind::Identifier(ident) TokenKind::Identifier((ident, _))
if cursor.strict_mode() && RESERVED_IDENTIFIERS_STRICT.contains(ident) => if cursor.strict_mode() && RESERVED_IDENTIFIERS_STRICT.contains(ident) =>
{ {
Err(Error::general( Err(Error::general(
@ -73,7 +73,7 @@ where
token.span().start(), token.span().start(),
)) ))
} }
TokenKind::Identifier(ident) => Ok(Identifier::new(*ident)), TokenKind::Identifier((ident, _)) => Ok(Identifier::new(*ident)),
TokenKind::Keyword((Keyword::Let, _)) if cursor.strict_mode() => Err(Error::general( TokenKind::Keyword((Keyword::Let, _)) if cursor.strict_mode() => Err(Error::general(
"using future reserved keyword not allowed in strict mode IdentifierReference", "using future reserved keyword not allowed in strict mode IdentifierReference",
token.span().start(), token.span().start(),
@ -155,19 +155,19 @@ where
let next_token = cursor.next(interner).or_abrupt()?; let next_token = cursor.next(interner).or_abrupt()?;
match next_token.kind() { match next_token.kind() {
TokenKind::Identifier(Sym::ARGUMENTS) if cursor.strict_mode() => { TokenKind::Identifier((Sym::ARGUMENTS, _)) if cursor.strict_mode() => {
Err(Error::lex(LexError::Syntax( Err(Error::lex(LexError::Syntax(
"unexpected identifier 'arguments' in strict mode".into(), "unexpected identifier 'arguments' in strict mode".into(),
next_token.span().start(), next_token.span().start(),
))) )))
} }
TokenKind::Identifier(Sym::EVAL) if cursor.strict_mode() => { TokenKind::Identifier((Sym::EVAL, _)) if cursor.strict_mode() => {
Err(Error::lex(LexError::Syntax( Err(Error::lex(LexError::Syntax(
"unexpected identifier 'eval' in strict mode".into(), "unexpected identifier 'eval' in strict mode".into(),
next_token.span().start(), next_token.span().start(),
))) )))
} }
TokenKind::Identifier(ident) => { TokenKind::Identifier((ident, _)) => {
if cursor.strict_mode() && RESERVED_IDENTIFIERS_STRICT.contains(ident) { if cursor.strict_mode() && RESERVED_IDENTIFIERS_STRICT.contains(ident) {
return Err(Error::general( return Err(Error::general(
"using future reserved keyword not allowed in strict mode", "using future reserved keyword not allowed in strict mode",

4
boa_parser/src/parser/expression/left_hand_side/call.rs

@ -98,7 +98,9 @@ where
cursor.advance(interner); cursor.advance(interner);
let access = match cursor.next(interner).or_abrupt()?.kind() { let access = match cursor.next(interner).or_abrupt()?.kind() {
TokenKind::Identifier(name) => SimplePropertyAccess::new(lhs, *name).into(), TokenKind::Identifier((name, _)) => {
SimplePropertyAccess::new(lhs, *name).into()
}
TokenKind::Keyword((kw, _)) => { TokenKind::Keyword((kw, _)) => {
SimplePropertyAccess::new(lhs, kw.to_sym(interner)).into() SimplePropertyAccess::new(lhs, kw.to_sym(interner)).into()
} }

16
boa_parser/src/parser/expression/left_hand_side/member.rs

@ -7,7 +7,7 @@
use super::arguments::Arguments; use super::arguments::Arguments;
use crate::{ use crate::{
lexer::{InputElement, TokenKind}, lexer::{token::ContainsEscapeSequence, InputElement, TokenKind},
parser::{ parser::{
expression::{ expression::{
left_hand_side::template::TaggedTemplateLiteral, primary::PrimaryExpression, Expression, left_hand_side::template::TaggedTemplateLiteral, primary::PrimaryExpression, Expression,
@ -85,7 +85,13 @@ where
if cursor.next_if(Punctuator::Dot, interner)?.is_some() { if cursor.next_if(Punctuator::Dot, interner)?.is_some() {
let token = cursor.next(interner).or_abrupt()?; let token = cursor.next(interner).or_abrupt()?;
match token.kind() { match token.kind() {
TokenKind::Identifier(Sym::TARGET) => { TokenKind::Identifier((Sym::TARGET, ContainsEscapeSequence(true))) => {
return Err(Error::general(
"'new.target' must not contain escaped characters",
token.span().start(),
));
}
TokenKind::Identifier((Sym::TARGET, ContainsEscapeSequence(false))) => {
return Ok(ast::Expression::NewTarget) return Ok(ast::Expression::NewTarget)
} }
_ => { _ => {
@ -116,7 +122,7 @@ where
TokenKind::Punctuator(Punctuator::Dot) => { TokenKind::Punctuator(Punctuator::Dot) => {
let token = cursor.next(interner).or_abrupt()?; let token = cursor.next(interner).or_abrupt()?;
let field = match token.kind() { let field = match token.kind() {
TokenKind::Identifier(name) => { TokenKind::Identifier((name, _)) => {
SuperPropertyAccess::new(PropertyAccessField::from(*name)) SuperPropertyAccess::new(PropertyAccessField::from(*name))
} }
TokenKind::Keyword((kw, _)) => { TokenKind::Keyword((kw, _)) => {
@ -178,7 +184,9 @@ where
let token = cursor.next(interner).or_abrupt()?; let token = cursor.next(interner).or_abrupt()?;
let access = match token.kind() { let access = match token.kind() {
TokenKind::Identifier(name) => SimplePropertyAccess::new(lhs, *name).into(), TokenKind::Identifier((name, _)) => {
SimplePropertyAccess::new(lhs, *name).into()
}
TokenKind::Keyword((kw, _)) => { TokenKind::Keyword((kw, _)) => {
SimplePropertyAccess::new(lhs, kw.to_sym(interner)).into() SimplePropertyAccess::new(lhs, kw.to_sym(interner)).into()
} }

2
boa_parser/src/parser/expression/left_hand_side/optional/mod.rs

@ -66,7 +66,7 @@ where
interner: &mut Interner, interner: &mut Interner,
) -> ParseResult<OptionalOperationKind> { ) -> ParseResult<OptionalOperationKind> {
let item = match token.kind() { let item = match token.kind() {
TokenKind::Identifier(name) => OptionalOperationKind::SimplePropertyAccess { TokenKind::Identifier((name, _)) => OptionalOperationKind::SimplePropertyAccess {
field: PropertyAccessField::Const(*name), field: PropertyAccessField::Const(*name),
}, },
TokenKind::Keyword((kw, _)) => OptionalOperationKind::SimplePropertyAccess { TokenKind::Keyword((kw, _)) => OptionalOperationKind::SimplePropertyAccess {

2
boa_parser/src/parser/expression/primary/mod.rs

@ -209,7 +209,7 @@ where
)) => IdentifierReference::new(self.allow_yield, self.allow_await) )) => IdentifierReference::new(self.allow_yield, self.allow_await)
.parse(cursor, interner) .parse(cursor, interner)
.map(Into::into), .map(Into::into),
TokenKind::StringLiteral(lit) => { TokenKind::StringLiteral((lit, _)) => {
let node = Literal::from(*lit).into(); let node = Literal::from(*lit).into();
cursor.advance(interner); cursor.advance(interner);
Ok(node) Ok(node)

35
boa_parser/src/parser/expression/primary/object_initializer/mod.rs

@ -11,7 +11,10 @@
mod tests; mod tests;
use crate::{ use crate::{
lexer::{token::Numeric, Error as LexError, TokenKind}, lexer::{
token::{ContainsEscapeSequence, Numeric},
Error as LexError, TokenKind,
},
parser::{ parser::{
expression::{identifiers::IdentifierReference, AssignmentExpression}, expression::{identifiers::IdentifierReference, AssignmentExpression},
function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters}, function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters},
@ -198,6 +201,7 @@ where
cursor.peek(1, interner).or_abrupt()?.kind(), cursor.peek(1, interner).or_abrupt()?.kind(),
TokenKind::Punctuator(Punctuator::OpenParen | Punctuator::Colon) TokenKind::Punctuator(Punctuator::OpenParen | Punctuator::Colon)
); );
let token = cursor.peek(0, interner).or_abrupt()?; let token = cursor.peek(0, interner).or_abrupt()?;
match token.kind() { match token.kind() {
TokenKind::Keyword((Keyword::Async, true)) if is_keyword => { TokenKind::Keyword((Keyword::Async, true)) if is_keyword => {
@ -258,7 +262,9 @@ where
_ => {} _ => {}
} }
if cursor.peek(0, interner).or_abrupt()?.kind() == &TokenKind::Punctuator(Punctuator::Mul) { let token = cursor.peek(0, interner).or_abrupt()?;
if token.kind() == &TokenKind::Punctuator(Punctuator::Mul) {
let position = cursor.peek(0, interner).or_abrupt()?.span().start(); let position = cursor.peek(0, interner).or_abrupt()?.span().start();
let (class_element_name, method) = let (class_element_name, method) =
GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
@ -284,6 +290,13 @@ where
} }
} }
let set_or_get_escaped_position = match token.kind() {
TokenKind::Identifier((Sym::GET | Sym::SET, ContainsEscapeSequence(true))) => {
Some(token.span().start())
}
_ => None,
};
let mut property_name = let mut property_name =
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?; PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
@ -306,6 +319,13 @@ where
match property_name { match property_name {
// MethodDefinition[?Yield, ?Await] -> get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] } // MethodDefinition[?Yield, ?Await] -> get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] }
property::PropertyName::Literal(str) if str == Sym::GET && !ordinary_method => { property::PropertyName::Literal(str) if str == Sym::GET && !ordinary_method => {
if let Some(position) = set_or_get_escaped_position {
return Err(Error::general(
"Keyword must not contain escaped characters",
position,
));
}
let position = cursor.peek(0, interner).or_abrupt()?.span().start(); let position = cursor.peek(0, interner).or_abrupt()?.span().start();
property_name = PropertyName::new(self.allow_yield, self.allow_await) property_name = PropertyName::new(self.allow_yield, self.allow_await)
@ -359,6 +379,13 @@ where
} }
// MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] } // MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
property::PropertyName::Literal(str) if str == Sym::SET && !ordinary_method => { property::PropertyName::Literal(str) if str == Sym::SET && !ordinary_method => {
if let Some(position) = set_or_get_escaped_position {
return Err(Error::general(
"Keyword must not contain escaped characters",
position,
));
}
property_name = PropertyName::new(self.allow_yield, self.allow_await) property_name = PropertyName::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?; .parse(cursor, interner)?;
@ -551,7 +578,9 @@ where
cursor.expect(Punctuator::CloseBracket, "expected token ']'", interner)?; cursor.expect(Punctuator::CloseBracket, "expected token ']'", interner)?;
return Ok(node.into()); return Ok(node.into());
} }
TokenKind::Identifier(name) | TokenKind::StringLiteral(name) => (*name).into(), TokenKind::Identifier((name, _)) | TokenKind::StringLiteral((name, _)) => {
(*name).into()
}
TokenKind::NumericLiteral(num) => match num { TokenKind::NumericLiteral(num) => match num {
Numeric::Rational(num) => Expression::Literal(Literal::from(*num)).into(), Numeric::Rational(num) => Expression::Literal(Literal::from(*num)).into(),
Numeric::Integer(num) => Expression::Literal(Literal::from(*num)).into(), Numeric::Integer(num) => Expression::Literal(Literal::from(*num)).into(),

68
boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -2,7 +2,7 @@
mod tests; mod tests;
use crate::{ use crate::{
lexer::{Error as LexError, TokenKind}, lexer::{token::ContainsEscapeSequence, Error as LexError, TokenKind},
parser::{ parser::{
expression::{ expression::{
AssignmentExpression, AsyncGeneratorMethod, AsyncMethod, BindingIdentifier, AssignmentExpression, AsyncGeneratorMethod, AsyncMethod, BindingIdentifier,
@ -595,7 +595,8 @@ where
cursor.advance(interner); cursor.advance(interner);
return Ok((None, None)); return Ok((None, None));
} }
TokenKind::Identifier(Sym::STATIC) => { TokenKind::Identifier((Sym::STATIC, ContainsEscapeSequence(contains_escape))) => {
let contains_escape = *contains_escape;
let token = cursor.peek(1, interner).or_abrupt()?; let token = cursor.peek(1, interner).or_abrupt()?;
match token.kind() { match token.kind() {
TokenKind::Identifier(_) TokenKind::Identifier(_)
@ -607,6 +608,12 @@ where
| TokenKind::Punctuator( | TokenKind::Punctuator(
Punctuator::OpenBracket | Punctuator::Mul | Punctuator::OpenBlock, Punctuator::OpenBracket | Punctuator::Mul | Punctuator::OpenBlock,
) => { ) => {
if contains_escape {
return Err(Error::general(
"keyword must not contain escaped characters",
token.span().start(),
));
}
// this "static" is a keyword. // this "static" is a keyword.
cursor.advance(interner); cursor.advance(interner);
true true
@ -630,7 +637,7 @@ where
let token = cursor.peek(0, interner).or_abrupt()?; let token = cursor.peek(0, interner).or_abrupt()?;
let position = token.span().start(); let position = token.span().start();
let element = match token.kind() { let element = match token.kind() {
TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { TokenKind::Identifier((Sym::CONSTRUCTOR, _)) if !r#static => {
cursor.advance(interner); cursor.advance(interner);
let strict = cursor.strict_mode(); let strict = cursor.strict_mode();
cursor.set_strict_mode(true); cursor.set_strict_mode(true);
@ -708,11 +715,13 @@ where
TokenKind::Punctuator(Punctuator::Mul) => { TokenKind::Punctuator(Punctuator::Mul) => {
let token = cursor.peek(1, interner).or_abrupt()?; let token = cursor.peek(1, interner).or_abrupt()?;
let name_position = token.span().start(); let name_position = token.span().start();
if token.kind() == &TokenKind::Identifier(Sym::CONSTRUCTOR) && !r#static { if !r#static {
return Err(Error::general( if let TokenKind::Identifier((Sym::CONSTRUCTOR, _)) = token.kind() {
"class constructor may not be a generator method", return Err(Error::general(
token.span().start(), "class constructor may not be a generator method",
)); token.span().start(),
));
}
} }
let strict = cursor.strict_mode(); let strict = cursor.strict_mode();
cursor.set_strict_mode(true); cursor.set_strict_mode(true);
@ -764,13 +773,20 @@ where
TokenKind::Punctuator(Punctuator::Mul) => { TokenKind::Punctuator(Punctuator::Mul) => {
let token = cursor.peek(1, interner).or_abrupt()?; let token = cursor.peek(1, interner).or_abrupt()?;
let name_position = token.span().start(); let name_position = token.span().start();
if token.kind() == &TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) match token.kind() {
|| token.kind() == &TokenKind::Identifier(Sym::CONSTRUCTOR) && !r#static TokenKind::PrivateIdentifier(Sym::CONSTRUCTOR) => {
{ return Err(Error::general(
return Err(Error::general( "class constructor may not be a private method",
"class constructor may not be a generator method", token.span().start(),
token.span().start(), ));
)); }
TokenKind::Identifier((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general(
"class constructor may not be a generator method",
token.span().start(),
));
}
_ => {}
} }
let strict = cursor.strict_mode(); let strict = cursor.strict_mode();
cursor.set_strict_mode(true); cursor.set_strict_mode(true);
@ -808,7 +824,7 @@ where
} }
} }
} }
TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { TokenKind::Identifier((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general( return Err(Error::general(
"class constructor may not be an async method", "class constructor may not be an async method",
token.span().start(), token.span().start(),
@ -859,7 +875,13 @@ where
} }
} }
} }
TokenKind::Identifier(Sym::GET) if is_keyword => { TokenKind::Identifier((Sym::GET, ContainsEscapeSequence(true))) if is_keyword => {
return Err(Error::general(
"keyword must not contain escaped characters",
token.span().start(),
))
}
TokenKind::Identifier((Sym::GET, ContainsEscapeSequence(false))) if is_keyword => {
cursor.advance(interner); cursor.advance(interner);
let token = cursor.peek(0, interner).or_abrupt()?; let token = cursor.peek(0, interner).or_abrupt()?;
match token.kind() { match token.kind() {
@ -911,7 +933,7 @@ where
) )
} }
} }
TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { TokenKind::Identifier((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general( return Err(Error::general(
"class constructor may not be a getter method", "class constructor may not be a getter method",
token.span().start(), token.span().start(),
@ -984,7 +1006,13 @@ where
} }
} }
} }
TokenKind::Identifier(Sym::SET) if is_keyword => { TokenKind::Identifier((Sym::SET, ContainsEscapeSequence(true))) if is_keyword => {
return Err(Error::general(
"keyword must not contain escaped characters",
token.span().start(),
))
}
TokenKind::Identifier((Sym::SET, ContainsEscapeSequence(false))) if is_keyword => {
cursor.advance(interner); cursor.advance(interner);
let token = cursor.peek(0, interner).or_abrupt()?; let token = cursor.peek(0, interner).or_abrupt()?;
match token.kind() { match token.kind() {
@ -1036,7 +1064,7 @@ where
) )
} }
} }
TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => { TokenKind::Identifier((Sym::CONSTRUCTOR, _)) if !r#static => {
return Err(Error::general( return Err(Error::general(
"class constructor may not be a setter method", "class constructor may not be a setter method",
token.span().start(), token.span().start(),

24
boa_parser/src/parser/statement/mod.rs

@ -37,7 +37,7 @@ use self::{
variable::VariableStatement, variable::VariableStatement,
}; };
use crate::{ use crate::{
lexer::{Error as LexError, InputElement, Token, TokenKind}, lexer::{token::EscapeSequence, Error as LexError, InputElement, Token, TokenKind},
parser::{ parser::{
expression::{BindingIdentifier, Initializer, PropertyName}, expression::{BindingIdentifier, Initializer, PropertyName},
AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser, AllowAwait, AllowReturn, AllowYield, Cursor, OrAbrupt, ParseResult, TokenParser,
@ -282,10 +282,17 @@ where
let global_strict = cursor.strict_mode(); let global_strict = cursor.strict_mode();
let mut directive_prologues = self.directive_prologues; let mut directive_prologues = self.directive_prologues;
let mut strict = self.strict; let mut strict = self.strict;
let mut string_literal_escape_sequence = None;
loop { loop {
match cursor.peek(0, interner)? { match cursor.peek(0, interner)? {
Some(token) if self.break_nodes.contains(token.kind()) => break, Some(token) if self.break_nodes.contains(token.kind()) => break,
Some(token) if directive_prologues && string_literal_escape_sequence.is_none() => {
if let TokenKind::StringLiteral((_, Some(escape_sequence))) = token.kind() {
string_literal_escape_sequence =
Some((token.span().start(), *escape_sequence));
}
}
None => break, None => break,
_ => {} _ => {}
} }
@ -306,6 +313,21 @@ where
) { ) {
cursor.set_strict_mode(true); cursor.set_strict_mode(true);
strict = true; strict = true;
if let Some((position, escape_sequence)) = string_literal_escape_sequence {
match escape_sequence {
EscapeSequence::LegacyOctal => return Err(Error::general(
"legacy octal escape sequences are not allowed in strict mode",
position,
)),
EscapeSequence::NonOctalDecimal => {
return Err(Error::general(
"decimal escape sequences are not allowed in strict mode",
position,
))
}
}
}
} }
} else { } else {
directive_prologues = false; directive_prologues = false;

Loading…
Cancel
Save