Skip to content

fix(hir): class field/element initializer test262 parity (v2, native-region-safe)#4779

Merged
proggeramlug merged 1 commit into
mainfrom
class-elements-v2-parity
Jun 8, 2026
Merged

fix(hir): class field/element initializer test262 parity (v2, native-region-safe)#4779
proggeramlug merged 1 commit into
mainfrom
class-elements-v2-parity

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Re-do of #4764 (correct on test262 but failed the compiler-output-regression / native-region-proof gate). This version keeps that gate green and scopes changes to class field/element semantics.

Results

  • language/statements/class/elements + language/expressions/class/elements: 50.1% → 75.5% (+722 tests; 1420 → 2142 pass).
  • native-region-proof gate: status: pass, NO failed_workloads on a debug build locally (the config CI uses). The class-field codegen change does not perturb the generic array/buffer native-region proofs.
  • Zero real regressions: broad built-ins language shard 0/12 went 76.9% → 77.9% (+61 gains). The 40 apparent shard "regressions" each pass in isolation on both this branch and origin/main — parallel-run timeout flakiness (the shard ran while a build saturated the shared box), confirmed by re-running all 40 individually (isolated-pass=40, isolated-fail=0).

Root causes fixed

  1. this undefined in static methods. C.m() binds this to the class; compile_static_method now seeds the this slot with the class-ref value and sets class_stack, so this.x / this.#x() / this[k] resolve.
  2. Private (#x) elements leaked into reflection. Now hidden (gated on class_id != 0, so {"#fff":1} literals stay visible) from Object.keys/values/entries, getOwnPropertyNames, getOwnPropertyDescriptor, hasOwnProperty, JSON.stringify, and object spread / Object.assign — on instances and the class constructor (class-ref).
  3. Static field/element initializers never ran for class EXPRESSIONS (var C = class { static x = 1 }). The var C = class {…} fast path and the general class-expression arm now emit inline StaticFieldSet/ClassStaticSymbolSet/static-block calls in source order (matching declarations); the per-evaluation ClassExprFresh path is reserved for class expressions inside function bodies (factories).
  4. Dynamic get/set on a runtime class-ref VALUE returned/dropped silently (C[key] / C[key]=v where C is e.g. a function param — propertyHelper's isWritable(C, name)). js_dyn_index_get/set now handle the 0x7FFE tag; codegen preserves class-ref tag bits at runtime for runtime-unknown receivers (classref_preserving_handle); the array string-key setter guards class-ref receivers instead of dereferencing them as a GC header.

Files

  • perry-codegen: codegen/method.rs, expr/index_get.rs, expr/index_set.rs
  • perry-hir: lower/lower_expr.rs, lower/stmt.rs
  • perry-runtime: object/{descriptors,object_ops,field_get_set,alloc}.rs, json/stringify.rs, value/dyn_index.rs, array/indexing.rs, typed_feedback.rs

Out of scope (separate feature areas; follow-up PRs)

  • async-generator yield* / async-iterator delegation semantics (the largest remaining class/elements cluster, but a generator-runtime feature)
  • parser-level grammar early-errors (invalid private-name SyntaxErrors)

…region-safe)

Lifts language/{statements,expressions}/class/elements parity 50.1% → 75.5%
(+722 tests) on the test262 class/elements suite, with the native-region-proof
compiler-output-regression gate green and zero real regressions.

Root causes fixed:

1. `this` was undefined inside static methods. A static method invoked as
   `C.m()` binds `this` to the class constructor; codegen now seeds the
   static-method `this` slot with the class-ref value and sets `class_stack`,
   so `this.x` / `this.#x()` / `this[k]` resolve. (compile_static_method)

2. Private (`#x`) elements leaked into reflection. They physically live in a
   class instance's keys_array / on the static side but are never reflectable.
   Hidden (gated on class_id != 0, so `{"#fff":1}` literals stay visible) from
   Object.keys/values/entries, getOwnPropertyNames, getOwnPropertyDescriptor,
   hasOwnProperty, JSON.stringify, and object spread / Object.assign — on both
   instances and the class constructor (class-ref).

3. Static field/element initializers never ran for class EXPRESSIONS
   (`var C = class { static x = 1 }`): the `var C = class {…}` fast path and
   the general class-expression arm emitted the ClassRef binding but not the
   inline static-field/static-block init the declaration path emits, so reads
   saw the uninitialized (0.0) slot. They now emit inline StaticFieldSet /
   ClassStaticSymbolSet / static-block calls in source order; the per-evaluation
   ClassExprFresh path is reserved for class expressions inside function bodies
   (factories) where freshness is actually needed.

4. Dynamic property get/set on a runtime class-ref VALUE returned/dropped
   silently. `C[key]` / `C[key] = v` where `C` is a runtime class-ref (e.g. a
   function parameter — propertyHelper's `isWritable(C, name)`) fell through to
   the not-a-pointer `undefined`/no-op path. js_dyn_index_get/set now handle the
   0x7FFE tag; the codegen index get/set paths preserve class-ref tag bits at
   runtime (classref_preserving_handle) for runtime-unknown receivers; and the
   array string-key setter guards class-ref receivers (routing to the by-name
   object setter) instead of dereferencing them as a GC header.

Native-region-proof: the compiler-output-regression `native-region-proof` suite
reports status: pass with NO failed_workloads on a debug build locally (the
config CI uses). The class-field codegen change (static-method `this` slot +
runtime class-ref tag select on dynamic property access) does not perturb the
generic array/buffer native-region proofs.

Zero regressions: broad `built-ins language` shard 0/12 went 76.9% → 77.9%
(+61 gains); the 40 apparent shard "regressions" all pass in isolation on both
this branch and origin/main (parallel-run timeout flakiness), confirmed by
re-running each individually.

Files:
  perry-codegen: codegen/method.rs, expr/index_get.rs, expr/index_set.rs
  perry-hir:     lower/lower_expr.rs, lower/stmt.rs
  perry-runtime: object/{descriptors,object_ops,field_get_set,alloc}.rs,
                 json/stringify.rs, value/dyn_index.rs, array/indexing.rs,
                 typed_feedback.rs

Out of scope (separate feature areas, left for follow-up PRs): async-generator
`yield*` / async-iterator delegation semantics, and parser-level grammar
early-errors (invalid private-name SyntaxErrors).
@proggeramlug proggeramlug merged commit f5cd4e8 into main Jun 8, 2026
13 checks passed
@proggeramlug proggeramlug deleted the class-elements-v2-parity branch June 8, 2026 09:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant