Skip to content

Commit

Permalink
Fix switch statement break and continue return values (#3205)
Browse files Browse the repository at this point in the history
* Fix switch statement early return values

* Remove unused control flow
  • Loading branch information
raskad authored Aug 11, 2023
1 parent 22b014d commit cd232b1
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 14 deletions.
11 changes: 0 additions & 11 deletions boa_engine/src/bytecompiler/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -843,33 +843,22 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> {
/// Compile a [`StatementList`].
pub fn compile_statement_list(&mut self, list: &StatementList, use_expr: bool, block: bool) {
if use_expr || self.jump_control_info_has_use_expr() {
let mut has_returns_value = false;
let mut use_expr_index = 0;
let mut first_return_is_abrupt = false;
for (i, statement) in list.statements().iter().enumerate() {
match statement {
StatementListItem::Statement(Statement::Break(_) | Statement::Continue(_)) => {
if !has_returns_value {
first_return_is_abrupt = true;
}
break;
}
StatementListItem::Statement(Statement::Empty | Statement::Var(_))
| StatementListItem::Declaration(_) => {}
StatementListItem::Statement(Statement::Block(block))
if !returns_value(block) => {}
StatementListItem::Statement(_) => {
has_returns_value = true;
use_expr_index = i;
}
}
}

if first_return_is_abrupt {
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::SetReturnValue);
}

for (i, item) in list.statements().iter().enumerate() {
self.compile_stmt_list_item(item, i == use_expr_index, block);
}
Expand Down
38 changes: 35 additions & 3 deletions boa_engine/src/bytecompiler/statement/try.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ use crate::{
use boa_ast::{
declaration::Binding,
operations::bound_names,
statement::{Catch, Finally, Try},
statement::{Block, Catch, Finally, Try},
Statement, StatementListItem,
};

impl ByteCompiler<'_, '_> {
Expand Down Expand Up @@ -130,7 +131,7 @@ impl ByteCompiler<'_, '_> {
self.emit_opcode(Opcode::Pop);
}

self.compile_block(catch.block(), use_expr);
self.compile_catch_finally_block(catch.block(), use_expr);

let env_index = self.pop_compile_environment();
self.patch_jump_with_target(push_env, env_index);
Expand All @@ -141,7 +142,7 @@ impl ByteCompiler<'_, '_> {
// TODO: We could probably remove the Get/SetReturnValue if we check that there is no break/continues statements.
self.current_stack_value_count += 1;
self.emit_opcode(Opcode::GetReturnValue);
self.compile_block(finally.block(), true);
self.compile_catch_finally_block(finally.block(), true);
self.emit_opcode(Opcode::SetReturnValue);
self.current_stack_value_count -= 1;

Expand All @@ -162,4 +163,35 @@ impl ByteCompiler<'_, '_> {

self.patch_jump(do_not_throw_exit);
}

/// Compile a catch or finally block.
///
/// If the block contains a break or continue as the first statement,
/// the return value is set to undefined.
/// See the [ECMAScript reference][spec] for more information.
///
/// [spec]: https://tc39.es/ecma262/#sec-try-statement-runtime-semantics-evaluation
fn compile_catch_finally_block(&mut self, block: &Block, use_expr: bool) {
let mut b = block;

loop {
match b.statement_list().first() {
Some(StatementListItem::Statement(
Statement::Break(_) | Statement::Continue(_),
)) => {
self.emit_opcode(Opcode::PushUndefined);
self.emit_opcode(Opcode::SetReturnValue);
break;
}
Some(StatementListItem::Statement(Statement::Block(block))) => {
b = block;
}
_ => {
break;
}
}
}

self.compile_block(block, use_expr);
}
}

0 comments on commit cd232b1

Please sign in to comment.