ReviewOS

zig-utils/zig-js

Interpreter: reuse an uncaptured initial for-let environment

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

Parent: #628 Related: #100, #429

Problem

Interpreter.evalFor always calls perIterEnv after evaluating a lexical classic-for initializer. That allocates a GC Environment, binding-table storage, and duplicate binding names before the first condition even when the initializer environment was not captured. Later iterations already use the correct observability rule: allocate a fresh copy only when the current environment was captured; otherwise reuse it in place.

On Linux exact Zig dev.1818, the unchanged 4 MiB recovery witness reaches this unnecessary initial copy after its deliberate pressure OOM and fails duplicating the one-byte r binding name. Raising the heap cap or weakening the witness would hide the engine allocation and is not acceptable.

Scope

  • Reuse the lexical initializer environment for the initial iteration when it was not captured.
  • Preserve CreatePerIterationEnvironment semantics when an initializer closure captures the head binding.
  • Add direct controls for uncaptured reuse and captured-initializer isolation.
  • Keep heap accounting, allocation failure propagation, loop updates, const behavior, and closure identity exact.

Acceptance

  • Exact Zig dev.1818 bounded-heap recovery witness passes unchanged on Linux and locally.
  • Initializer-captured and body-captured classic-for closures retain per-iteration values.
  • Focused interpreter/context tests and full unit shards have zero regressions.
  • TSan unit coverage passes.

No-workaround rules

No heap-limit increase, smaller pressure workload, swallowed allocation error, arena leak, disabled GC, or weakened closure semantics.

9 days ago

Landed in 935930c7.

Exact Zig dev.1818 evidence:

  • unchanged 4 MiB recovery witness: 1/1 normally and 1/1 under TSan
  • focused reuse/capture witness: 1/1 normally and 1/1 under TSan
  • mixed lexical initializer, forced tree-walker↔required-bytecode identity, and VM per-iteration capture: 3/3
  • exact unit shard 0: 435/435, zero failures/leaks
  • Test262 test/language/statements/for: 0 fail / 385 pass before → 0 / 385 after, zero flips

The heap cap, pressure workload, allocation error propagation, and captured-environment copy path are unchanged.

Sign in to comment on this issue.