Skip to content

Fix #475: Object.keys/values/entries/for-in hide internal slots + respect enumerability#802

Merged
nickna merged 2 commits into
mainfrom
wrk/object-enum-475
Jun 18, 2026
Merged

Fix #475: Object.keys/values/entries/for-in hide internal slots + respect enumerability#802
nickna merged 2 commits into
mainfrom
wrk/object-enum-475

Conversation

@nickna

@nickna nickna commented Jun 17, 2026

Copy link
Copy Markdown
Owner

The enumeration half of the boxed-primitive cluster, done with the two companion fixes that were missing when it was pulled from #800.

Problem

Boxed wrappers (new String/Number/Boolean) store their internal slots __primitiveType/__primitiveValue as ordinary fields, and the interpreter's Object.keys/values/entries and for-in enumerated obj.Fields directly — leaking those slots and ignoring per-property enumerability.

Fix

  • SharpTSObject.OwnEnumerableKeys() — own data fields minus the internal slots, honoring enumerability — shared by Object.keys/values/entries and both for-in paths so they stay consistent (the keys-vs-for-in mismatch on a boxed String wrapper was the original regression that pulled this from Fix #708, #574, #565: boxed-primitive wrapper coercion #800). A String exotic's length is now non-enumerable.
  • Object.defineProperty preserves omitted attributes — when a partial descriptor (e.g. { writable:false }) redefines an existing property, its writable/enumerable/configurable are preserved per ECMA-262 §10.1.6.3 instead of reset to false (which had been dropping properties from enumeration). Attribute presence is read prototype-aware.

Verification

Compiled baseline unchanged (interpreter-only fix).

nickna added 2 commits June 17, 2026 14:41
…pect enumerability

Boxed primitive wrappers (new String/Number/Boolean) store their internal slots
__primitiveType/__primitiveValue as ordinary fields, and the interpreter's
Object.keys/values/entries and for-in enumerated obj.Fields directly — leaking
those slots and ignoring per-property enumerability.

- Add SharpTSObject.OwnEnumerableKeys() (data fields minus internal slots,
  honoring enumerability), shared by Object.keys/values/entries and both for-in
  paths so they stay consistent (fixes the keys-vs-for-in mismatch on a boxed
  String wrapper). A String exotic's `length` is marked non-enumerable.
- Object.defineProperty now preserves an existing property's writable/enumerable/
  configurable attributes when the descriptor omits them (ECMA-262 10.1.6.3),
  via PreserveOmittedAttributes — attribute presence is read prototype-aware.
  Previously a partial redefine (e.g. {writable:false}) reset the others to
  false, dropping the property from enumeration.

Net +96 interpreted Test262 (Object/create, Object/defineProperties,
Object/defineProperty flag-preservation, Object/keys, for-in). One inherent
regression — Object/defineProperty/15.2.3.6-3-40-1 — from a separate gap (a
descriptor object's INHERITED attributes aren't read through its prototype);
tracked as a follow-up. The data-value-preservation half (FromObject defaults an
omitted `value` to the undefined sentinel) is deferred to that same follow-up
since it depends on prototype-aware descriptor reads.

Full suite green (13419).
Enumeration + defineProperty attribute-preservation (#475) flip 96 interpreted
Test262 tests to Pass (Object/create, Object/defineProperties, Object/defineProperty
flag-preservation, Object/keys, for-in). One inherent regression recorded:
Object/defineProperty/15.2.3.6-3-40-1 (Pass->Fail) — descriptor inherited-attribute
read gap, tracked in #801. Isolated by regenerating interpreted.txt on main vs this
branch; only these 97 lines changed. Compiled baseline unchanged (interp-only fix).
@nickna nickna merged commit b9d3aa3 into main Jun 18, 2026
3 checks passed
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.

Boxed primitive wrappers leak __primitiveType/__primitiveValue into enumeration; interp Object.keys ignores enumerability

1 participant