Revert variables on catch

This commit is contained in:
Andrew Morris
2023-03-24 18:31:38 +11:00
parent 11680ebe97
commit 7b5f1de302
3 changed files with 74 additions and 1 deletions

View File

@@ -416,6 +416,7 @@ not the subset of ValueScript that has actually been implemented.
- Recursion
- Destructuring
- Exceptions
- Variables changed during try block are reverted on catch
- Local imports (not yet in the playground)
- Tree shaking (not yet in the playground)
- Copy-on-write optimizations
@@ -466,7 +467,6 @@ not the subset of ValueScript that has actually been implemented.
- `{} === {} -> true`
- JS: `-> false`
- This is a value semantics thing - objects don't have identity
- Restoring variables changed during `try` when an exception is thrown
- Enforcing `const`
- Temporal dead zones
- Iterators

View File

@@ -0,0 +1,22 @@
// test_output! [0,1]
export default function () {
return [
test(true),
test(false),
];
}
function test(shouldThrow: boolean) {
let x = 0;
try {
x++;
if (shouldThrow) {
throw new Error("boom");
}
} catch {}
return x;
}

View File

@@ -652,6 +652,29 @@ impl FunctionCompiler {
}
self.apply_catch_setting();
let mut snap_pairs = HashSet::<(Register, Register)>::new();
if try_.handler.is_some() {
let snap_registers: HashSet<Register> = self.get_mutated_registers(try_.block.span);
for reg in snap_registers {
let reg_name = match &reg {
Register::Named(name) => name,
_ => continue,
};
let snap_reg = self.allocate_reg_fresh(&format!("snap_{}", reg_name));
self.push(Instruction::Mov(
Value::Register(reg.clone()),
snap_reg.clone(),
));
snap_pairs.insert((reg, snap_reg));
}
}
self.block_statement(&try_.block);
self.pop_catch_setting(); // TODO: Avoid redundant set_catch to our own finally
@@ -663,6 +686,10 @@ impl FunctionCompiler {
self.label(catch_label.unwrap());
self.apply_catch_setting(); // TODO: Avoid redundant unset_catch
for (reg, snap_reg) in snap_pairs {
self.push(Instruction::Mov(Value::Register(snap_reg), reg));
}
if let Some(param) = &catch_clause.param {
let mut ec = ExpressionCompiler { fnc: self };
@@ -1135,4 +1162,28 @@ impl FunctionCompiler {
return asm;
}
fn get_mutated_registers(&self, span: swc_common::Span) -> HashSet<Register> {
let start = swc_common::Span {
lo: span.lo,
hi: span.lo,
ctxt: span.ctxt,
};
let end = swc_common::Span {
lo: span.hi,
hi: span.hi,
ctxt: span.ctxt,
};
let mut mutated_registers = HashSet::<Register>::new();
for (_span, mutated_name_id) in self.scope_analysis.mutations.range(start..end) {
if let Some(Value::Register(reg)) = self.lookup_by_name_id(mutated_name_id) {
mutated_registers.insert(reg);
}
}
mutated_registers
}
}