ReviewOS

zig-utils/zig-js

VM: establish class private environments before computed names

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

Parent: #465. Related: #803, #806.

Reproduction and current boundary

At a6feac57, ordinary-function admission still rejects a class whose computed element name references that class's own private name:

function f() {
  class Box {
    #value;
    [globalThis.#value] = 1;
  }
}

The parser correctly accepts the source and enforces that the private name is lexically visible. The tree-walker creates a fresh class PrivateEnvironment, mints the class's private-name identities, rewrites a per-evaluation member copy, and evaluates computed names under that map. VM class lowering instead evaluates heritage and every computed name eagerly before eval_class; Compiler.addMemberName must reject the unresolved raw #value, because treating it as a public property would erase the required private brand check.

This is one of the four computed-class/private-visibility failures retained after #806. The existing rejection is correctness-preserving, but universal eligibility remains incomplete.

Scope

  • Create the class's per-evaluation private environment after heritage validation and before computed element-name evaluation, matching ClassDefinitionEvaluation ordering.
  • Let eagerly lowered computed names resolve raw private references through that exact environment, including get/set/update/call/#name in and direct eval where reachable.
  • Reuse the same freshly minted private-name identities when eval_class constructs methods, accessors, fields, brands, and deferred initializers.
  • Preserve the enclosing private environment for heritage and nested classes, class-name TDZ, strictness, source-order/once-only ToPropertyKey, suspension, abrupt completion, OOM cleanup, moving GC, and no-GIL ownership.
  • Preserve closures created during a computed-name expression: a closure capturing #name must retain the same private identity later installed on instances of that one class evaluation.

Acceptance

  • Statement/expression class Test262 visibility cases for private fields, methods, and accessors pass in required bytecode with zero fallback.
  • Paired tree-walker/required witnesses cover get, set, update, call, #name in, direct eval, nested/outer private shadowing, repeated class evaluation, and a closure escaping a computed-name expression.
  • Heritage still resolves only the enclosing private environment; computed names resolve the new class environment.
  • Generator/async suspension, actual moving-GC, allocation-failure replay, no-GIL/TSan, full unit, and relevant Test262 gates pass.
  • Exact-parent admission gains/regressions are reported and the implementation commit/CI are linked.

No compile-time private-name identity, duplicate per-phase identities, public-property lowering, source rewrite, disabled collection, suppressed failure, or tree-walker fallback counted as completion.

9 days ago

Implemented and pushed to main in 58ed1311.

The VM now creates each class PrivateEnvironment after heritage and before computed names, resolves eager raw private get/set/update/call/#in operations through dedicated fail-closed bytecodes, and preserves the exact identity through closures, direct eval, unwind, suspension, moving GC, and shared no-GIL execution. The tree-walker nested-class shadowing path now follows the same identity rules.

Evidence:

  • Test262 test/language/statements/class/elements: required VM 4 -> 0 divergences (+4, 0 regressions); ordinary mode 1,534/1,534.
  • Full unit suite: 2,245 passed, 1 expected platform skip, 0 failed, 0 leaked (2,246 accounted).
  • Focused TSan: 6/6 computed-class tests passed, including moving GC and shared no-GIL execution.
  • ReleaseSafe: 6/6 focused tests passed.
  • Direct-eval suite: 36/36 passed.
  • Seeded threadfuzz: 400 programs, 0 failures.

Sign in to comment on this issue.