Skip to content

Commit

Permalink
Correctly parse consecutive semicolons
Browse files Browse the repository at this point in the history
  • Loading branch information
raskad committed Jan 15, 2023
1 parent f52d1d3 commit b12d242
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 18 deletions.
31 changes: 23 additions & 8 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -660,12 +660,20 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> {
use_expr: bool,
configurable_globals: bool,
) -> JsResult<()> {
if let Some((last, items)) = list.statements().split_last() {
for node in items {
self.compile_stmt_list_item(node, false, configurable_globals)?;
let mut items = list
.statements()
.iter()
.filter(|item| item != &&StatementListItem::Statement(Statement::Empty))
.peekable();

while let Some(item) = items.next() {
if items.peek().is_some() {
self.compile_stmt_list_item(item, false, configurable_globals)?;
} else {
self.compile_stmt_list_item(item, use_expr, configurable_globals)?;
}
self.compile_stmt_list_item(last, use_expr, configurable_globals)?;
}

Ok(())
}

Expand All @@ -681,11 +689,18 @@ impl<'b, 'icu> ByteCompiler<'b, 'icu> {

self.create_decls(list, true);

if let Some((last, items)) = list.statements().split_last() {
for node in items {
self.compile_stmt_list_item(node, false, true)?;
let mut items = list
.statements()
.iter()
.filter(|item| item != &&StatementListItem::Statement(Statement::Empty))
.peekable();

while let Some(item) = items.next() {
if items.peek().is_some() {
self.compile_stmt_list_item(item, false, true)?;
} else {
self.compile_stmt_list_item(item, use_expr, true)?;
}
self.compile_stmt_list_item(last, use_expr, true)?;
}

let (num_bindings, compile_environment) = self.context.pop_compile_time_environment();
Expand Down
22 changes: 15 additions & 7 deletions boa_parser/src/parser/statement/declaration/lexical.rs
Original file line number Diff line number Diff line change
Expand Up @@ -69,29 +69,37 @@ where
let _timer = Profiler::global().start_event("LexicalDeclaration", "Parsing");
let tok = cursor.next(interner).or_abrupt()?;

match tok.kind() {
TokenKind::Keyword((Keyword::Const | Keyword::Let, true)) => Err(Error::general(
"Keyword must not contain escaped characters",
tok.span().start(),
)),
let lexical_declaration = match tok.kind() {
TokenKind::Keyword((Keyword::Const | Keyword::Let, true)) => {
return Err(Error::general(
"Keyword must not contain escaped characters",
tok.span().start(),
))
}
TokenKind::Keyword((Keyword::Const, false)) => BindingList::new(
self.allow_in,
self.allow_yield,
self.allow_await,
true,
self.loop_init,
)
.parse(cursor, interner),
.parse(cursor, interner)?,
TokenKind::Keyword((Keyword::Let, false)) => BindingList::new(
self.allow_in,
self.allow_yield,
self.allow_await,
false,
self.loop_init,
)
.parse(cursor, interner),
.parse(cursor, interner)?,
_ => unreachable!("unknown token found: {:?}", tok),
};

if !self.loop_init {
cursor.expect_semicolon("lexical declaration", interner)?;
}

Ok(lexical_declaration)
}
}

Expand Down
3 changes: 0 additions & 3 deletions boa_parser/src/parser/statement/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -313,9 +313,6 @@ where
}

items.push(item);

// move the cursor forward for any consecutive semicolon.
while cursor.next_if(Punctuator::Semicolon, interner)?.is_some() {}
}

items.sort_by(ast::StatementListItem::hoistable_order);
Expand Down
34 changes: 34 additions & 0 deletions boa_parser/src/parser/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,7 @@ fn empty_statement() {
if(a) ;
",
vec![
Statement::Empty.into(),
Statement::Empty.into(),
Statement::Var(VarDeclaration(
vec![Variable::from_identifier(
Expand All @@ -527,6 +528,39 @@ fn empty_statement() {
);
}

#[test]
fn empty_statement_ends_directive_prologues() {
let interner = &mut Interner::default();
let a = interner.get_or_intern_static("a", utf16!("a"));
let use_strict = interner.get_or_intern_static("use strict", utf16!("use strict"));
let public = interner
.get_or_intern_static("public", utf16!("public"))
.into();
check_parser(
r#"
"a";
;
"use strict";
let public = 5;
"#,
vec![
Statement::Expression(Expression::from(Literal::String(a))).into(),
Statement::Empty.into(),
Statement::Expression(Expression::from(Literal::String(use_strict))).into(),
Declaration::Lexical(LexicalDeclaration::Let(
vec![Variable::from_identifier(
public,
Some(Literal::from(5).into()),
)]
.try_into()
.unwrap(),
))
.into(),
],
interner,
);
}

#[test]
fn hashbang_use_strict_no_with() {
check_parser(
Expand Down

0 comments on commit b12d242

Please sign in to comment.