From cd232b18f9e789acca56344503559daf9692e755 Mon Sep 17 00:00:00 2001 From: raskad <32105367+raskad@users.noreply.github.com> Date: Fri, 11 Aug 2023 11:42:09 +0200 Subject: [PATCH] Fix switch statement `break` and `continue` return values (#3205) * Fix switch statement early return values * Remove unused control flow --- boa_engine/src/bytecompiler/mod.rs | 11 ------ boa_engine/src/bytecompiler/statement/try.rs | 38 ++++++++++++++++++-- 2 files changed, 35 insertions(+), 14 deletions(-) diff --git a/boa_engine/src/bytecompiler/mod.rs b/boa_engine/src/bytecompiler/mod.rs index 1973d3d76b..b0364f9f36 100644 --- a/boa_engine/src/bytecompiler/mod.rs +++ b/boa_engine/src/bytecompiler/mod.rs @@ -843,15 +843,10 @@ 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(_)) @@ -859,17 +854,11 @@ impl<'ctx, 'host> ByteCompiler<'ctx, 'host> { 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); } diff --git a/boa_engine/src/bytecompiler/statement/try.rs b/boa_engine/src/bytecompiler/statement/try.rs index d05501d177..fcaffc4d08 100644 --- a/boa_engine/src/bytecompiler/statement/try.rs +++ b/boa_engine/src/bytecompiler/statement/try.rs @@ -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<'_, '_> { @@ -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); @@ -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; @@ -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); + } }