ReviewOS

zig-utils/zig-js

IteratorClose: reject primitive Symbol and BigInt return results

#1100
Closed chris opened this 9 days ago ·
9 days ago

Found while validating #1098. This predates the native iterator admission change.

A normal for-of break accepts primitive Symbol and BigInt values returned by the iterator's return method. Node and system JavaScriptCore throw TypeError for both. zig-js returns ["accepted","accepted"] in both tree-walker and required-VM modes, with both the preserved 136178a runner and the current candidate runner.

Reproducer:

(function () {
  return JSON.stringify([Symbol("result"), 1n].map(function (result) {
    var iterator = {
      next: function () { return { done: false, value: 1 }; },
      return: function () { return result; }
    };
    var iterable = { [Symbol.iterator]: function () { return iterator; } };
    try {
      for (var value of iterable) break;
      return "accepted";
    } catch (error) {
      return error.name;
    }
  }));
})();

Expected: ["TypeError","TypeError"].

ECMA-262 IteratorClose, step 7 requires an Object result after a successful close with a non-throw completion. Step 5 preserves an existing throw completion first.

The shared Interpreter.iteratorClose validation currently checks the object representation tag, which also represents primitive Symbols and BigInts. Correct the semantic Object check without rejecting actual boxed objects.

Evidence:

  • Before: 136178a; runner SHA-256 45b2119b7f4bb114dc47889374f0af95b7d5b8c79ec2f70fd2c6bce553d5945b.
  • Candidate for #1098: runner SHA-256 0d79a3299dcd395338e0d20acdbc2beb92127b5fe47e6d64a40ee1fed15706b9.
  • Both runners: tree and required-VM output OK ["accepted","accepted"].
  • Node and system JavaScriptCore: ["TypeError","TypeError"].

Acceptance:

  • Primitive Symbol and BigInt close results throw TypeError for normal/break/return completion.
  • Ordinary and boxed-object results remain accepted; an existing throw completion keeps its original identity.
  • Pin oracle-confirmed tree/VM and optimizer-off/on cases, including warmed native closing.
  • Measure exact before/after failing-path sets in affected conformance subtrees; run focused normal/TSan checks and callback GC/receiver/single-evaluation controls.
9 days ago

Fixed in 80e45951.

Both ordinary and generator IteratorClose result checks now reuse the existing semantic Object predicate. Primitive Symbol/BigInt cells are rejected; genuine boxed objects remain accepted. Original throw completion precedence is preserved.

Evidence:

  • The 88-case Node/JSC-confirmed matrix changed from 12 mismatches to zero in both forced tree and required-VM execution.
  • Sixty direct bytecode/native controls cover ordinary and generator closing, primitive and boxed results, normal and throw completions, GC during return lookup and invocation, exact receiver identity, and one getter/call execution. Debug, ReleaseSafe and instrumented TSan passed without supplied suppressions.
  • The production off/on matrix also passed its filtered TSan gate. Production JIT: 156 passes plus one platform skip; VM: 6; concurrency: 4; frontend: 79. Library and relocation inventory gates passed.
  • Exact comparison against 03f45cb: ten affected subtrees, 3,506 cases, zero failures and zero VM divergences on both binaries, zero test262 flips and no new failing paths. Existing corpus cases do not cover these primitive close results.
  • Default threadfuzz: 400 programs; broad: 300; both zero failures.

The fixture retains all sixty native controls while reusing four contexts and resetting inputs/counters per case. Development smoke checks retained checksums; no public performance claim is made. The combined cold unit link remains the separate memory-policy blocker in #933.

Sign in to comment on this issue.