Browse Source

Implement HTML comments and gate behind the `annex-b` feature (#2817)

Small steps towards ES5 conformance.

This PR changes the following:

- Implements HTML comments parsing (`<!--`, `-->`).
- Gates the functionality behind a new `annex-b` feature for `boa_parser`.
- Renames `strict_mode` to `strict` to be consistent with `Parser::set_strict`.
pull/2821/head
José Julián Espina 2 years ago
parent
commit
460051261a
  1. 2
      boa_engine/Cargo.toml
  2. 5
      boa_engine/src/builtins/function/mod.rs
  3. 3
      boa_parser/Cargo.toml
  4. 2
      boa_parser/src/lexer/comment.rs
  5. 28
      boa_parser/src/lexer/cursor.rs
  6. 112
      boa_parser/src/lexer/mod.rs
  7. 4
      boa_parser/src/lexer/number.rs
  8. 12
      boa_parser/src/lexer/string.rs
  9. 4
      boa_parser/src/lexer/tests.rs
  10. 25
      boa_parser/src/parser/cursor/buffered_lexer/mod.rs
  11. 20
      boa_parser/src/parser/cursor/mod.rs
  12. 5
      boa_parser/src/parser/expression/assignment/mod.rs
  13. 6
      boa_parser/src/parser/expression/identifiers.rs
  14. 4
      boa_parser/src/parser/expression/primary/async_function_expression/mod.rs
  15. 4
      boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs
  16. 6
      boa_parser/src/parser/expression/primary/class_expression/mod.rs
  17. 4
      boa_parser/src/parser/expression/primary/function_expression/mod.rs
  18. 4
      boa_parser/src/parser/expression/primary/generator_expression/mod.rs
  19. 2
      boa_parser/src/parser/expression/primary/mod.rs
  20. 2
      boa_parser/src/parser/expression/unary.rs
  21. 8
      boa_parser/src/parser/expression/update.rs
  22. 32
      boa_parser/src/parser/function/mod.rs
  23. 8
      boa_parser/src/parser/mod.rs
  24. 2
      boa_parser/src/parser/statement/block/mod.rs
  25. 102
      boa_parser/src/parser/statement/declaration/hoistable/class_decl/mod.rs
  26. 5
      boa_parser/src/parser/statement/declaration/hoistable/mod.rs
  27. 4
      boa_parser/src/parser/statement/if_stm/mod.rs
  28. 2
      boa_parser/src/parser/statement/iteration/for_statement.rs
  29. 2
      boa_parser/src/parser/statement/labelled_stm/mod.rs
  30. 6
      boa_parser/src/parser/statement/mod.rs
  31. 2
      boa_parser/src/parser/statement/switch/mod.rs
  32. 2
      boa_parser/src/parser/statement/with/mod.rs

2
boa_engine/Cargo.toml

@ -40,7 +40,7 @@ trace = []
console = []
# Enable Boa's additional ECMAScript features for web browsers.
annex-b = []
annex-b = ["boa_parser/annex-b"]
[dependencies]
boa_interner.workspace = true

5
boa_engine/src/builtins/function/mod.rs

@ -614,10 +614,9 @@ impl BuiltInFunctionObject {
} else {
let mut parameters = Vec::with_capacity(args.len());
for arg in args {
parameters.push(arg.to_string(context)?.as_slice().to_owned());
parameters.push(arg.to_string(context)?);
}
let mut parameters = parameters.join(utf16!(","));
parameters.push(u16::from(b')'));
let parameters = parameters.join(utf16!(","));
// TODO: make parser generic to u32 iterators
let parameters =

3
boa_parser/Cargo.toml

@ -22,3 +22,6 @@ num-traits = "0.2.15"
bitflags = "2.1.0"
num-bigint = "0.4.3"
regress = "0.5.0"
[features]
annex-b = []

2
boa_parser/src/lexer/comment.rs

@ -98,7 +98,7 @@ impl<R> Tokenizer<R> for MultiLineComment {
}
}
///Lexes a first line Hashbang comment
/// Lexes a first line Hashbang comment
///
/// More information:
/// - [ECMAScript reference][spec]

28
boa_parser/src/lexer/cursor.rs

@ -8,7 +8,8 @@ use std::io::{self, Bytes, Error, ErrorKind, Read};
pub(super) struct Cursor<R> {
iter: InnerIter<R>,
pos: Position,
strict_mode: bool,
module: bool,
strict: bool,
}
impl<R> Cursor<R> {
@ -31,13 +32,24 @@ impl<R> Cursor<R> {
}
/// Returns if strict mode is currently active.
pub(super) const fn strict_mode(&self) -> bool {
self.strict_mode
pub(super) const fn strict(&self) -> bool {
self.strict
}
/// Sets the current strict mode.
pub(super) fn set_strict_mode(&mut self, strict_mode: bool) {
self.strict_mode = strict_mode;
pub(super) fn set_strict(&mut self, strict: bool) {
self.strict = strict;
}
/// Returns if the module mode is currently active.
pub(super) const fn module(&self) -> bool {
self.module
}
/// Sets the current goal symbol to module.
pub(super) fn set_module(&mut self, module: bool) {
self.module = module;
self.strict = module;
}
}
@ -50,7 +62,8 @@ where
Self {
iter: InnerIter::new(inner.bytes()),
pos: Position::new(1, 1),
strict_mode: false,
strict: false,
module: false,
}
}
@ -59,7 +72,8 @@ where
Self {
iter: InnerIter::new(inner.bytes()),
pos,
strict_mode: false,
strict: false,
module: false,
}
}

112
boa_parser/src/lexer/mod.rs

@ -71,23 +71,6 @@ pub struct Lexer<R> {
}
impl<R> Lexer<R> {
/// Checks if a character is whitespace as per ECMAScript standards.
///
/// The Rust `char::is_whitespace` function and the ECMAScript standard use different sets of
/// characters as whitespaces:
/// * Rust uses `\p{White_Space}`,
/// * ECMAScript standard uses `\{Space_Separator}` + `\u{0009}`, `\u{000B}`, `\u{000C}`, `\u{FEFF}`
///
/// [More information](https://tc39.es/ecma262/#table-32)
const fn is_whitespace(ch: u32) -> bool {
matches!(
ch,
0x0020 | 0x0009 | 0x000B | 0x000C | 0x00A0 | 0xFEFF |
// Unicode Space_Seperator category (minus \u{0020} and \u{00A0} which are allready stated above)
0x1680 | 0x2000..=0x200A | 0x202F | 0x205F | 0x3000
)
}
/// Sets the goal symbol for the lexer.
pub(crate) fn set_goal(&mut self, elm: InputElement) {
self.goal_symbol = elm;
@ -99,13 +82,23 @@ impl<R> Lexer<R> {
}
/// Returns if strict mode is currently active.
pub(super) const fn strict_mode(&self) -> bool {
self.cursor.strict_mode()
pub(super) const fn strict(&self) -> bool {
self.cursor.strict()
}
/// Sets the current strict mode.
pub(super) fn set_strict_mode(&mut self, strict_mode: bool) {
self.cursor.set_strict_mode(strict_mode);
pub(super) fn set_strict(&mut self, strict: bool) {
self.cursor.set_strict(strict);
}
/// Returns if module mode is currently active.
pub(super) const fn module(&self) -> bool {
self.cursor.module()
}
/// Signals that the goal symbol is a module
pub(super) fn set_module(&mut self, module: bool) {
self.cursor.set_module(module);
}
/// Creates a new lexer.
@ -180,14 +173,38 @@ impl<R> Lexer<R> {
}
}
/// Skips an HTML close comment (`-->`) if the `annex-b` feature is enabled.
pub(crate) fn skip_html_close(&mut self, interner: &mut Interner) -> Result<(), Error>
where
R: Read,
{
if !cfg!(feature = "annex-b") || self.module() {
return Ok(());
}
while self.cursor.peek_char()?.map_or(false, is_whitespace) {
let _next = self.cursor.next_char();
}
if self.cursor.peek_n(3)? == [b'-', b'-', b'>'] {
let _next = self.cursor.next_byte();
let _next = self.cursor.next_byte();
let _next = self.cursor.next_byte();
let start = self.cursor.pos();
SingleLineComment.lex(&mut self.cursor, start, interner)?;
}
Ok(())
}
/// Retrieves the next token from the lexer.
///
/// # Errors
///
/// Will return `Err` on invalid tokens and invalid reads of the bytes being lexed.
// We intentionally don't implement Iterator trait as Result<Option> is cleaner to handle.
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self, interner: &mut Interner) -> Result<Option<Token>, Error>
pub(crate) fn next_no_skip(&mut self, interner: &mut Interner) -> Result<Option<Token>, Error>
where
R: Read,
{
@ -197,7 +214,7 @@ impl<R> Lexer<R> {
let start = self.cursor.pos();
if let Some(next_ch) = self.cursor.next_char()? {
// Ignore whitespace
if !Self::is_whitespace(next_ch) {
if !is_whitespace(next_ch) {
break (start, next_ch);
}
} else {
@ -269,6 +286,14 @@ impl<R> Lexer<R> {
)),
'#' => PrivateIdentifier::new().lex(&mut self.cursor, start, interner),
'/' => self.lex_slash_token(start, interner),
#[cfg(feature = "annex-b")]
'<' if !self.module() && self.cursor.peek_n(3)? == [b'!', b'-', b'-'] => {
let _next = self.cursor.next_byte();
let _next = self.cursor.next_byte();
let _next = self.cursor.next_byte();
let start = self.cursor.pos();
SingleLineComment.lex(&mut self.cursor, start, interner)
}
#[allow(clippy::cast_possible_truncation)]
'=' | '*' | '+' | '-' | '%' | '|' | '&' | '^' | '<' | '>' | '!' | '~' | '?' => {
Operator::new(next_ch as u8).lex(&mut self.cursor, start, interner)
@ -311,6 +336,28 @@ impl<R> Lexer<R> {
}
}
/// Retrieves the next token from the lexer, skipping comments.
///
/// # Errors
///
/// Will return `Err` on invalid tokens and invalid reads of the bytes being lexed.
// We intentionally don't implement Iterator trait as Result<Option> is cleaner to handle.
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self, interner: &mut Interner) -> Result<Option<Token>, Error>
where
R: Read,
{
loop {
let Some(next) = self.next_no_skip(interner)? else {
return Ok(None)
};
if next.kind() != &TokenKind::Comment {
return Ok(Some(next));
}
}
}
/// Performs the lexing of a template literal.
pub(crate) fn lex_template(
&mut self,
@ -339,3 +386,20 @@ impl Default for InputElement {
Self::RegExp
}
}
/// Checks if a character is whitespace as per ECMAScript standards.
///
/// The Rust `char::is_whitespace` function and the ECMAScript standard use different sets of
/// characters as whitespaces:
/// * Rust uses `\p{White_Space}`,
/// * ECMAScript standard uses `\{Space_Separator}` + `\u{0009}`, `\u{000B}`, `\u{000C}`, `\u{FEFF}`
///
/// [More information](https://tc39.es/ecma262/#table-32)
const fn is_whitespace(ch: u32) -> bool {
matches!(
ch,
0x0020 | 0x0009 | 0x000B | 0x000C | 0x00A0 | 0xFEFF |
// Unicode Space_Seperator category (minus \u{0020} and \u{00A0} which are allready stated above)
0x1680 | 0x2000..=0x200A | 0x202F | 0x205F | 0x3000
)
}

4
boa_parser/src/lexer/number.rs

@ -255,7 +255,7 @@ impl<R> Tokenizer<R> for NumberLiteral {
let ch = char::from(byte);
if ch.is_digit(8) {
// LegacyOctalIntegerLiteral, or a number with leading 0s.
if cursor.strict_mode() {
if cursor.strict() {
// LegacyOctalIntegerLiteral is forbidden with strict mode true.
return Err(Error::syntax(
"implicit octal literals are not allowed in strict mode",
@ -278,7 +278,7 @@ impl<R> Tokenizer<R> for NumberLiteral {
// Indicates a numerical digit comes after then 0 but it isn't an octal digit
// so therefore this must be a number with an unneeded leading 0. This is
// forbidden in strict mode.
if cursor.strict_mode() {
if cursor.strict() {
return Err(Error::syntax(
"leading 0's are not allowed in strict mode",
start_pos,

12
boa_parser/src/lexer/string.rs

@ -89,7 +89,7 @@ impl<R> Tokenizer<R> for StringLiteral {
let _timer = Profiler::global().start_event("StringLiteral", "Lexing");
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())?;
Ok(Token::new(
TokenKind::string_literal(interner.get_or_intern(&lit[..]), escape_sequence),
@ -116,7 +116,7 @@ impl StringLiteral {
cursor: &mut Cursor<R>,
start_pos: Position,
terminator: StringTerminator,
is_strict_mode: bool,
strict: bool,
) -> Result<(Vec<u16>, Span, Option<EscapeSequence>), Error>
where
R: Read,
@ -139,7 +139,7 @@ impl StringLiteral {
Self::take_escape_sequence_or_line_continuation(
cursor,
ch_start_pos,
is_strict_mode,
strict,
false,
)?
{
@ -167,7 +167,7 @@ impl StringLiteral {
pub(super) fn take_escape_sequence_or_line_continuation<R>(
cursor: &mut Cursor<R>,
start_pos: Position,
is_strict_mode: bool,
strict: bool,
is_template_literal: bool,
) -> Result<Option<(u32, Option<EscapeSequence>)>, Error>
where
@ -208,7 +208,7 @@ impl StringLiteral {
"\\8 and \\9 are not allowed in template literal",
start_pos,
));
} else if is_strict_mode {
} else if strict {
return Err(Error::syntax(
"\\8 and \\9 are not allowed in strict mode",
start_pos,
@ -224,7 +224,7 @@ impl StringLiteral {
));
}
if is_strict_mode {
if strict {
return Err(Error::syntax(
"octal escape sequences are not allowed in strict mode",
start_pos,

4
boa_parser/src/lexer/tests.rs

@ -1048,7 +1048,7 @@ fn string_legacy_octal_escape() {
for (s, _) in &test_cases {
let mut lexer = Lexer::new(s.as_bytes());
let interner = &mut Interner::default();
lexer.set_strict_mode(true);
lexer.set_strict(true);
if let Error::Syntax(_, pos) = lexer
.next(interner)
@ -1096,7 +1096,7 @@ fn string_non_octal_decimal_escape() {
for (s, _) in &test_cases {
let mut lexer = Lexer::new(s.as_bytes());
let interner = &mut Interner::default();
lexer.set_strict_mode(true);
lexer.set_strict(true);
if let Error::Syntax(_, pos) = lexer
.next(interner)

25
boa_parser/src/parser/cursor/buffered_lexer/mod.rs

@ -100,12 +100,20 @@ where
.map_err(Error::from)
}
pub(super) const fn strict_mode(&self) -> bool {
self.lexer.strict_mode()
pub(super) const fn strict(&self) -> bool {
self.lexer.strict()
}
pub(super) fn set_strict_mode(&mut self, strict_mode: bool) {
self.lexer.set_strict_mode(strict_mode);
pub(super) fn set_strict(&mut self, strict: bool) {
self.lexer.set_strict(strict);
}
pub(super) const fn module(&self) -> bool {
self.lexer.module()
}
pub(super) fn set_module(&mut self, module: bool) {
self.lexer.set_module(module);
}
/// Fills the peeking buffer with the next token.
@ -124,10 +132,13 @@ where
// We don't want to have multiple contiguous line terminators in the buffer, since
// they have no meaning.
let next = loop {
let next = self.lexer.next(interner)?;
self.lexer.skip_html_close(interner)?;
let next = self.lexer.next_no_skip(interner)?;
if let Some(ref token) = next {
if token.kind() != &TokenKind::LineTerminator {
break next;
match token.kind() {
TokenKind::LineTerminator => { /* skip */ }
TokenKind::Comment => self.lexer.skip_html_close(interner)?,
_ => break next,
}
} else {
break None;

20
boa_parser/src/parser/cursor/mod.rs

@ -36,9 +36,6 @@ pub(super) struct Cursor<R> {
/// Indicate if the cursor is used in `JSON.parse`.
json_parse: bool,
/// Indicate if the cursor's **goal symbol** is a Module.
module: bool,
}
impl<R> Cursor<R>
@ -53,19 +50,18 @@ where
private_environment_root_index: 0,
arrow: false,
json_parse: false,
module: false,
}
}
/// Sets the goal symbol of the cursor to `Module`.
#[allow(unused)]
pub(super) fn set_module_mode(&mut self) {
self.module = true;
pub(super) fn set_module(&mut self) {
self.buffered_lexer.set_module(true);
}
/// Returns `true` if the cursor is currently parsing a `Module`.
pub(super) const fn module_mode(&self) -> bool {
self.module
pub(super) const fn module(&self) -> bool {
self.buffered_lexer.module()
}
pub(super) fn set_goal(&mut self, elm: InputElement) {
@ -116,13 +112,13 @@ where
}
/// Gets the current strict mode for the cursor.
pub(super) const fn strict_mode(&self) -> bool {
self.buffered_lexer.strict_mode()
pub(super) const fn strict(&self) -> bool {
self.buffered_lexer.strict()
}
/// Sets the strict mode to strict or non-strict.
pub(super) fn set_strict_mode(&mut self, strict_mode: bool) {
self.buffered_lexer.set_strict_mode(strict_mode);
pub(super) fn set_strict(&mut self, strict: bool) {
self.buffered_lexer.set_strict(strict);
}
/// Returns if the cursor is currently in an arrow function declaration.

5
boa_parser/src/parser/expression/assignment/mod.rs

@ -240,8 +240,7 @@ where
cursor.advance(interner);
cursor.set_goal(InputElement::RegExp);
if let Some(target) = AssignTarget::from_expression(&lhs, cursor.strict_mode())
{
if let Some(target) = AssignTarget::from_expression(&lhs, cursor.strict()) {
if let AssignTarget::Identifier(ident) = target {
self.name = Some(ident);
}
@ -257,7 +256,7 @@ where
TokenKind::Punctuator(p) if p.as_assign_op().is_some() => {
cursor.advance(interner);
if let Some(target) =
AssignTarget::from_expression_simple(&lhs, cursor.strict_mode())
AssignTarget::from_expression_simple(&lhs, cursor.strict())
{
let assignop = p.as_assign_op().expect("assignop disappeared");
if assignop == AssignOp::BoolAnd

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

@ -109,7 +109,7 @@ where
let span = cursor.peek(0, interner).or_abrupt()?.span();
let ident = Identifier.parse(cursor, interner)?;
match ident.sym() {
Sym::ARGUMENTS | Sym::EVAL if cursor.strict_mode() => {
Sym::ARGUMENTS | Sym::EVAL if cursor.strict() => {
let name = interner
.resolve_expect(ident.sym())
.utf8()
@ -178,7 +178,7 @@ where
}
};
if cursor.strict_mode() && ident.is_strict_reserved_identifier() {
if cursor.strict() && ident.is_strict_reserved_identifier() {
return Err(Error::unexpected(
interner
.resolve_expect(ident)
@ -189,7 +189,7 @@ where
));
}
if cursor.module_mode() && ident == Sym::AWAIT {
if cursor.module() && ident == Sym::AWAIT {
return Err(Error::unexpected(
"await",
tok.span(),

4
boa_parser/src/parser/expression/primary/async_function_expression/mod.rs

@ -97,7 +97,7 @@ where
// 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() {
if (cursor.strict() || body.strict()) && params.has_duplicates() {
return Err(Error::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
@ -116,7 +116,7 @@ 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())
if (cursor.strict() || body.strict())
&& [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())
{
return Err(Error::lex(LexError::Syntax(

4
boa_parser/src/parser/expression/primary/async_generator_expression/mod.rs

@ -135,7 +135,7 @@ where
// 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() {
if (cursor.strict() || body.strict()) && params.has_duplicates() {
return Err(Error::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
@ -154,7 +154,7 @@ 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())
if (cursor.strict() || body.strict())
&& [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())
{
return Err(Error::lex(LexError::Syntax(

6
boa_parser/src/parser/expression/primary/class_expression/mod.rs

@ -47,8 +47,8 @@ where
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("ClassExpression", "Parsing");
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let mut has_binding_identifier = false;
let token = cursor.peek(0, interner).or_abrupt()?;
@ -62,7 +62,7 @@ where
}
_ => self.name,
};
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
ClassTail::new(
name,

4
boa_parser/src/parser/expression/primary/function_expression/mod.rs

@ -92,7 +92,7 @@ where
// 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() {
if (cursor.strict() || body.strict()) && params.has_duplicates() {
return Err(Error::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
@ -111,7 +111,7 @@ 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())
if (cursor.strict() || body.strict())
&& [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())
{
return Err(Error::lex(LexError::Syntax(

4
boa_parser/src/parser/expression/primary/generator_expression/mod.rs

@ -99,7 +99,7 @@ where
// If the source text matched by FormalParameters is strict mode code,
// the Early Error rules for UniqueFormalParameters : FormalParameters are applied.
// https://tc39.es/ecma262/#sec-generator-function-definitions-static-semantics-early-errors
if (cursor.strict_mode() || body.strict()) && params.has_duplicates() {
if (cursor.strict() || body.strict()) && params.has_duplicates() {
return Err(Error::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
@ -119,7 +119,7 @@ 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())
if (cursor.strict() || body.strict())
&& [Sym::EVAL, Sym::ARGUMENTS].contains(&name.sym())
{
return Err(Error::lex(LexError::Syntax(

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

@ -515,7 +515,7 @@ where
expression_to_formal_parameters(
&node,
&mut parameters,
cursor.strict_mode(),
cursor.strict(),
start_span,
)?;
}

2
boa_parser/src/parser/expression/unary.rs

@ -79,7 +79,7 @@ where
let target = self.parse(cursor, interner)?;
match target.flatten() {
Expression::Identifier(_) if cursor.strict_mode() => {
Expression::Identifier(_) if cursor.strict() => {
return Err(Error::lex(LexError::Syntax(
"cannot delete variables in strict mode".into(),
token_start,

8
boa_parser/src/parser/expression/update.rs

@ -106,7 +106,7 @@ where
.parse(cursor, interner)?;
// https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors
return (as_simple(&target, position, cursor.strict_mode())?).map_or_else(
return (as_simple(&target, position, cursor.strict())?).map_or_else(
|| {
Err(Error::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
@ -125,7 +125,7 @@ where
.parse(cursor, interner)?;
// https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors
return (as_simple(&target, position, cursor.strict_mode())?).map_or_else(
return (as_simple(&target, position, cursor.strict())?).map_or_else(
|| {
Err(Error::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
@ -154,7 +154,7 @@ where
.expect("Punctuator::Inc token disappeared");
// https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors
return (as_simple(&lhs, position, cursor.strict_mode())?).map_or_else(
return (as_simple(&lhs, position, cursor.strict())?).map_or_else(
|| {
Err(Error::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),
@ -170,7 +170,7 @@ where
.expect("Punctuator::Dec token disappeared");
// https://tc39.es/ecma262/#sec-update-expressions-static-semantics-early-errors
return (as_simple(&lhs, position, cursor.strict_mode())?).map_or_else(
return (as_simple(&lhs, position, cursor.strict())?).map_or_else(
|| {
Err(Error::lex(LexError::Syntax(
"Invalid left-hand side in assignment".into(),

32
boa_parser/src/parser/function/mod.rs

@ -69,15 +69,17 @@ where
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let _timer = Profiler::global().start_event("FormalParameters", "Parsing");
cursor.set_goal(InputElement::RegExp);
let mut params = Vec::new();
let Some(start_position) = cursor
.peek(0, interner)?
.filter(|&tok| tok.kind() != &TokenKind::Punctuator(Punctuator::CloseParen))
.map(|tok| tok.span().start()) else {
return Ok( FormalParameterList::default());
};
let next_token = cursor.peek(0, interner).or_abrupt()?;
if next_token.kind() == &TokenKind::Punctuator(Punctuator::CloseParen) {
return Ok(FormalParameterList::default());
}
let start_position = next_token.span().start();
let mut params = Vec::new();
loop {
let mut rest_param = false;
@ -101,9 +103,9 @@ where
params.push(next_param);
if cursor.peek(0, interner).or_abrupt()?.kind()
== &TokenKind::Punctuator(Punctuator::CloseParen)
{
if cursor.peek(0, interner)?.map_or(true, |tok| {
tok.kind() == &TokenKind::Punctuator(Punctuator::CloseParen)
}) {
break;
}
@ -117,9 +119,9 @@ where
}
cursor.expect(Punctuator::Comma, "parameter list", interner)?;
if cursor.peek(0, interner).or_abrupt()?.kind()
== &TokenKind::Punctuator(Punctuator::CloseParen)
{
if cursor.peek(0, interner)?.map_or(true, |tok| {
tok.kind() == &TokenKind::Punctuator(Punctuator::CloseParen)
}) {
break;
}
}
@ -381,9 +383,9 @@ where
_ => {
let ident = BindingIdentifier::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let init = if *cursor.peek(0, interner).or_abrupt()?.kind()
== TokenKind::Punctuator(Punctuator::Assign)
{
let init = if cursor.peek(0, interner)?.map_or(false, |tok| {
tok.kind() == &TokenKind::Punctuator(Punctuator::Assign)
}) {
Some(
Initializer::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,

8
boa_parser/src/parser/mod.rs

@ -208,7 +208,7 @@ impl<R> Parser<'_, R> {
where
R: Read,
{
self.cursor.set_strict_mode(true);
self.cursor.set_strict(true);
}
/// Set the parser JSON mode to true.
@ -246,8 +246,8 @@ where
type Output = StatementList;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
let statement_list = ScriptBody::new(true, cursor.strict_mode(), self.direct_eval)
.parse(cursor, interner)?;
let statement_list =
ScriptBody::new(true, cursor.strict(), self.direct_eval).parse(cursor, interner)?;
// It is a Syntax Error if the LexicallyDeclaredNames of ScriptBody contains any duplicate entries.
let mut lexical_names = FxHashSet::default();
@ -401,7 +401,7 @@ where
type Output = ModuleItemList;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.set_module_mode();
cursor.set_module();
let items = if cursor.peek(0, interner)?.is_some() {
self::statement::ModuleItemList.parse(cursor, interner)?

2
boa_parser/src/parser/statement/block/mod.rs

@ -103,7 +103,7 @@ where
let mut lexical_names = FxHashMap::default();
for (name, is_fn) in lexically_declared_names_legacy(&statement_list) {
if let Some(is_fn_previous) = lexical_names.insert(name, is_fn) {
match (cursor.strict_mode(), is_fn, is_fn_previous) {
match (cursor.strict(), is_fn, is_fn_previous) {
(false, true, true) => {}
_ => {
return Err(Error::general(

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

@ -73,8 +73,8 @@ where
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult<Self::Output> {
cursor.expect((Keyword::Class, false), "class declaration", interner)?;
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let mut has_binding_identifier = false;
let token = cursor.peek(0, interner).or_abrupt()?;
@ -94,7 +94,7 @@ where
))
}
};
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
ClassTail::new(
name,
@ -166,11 +166,11 @@ where
cursor.expect(Punctuator::OpenBlock, "class tail", interner)?;
// Temporarily disable strict mode because "strict" may be parsed as a keyword.
let strict = cursor.strict_mode();
cursor.set_strict_mode(false);
let strict = cursor.strict();
cursor.set_strict(false);
let is_close_block = cursor.peek(0, interner).or_abrupt()?.kind()
== &TokenKind::Punctuator(Punctuator::CloseBlock);
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
if is_close_block {
cursor.advance(interner);
@ -264,11 +264,11 @@ where
interner,
)?;
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let lhs = LeftHandSideExpression::new(None, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
Ok(lhs)
}
@ -316,8 +316,8 @@ where
// The identifier "static" is forbidden in strict mode but used as a keyword in classes.
// Because of this, strict mode has to temporarily be disabled while parsing class field names.
let strict = cursor.strict_mode();
cursor.set_strict_mode(false);
let strict = cursor.strict();
cursor.set_strict(false);
loop {
let token = cursor.peek(0, interner).or_abrupt()?;
let position = token.span().start();
@ -538,7 +538,7 @@ where
}
}
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
Ok((constructor, elements))
}
@ -641,8 +641,8 @@ where
let element = match token.kind() {
TokenKind::IdentifierName((Sym::CONSTRUCTOR, _)) if !r#static => {
cursor.advance(interner);
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
cursor.expect(Punctuator::OpenParen, "class constructor", interner)?;
let parameters = FormalParameters::new(self.allow_yield, self.allow_await)
@ -660,7 +660,7 @@ where
"class constructor",
interner,
)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
return Ok((Some(Function::new(self.name, parameters, body)), None));
}
@ -672,8 +672,8 @@ where
{
ast::StatementList::default()
} else {
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let position = cursor.peek(0, interner).or_abrupt()?.span().start();
let statement_list = StatementList::new(
false,
@ -709,7 +709,7 @@ where
"class definition",
interner,
)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
statement_list
};
function::ClassElement::StaticBlock(statement_list)
@ -725,12 +725,12 @@ where
));
}
}
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let (class_element_name, method) =
GeneratorMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
match class_element_name {
ClassElementName::PropertyName(property_name) if r#static => {
@ -790,12 +790,12 @@ where
}
_ => {}
}
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let (class_element_name, method) =
AsyncGeneratorMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
match class_element_name {
ClassElementName::PropertyName(property_name) if r#static => {
if property_name.literal() == Some(Sym::PROTOTYPE) {
@ -834,12 +834,12 @@ where
}
_ => {
let name_position = token.span().start();
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let (class_element_name, method) =
AsyncMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
match class_element_name {
ClassElementName::PropertyName(property_name) if r#static => {
@ -896,8 +896,8 @@ where
TokenKind::PrivateIdentifier(name) => {
let name = *name;
cursor.advance(interner);
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let params =
UniqueFormalParameters::new(false, false).parse(cursor, interner)?;
cursor.expect(
@ -921,7 +921,7 @@ where
token.span().start(),
)));
}
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
let method = MethodDefinition::Get(Function::new(None, params, body));
if r#static {
function::ClassElement::PrivateStaticMethodDefinition(
@ -965,10 +965,10 @@ where
"class getter",
interner,
)?;
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let body = FunctionBody::new(false, false).parse(cursor, interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"class getter",
@ -1027,8 +1027,8 @@ where
TokenKind::PrivateIdentifier(name) => {
let name = *name;
cursor.advance(interner);
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let params =
UniqueFormalParameters::new(false, false).parse(cursor, interner)?;
cursor.expect(
@ -1052,7 +1052,7 @@ where
token.span().start(),
)));
}
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
let method = MethodDefinition::Set(Function::new(None, params, body));
if r#static {
function::ClassElement::PrivateStaticMethodDefinition(
@ -1081,8 +1081,8 @@ where
let name_position = token.span().start();
let name = PropertyName::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let params =
UniqueFormalParameters::new(false, false).parse(cursor, interner)?;
cursor.expect(
@ -1106,7 +1106,7 @@ where
token.span().start(),
)));
}
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
let method = MethodDefinition::Set(Function::new(None, params, body));
if r#static {
if name.literal() == Some(Sym::PROTOTYPE) {
@ -1154,8 +1154,8 @@ where
match token.kind() {
TokenKind::Punctuator(Punctuator::Assign) => {
cursor.advance(interner);
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let rhs = AssignmentExpression::new(
Some(name_private.into()),
true,
@ -1164,7 +1164,7 @@ where
)
.parse(cursor, interner)?;
cursor.expect_semicolon("expected semicolon", interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
if r#static {
function::ClassElement::PrivateStaticFieldDefinition(
PrivateName::new(name),
@ -1178,8 +1178,8 @@ where
}
}
TokenKind::Punctuator(Punctuator::OpenParen) => {
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let params =
UniqueFormalParameters::new(false, false).parse(cursor, interner)?;
cursor.expect(
@ -1204,7 +1204,7 @@ where
)));
}
let method = MethodDefinition::Ordinary(Function::new(None, params, body));
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
if r#static {
function::ClassElement::PrivateStaticMethodDefinition(
PrivateName::new(name),
@ -1261,8 +1261,8 @@ where
}
}
cursor.advance(interner);
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let rhs = AssignmentExpression::new(
name.literal().map(Into::into),
true,
@ -1271,7 +1271,7 @@ where
)
.parse(cursor, interner)?;
cursor.expect_semicolon("expected semicolon", interner)?;
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
if r#static {
function::ClassElement::StaticFieldDefinition(name, Some(rhs))
} else {
@ -1285,8 +1285,8 @@ where
name_position,
));
}
let strict = cursor.strict_mode();
cursor.set_strict_mode(true);
let strict = cursor.strict();
cursor.set_strict(true);
let params =
UniqueFormalParameters::new(false, false).parse(cursor, interner)?;
cursor.expect(
@ -1311,7 +1311,7 @@ where
)));
}
let method = MethodDefinition::Ordinary(Function::new(None, params, body));
cursor.set_strict_mode(strict);
cursor.set_strict(strict);
if r#static {
function::ClassElement::StaticMethodDefinition(name, method)
} else {

5
boa_parser/src/parser/statement/declaration/hoistable/mod.rs

@ -184,7 +184,7 @@ fn parse_callable_declaration<R: Read, C: CallableDeclaration>(
// If the source text matched by FormalParameters is strict mode code,
// the Early Error rules for UniqueFormalParameters : FormalParameters are applied.
if (cursor.strict_mode() || body.strict()) && params.has_duplicates() {
if (cursor.strict() || body.strict()) && params.has_duplicates() {
return Err(Error::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
@ -202,8 +202,7 @@ fn parse_callable_declaration<R: Read, C: CallableDeclaration>(
// 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())
{
if (cursor.strict() || 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(),

4
boa_parser/src/parser/statement/if_stm/mod.rs

@ -71,7 +71,7 @@ where
.span()
.end();
let strict = cursor.strict_mode();
let strict = cursor.strict();
let token = cursor.peek(0, interner).or_abrupt()?;
let then_node = match token.kind() {
TokenKind::Keyword((Keyword::Function, _)) => {
@ -110,7 +110,7 @@ where
TokenKind::Keyword((Keyword::Else, false)) => {
cursor.advance(interner);
let strict = cursor.strict_mode();
let strict = cursor.strict();
let token = cursor.peek(0, interner).or_abrupt()?;
let position = token.span().start();
let stmt = match token.kind() {

2
boa_parser/src/parser/statement/iteration/for_statement.rs

@ -159,7 +159,7 @@ where
(Some(init), TokenKind::Keyword((kw @ (Keyword::In | Keyword::Of), false))) => {
let kw = *kw;
let init =
initializer_to_iterable_loop_initializer(init, position, cursor.strict_mode())?;
initializer_to_iterable_loop_initializer(init, position, cursor.strict())?;
cursor.advance(interner);
let expr = Expression::new(None, true, self.allow_yield, self.allow_await)

2
boa_parser/src/parser/statement/labelled_stm/mod.rs

@ -58,7 +58,7 @@ where
cursor.expect(Punctuator::Colon, "Labelled Statement", interner)?;
let strict = cursor.strict_mode();
let strict = cursor.strict();
let next_token = cursor.peek(0, interner).or_abrupt()?;
let labelled_item = match next_token.kind() {

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

@ -294,7 +294,7 @@ where
let _timer = Profiler::global().start_event("StatementList", "Parsing");
let mut items = Vec::new();
let global_strict = cursor.strict_mode();
let global_strict = cursor.strict();
let mut directive_prologues = self.directive_prologues;
let mut strict = self.strict;
let mut string_literal_escape_sequence = None;
@ -326,7 +326,7 @@ where
|g| g == utf16!("use strict"),
true,
) {
cursor.set_strict_mode(true);
cursor.set_strict(true);
strict = true;
if let Some((position, escape_sequence)) = string_literal_escape_sequence {
@ -354,7 +354,7 @@ where
items.sort_by(ast::StatementListItem::hoistable_order);
cursor.set_strict_mode(global_strict);
cursor.set_strict(global_strict);
Ok(ast::StatementList::new(items, strict))
}

2
boa_parser/src/parser/statement/switch/mod.rs

@ -84,7 +84,7 @@ where
let mut lexical_names = FxHashMap::default();
for (name, is_fn) in lexically_declared_names_legacy(&switch) {
if let Some(is_fn_previous) = lexical_names.insert(name, is_fn) {
match (cursor.strict_mode(), is_fn, is_fn_previous) {
match (cursor.strict(), is_fn, is_fn_previous) {
(false, true, true) => {}
_ => {
return Err(Error::general(

2
boa_parser/src/parser/statement/with/mod.rs

@ -62,7 +62,7 @@ where
.start();
// It is a Syntax Error if the source text matched by this production is contained in strict mode code.
if cursor.strict_mode() {
if cursor.strict() {
return Err(Error::general(
"with statement not allowed in strict mode",
position,

Loading…
Cancel
Save