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
..
ASTCodegen.cpp LibJS: Fix UpdateEmpty completion value semantics for loops/switch/if 2026-02-11 14:29:36 +01:00
BasicBlock.cpp LibJS: Stop worrying about Instruction destructors 2025-11-21 09:46:03 +01:00
BasicBlock.h LibJS: Collapse handler/finalizer into single exception handler target 2026-02-09 16:35:39 +01:00
BuiltinAbstractOperationsEnabled.h LibJS: Introduce NativeJavaScriptBackedFunction 2025-11-30 11:54:54 +01:00
Builtins.cpp Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Builtins.h LibJS: Skip generic call when using regexp builtins in StringPrototype 2025-12-13 13:51:12 -06:00
Bytecode.def LibJS: Do not allow reassignment to local const variable 2026-02-09 21:06:46 +01:00
CodeGenerationError.cpp Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
CodeGenerationError.h LibJS: Revert Enable EXPLICIT_SYMBOL_EXPORT 2025-07-22 11:51:29 -04:00
Executable.cpp LibJS: Use binary search for exception handler lookup 2026-02-09 16:35:39 +01:00
Executable.h LibJS: Collapse handler/finalizer into single exception handler target 2026-02-09 16:35:39 +01:00
FormatOperand.h LibJS: Flatten Operand to 32-bit index in bytecode instruction stream 2025-12-09 21:44:13 -06:00
Generator.cpp LibJS: Fix UpdateEmpty completion value semantics for loops/switch/if 2026-02-11 14:29:36 +01:00
Generator.h LibJS: Fix UpdateEmpty completion value semantics for loops/switch/if 2026-02-11 14:29:36 +01:00
IdentifierTable.cpp LibJS+LibWeb+WebContent: Port JS::PropertyKey to UTF-16 2025-08-05 07:07:15 -04:00
IdentifierTable.h LibJS: Give Interpreter a direct pointer to the identifier table 2025-10-07 23:50:51 +02:00
Instruction.cpp LibJS: Flatten Operand to 32-bit index in bytecode instruction stream 2025-12-09 21:44:13 -06:00
Instruction.h LibJS: Add % (modulo) fast path in bytecode interpreter 2026-01-09 09:16:50 +01:00
Interpreter.cpp LibJS: Do not allow reassignment to local const variable 2026-02-09 21:06:46 +01:00
Interpreter.h LibJS: Remove LeaveUnwindContext opcode 2026-02-09 16:35:39 +01:00
Label.cpp Everywhere: Hoist the Libraries folder to the top-level 2024-11-10 12:50:45 +01:00
Label.h LibJS: Revert Enable EXPLICIT_SYMBOL_EXPORT 2025-07-22 11:51:29 -04:00
Operand.h LibJS: Flatten Operand to 32-bit index in bytecode instruction stream 2025-12-09 21:44:13 -06:00
PropertyAccess.h LibJS: Take snapshot of prototype chain validity later in GetById 2026-01-09 09:16:50 +01:00
PropertyKeyTable.cpp LibJS: Cache fully-formed PropertyKeys in Executable 2025-12-11 14:34:45 -06:00
PropertyKeyTable.h LibJS+LibWeb: Add missing GC marking visits 2026-01-07 12:48:58 +01:00
PutKind.h LibJS: Rename Bytecode::Op::PropertyKind => Bytecode::PutKind 2025-10-11 20:08:58 +02:00
RegexTable.cpp LibJS: Don't rerun regexp optimizer every time a regexp literal is used 2025-12-12 11:43:35 -06:00
RegexTable.h LibJS: Don't rerun regexp optimizer every time a regexp literal is used 2025-12-12 11:43:35 -06:00
Register.h LibJS: Replace implicit environment stack with explicit registers 2026-02-09 16:35:39 +01:00
ScopedOperand.cpp LibJS: Flatten Operand to 32-bit index in bytecode instruction stream 2025-12-09 21:44:13 -06:00
ScopedOperand.h LibJS: Revert Enable EXPLICIT_SYMBOL_EXPORT 2025-07-22 11:51:29 -04:00
StringTable.cpp LibJS+LibWeb: Port interned bytecode strings to UTF-16 2025-08-14 10:27:08 +02:00
StringTable.h LibJS+LibWeb: Port interned bytecode strings to UTF-16 2025-08-14 10:27:08 +02:00