Skip to content

Register forwarding can reread a mutable left operand after right-side effects #5456

Description

@dev-willbird1936

Describe the bug

Register forwarding in the bytecode compiler can violate JavaScript's
left-to-right operand evaluation when the left operand is a mutable local and
the right operand mutates that local. The generated binary operation rereads
the mutated value instead of using the value captured before evaluating the
right operand.

To Reproduce

Run this script:

let a = 1;
a = a + (a = 5);
a;

Current main evaluates the final expression to 10.

The same stale-value problem affects other forwarded binary operations, for
example:

let a = 1;
a = a | (a = 4); // current result: 4, expected: 5

Expected behavior

The left operand must be evaluated and retained before the right operand is
evaluated. The first example should therefore produce 6, not 10.

Build environment (please complete the following information):

  • OS: Windows 11
  • Boa commit: f54077467b4b01eb0fe221cfff470a8546ebe36c
  • Target triple: x86_64-pc-windows-gnu
  • Rust toolchain: 1.95.0-x86_64-pc-windows-gnu

Additional context

The regression begins in the register-forwarding path introduced by #4845.
Immutable locals and cached constants can retain the fast path; mutable locals
need to be snapshotted before compiling an operand that may have side effects.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions