Files
ladybird/Libraries/LibJS/Bytecode
Andreas Kling 479b89aa6d LibJS: Fix UpdateEmpty completion value semantics for loops/switch/if
When a loop or switch body produces an abrupt completion (break or
continue) with an empty value, the ES spec requires UpdateEmpty to
replace the empty value with the last non-empty completion value V.

The bytecode compiler was failing to do this because it only updated
the completion register after body codegen, guarded by
!is_current_block_terminated(). When break/continue terminated the
block, the update was skipped.

Fix this with three changes:

1. Introduce a CompletionRegisterScope that tells
   ScopeNode::generate_bytecode to eagerly emit Mov instructions
   into the completion register after each value-producing
   statement. This ensures the register is up to date before any
   break or continue fires.

2. Give IfStatement its own CompletionRegisterScope (initialized
   to undefined) during branch evaluation. This models the spec's
   UpdateEmpty(stmtCompletion, undefined) for if-statements: when
   break/continue fires inside an if-branch, the scoped jump
   propagation sees that the if's completion register differs from
   the loop's and emits a Mov, correctly replacing the eagerly
   written value with undefined. Without this, code like
   { 3; if (true) { break; } else { } } would incorrectly carry
   the value 3 instead of undefined through the break.

3. Capture loop body results and emit a fallback Mov for
   non-ScopeNode bodies (e.g. bare expression statements like
   do x=1; while(false)) that don't participate in the eager
   CompletionRegisterScope update mechanism.

For labelled break/continue that cross loop boundaries, the jump
codegen now propagates the inner completion register to the target
scope's completion register before emitting the jump.

Also fix ForStatement to use a proper completion register
(previously it returned the body result directly, which was wrong
for empty bodies and break-with-no-value cases).
2026-02-11 14:29:36 +01:00
..