fix(hir): class static-field reflection remnant#5036
Merged
Conversation
Three shared root causes behind the class/elements static-field cluster: 1. Object.keys(C) / for-in returned nothing for a class constructor. A class ref is INT32-tagged (not a heap pointer), so js_object_keys_value fell through to an empty array. Added a class-ref arm returning the enumerable static-field keys (CLASS_DYNAMIC_PROPS, ECMA order; #-private and the non-enumerable length/name/prototype + static methods excluded), via a new class_own_enumerable_field_names helper. 2. Object.prototype.propertyIsEnumerable.call(C, 'f') returned false for a static field: extract_obj_ptr nulls out on the INT32 ClassRef payload, so every key reported non-enumerable. Added a class-ref string-key arm: static fields enumerable, everything else non-enumerable. This was the third leg of verifyProperty's isEnumerable check (for-in + hasOwn + propertyIsEnumerable) — fixes #1/#2 together let the descriptor cluster pass. 3. Uninitialized non-computed static fields (static foo; static 0;) were registered by init_static_fields_late, which runs AFTER user statements — so Object.keys/getOwnPropertyDescriptor immediately after the class decl saw nothing. Their value is the compile-time constant undefined and a class name is in TDZ before its declaration, so registration moved to init_static_fields_early (before user code); the late else-branch was removed (it also clobbered any C.foo=... write made before module-init end). test262 language/{statements,expressions}/class: pass 5088 -> 5094 (+6), runtime-fail 196 -> 190, diff/compile-fail unchanged (zero regressions). native-region-proof gate unchanged vs origin/main (its sole failure, h1_buffer_alias_negative, reproduces identically on clean main — pre-existing).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes the static-field leaf of the test262
language/{statements,expressions}/classremnant by fixing three shared root causes behind theclass/elements/static-*cluster. All three converge on a single observation: a class constructor is an INT32-taggedClassRef, not a heap object, so the generic object reflection paths skipped it.test262
language/statements/class+language/expressions/class:pass 5088 → 5094 (+6),runtime-fail 196 → 190,diff/compile-failunchanged → zero regressions.Root causes & fixes
Object.keys(C)/for (k in C)returned[].js_object_keys_valuehad noClassRefarm; the INT32 value isn't a pointer, so it fell through to an empty array. Added a class-ref arm returning the enumerable static-field keys fromCLASS_DYNAMIC_PROPSin ECMA order (#-private and the non-enumerablelength/name/prototype+ static methods excluded), via a newclass_own_enumerable_field_nameshelper. BothObject.keysand the for-in desugar funnel through this function.Object.prototype.propertyIsEnumerable.call(C, 'f')returnedfalse.extract_obj_ptrnulls out on the INT32ClassRefpayload, so every key was reported non-enumerable. Added a class-ref string-key arm: a registered static field → enumerable, everything else → non-enumerable. This is the third leg ofverifyProperty'sisEnumerable(for-in +hasOwnProperty+propertyIsEnumerable); only with Support custom menu bar items #1 and linux compilation and README #2 does the descriptor cluster pass.Uninitialized non-computed static fields (
static foo;,static 0;) registered too late.They were handled by
init_static_fields_late, which runs after user statements — soObject.keys(C)/getOwnPropertyDescriptor(C, "foo")immediately after the class declaration saw nothing. Their value is the compile-time constantundefined, and a class name is in TDZ before its declaration, so registration moved toinit_static_fields_early(before user code). The lateelse-branch was removed — it also clobbered anyC.foo = …the program performed before module-init end.Out of scope (remaining static-field tail)
static ['constructor'];) route through the symbol side-table rather thanCLASS_DYNAMIC_PROPS(string-vs-symbol key routing).static 0 = 'bar'; C[0]).static [i++] = i++).Validation
passstrictly up, no bucket grew).native-region-proofgate: unchanged vsorigin/main. Its sole failure (h1_buffer_alias_negative, an unrelated HTTP buffer-alias codegen proof) reproduces identically on a cleanorigin/mainbuild — pre-existing, not introduced here.cargo fmt --allclean; file-size gate green (all edited large files already allowlisted).Files
crates/perry-runtime/src/object/field_get_set.rs—js_object_keys_valueclass-ref armcrates/perry-runtime/src/object/object_ops.rs—propertyIsEnumerableclass-ref armcrates/perry-runtime/src/object/class_registry.rs—class_own_enumerable_field_namescrates/perry-runtime/src/object/descriptors.rs—sort_property_names_ecmamadepub(crate)crates/perry-codegen/src/codegen/helpers.rs— early registration of no-init static fields