diff --git a/crates/js/src/bytecode.rs b/crates/js/src/bytecode.rs index 4ecb582d0..b23d9a525 100644 --- a/crates/js/src/bytecode.rs +++ b/crates/js/src/bytecode.rs @@ -142,6 +142,9 @@ pub(crate) enum Op { ForInNext(u32), /// Pops the value of an expression statement into the completion value. SetCompletion, + /// Pushes the completion value, which a `finally` block in script and eval code keeps and + /// sets back with `SetCompletion` when it ends normally. + GetCompletion, } /// What code is: a function body, a script, or eval code. @@ -237,7 +240,7 @@ fn effect(op: Op, finally_tables: &[Vec]) -> Result { Op::GetVar(_) | Op::TypeofVar(_) | Op::DeleteVar(_) | Op::IncVar { .. } | Op::RefVar(_) => e(0, 1), Op::GetRef(_) => e(1, 1), Op::PutRef(_) => e(2, 1), - Op::NewObject | Op::NewArray | Op::Closure(_) | Op::RegExp(_) => e(0, 1), + Op::NewObject | Op::NewArray | Op::Closure(_) | Op::RegExp(_) | Op::GetCompletion => e(0, 1), Op::Pop | Op::PushWith | Op::PushCatch(_) | Op::SetCompletion => e(1, 0), Op::PopUnder(n) => e(n.checked_add(1).ok_or("operand too large")?, 1), Op::Dup => e(1, 2), diff --git a/crates/js/src/compiler.rs b/crates/js/src/compiler.rs index 6e16437a5..ae470be44 100644 --- a/crates/js/src/compiler.rs +++ b/crates/js/src/compiler.rs @@ -708,7 +708,16 @@ impl<'a> FnCompiler<'a> { self.emit(Op::Undefined); self.push_number(0.0); self.bind(fin_entry); + // In script and eval code the finally block keeps the completion value of the try or + // catch block when it ends normally (ECMA-262 5.1 section 12.14). + let keeps_completion = self.kind != CodeKind::Function; + if keeps_completion { + self.emit(Op::GetCompletion); + } self.stmts(fin)?; + if keeps_completion { + self.emit(Op::SetCompletion); + } let dispatch: Vec