Skip to content

fix(runtime): built-in prototype methods are non-enumerable (spec descriptor) - #2554

Merged
proggeramlug merged 1 commit into
mainfrom
fix/builtin-method-nonenumerable
May 29, 2026
Merged

fix(runtime): built-in prototype methods are non-enumerable (spec descriptor)#2554
proggeramlug merged 1 commit into
mainfrom
fix/builtin-method-nonenumerable

Conversation

@proggeramlug

Copy link
Copy Markdown
Contributor

Summary

Built-in prototype methods (Array/String/Number/Object/TypedArray/… .prototype.*) were installed via the ordinary field-set path, which defaults to { writable: true, enumerable: true, configurable: true }. Per spec they are non-enumerable, so Perry disagreed with Node on all three observation paths:

Object.getOwnPropertyDescriptor(Array.prototype, "map").enumerable  // Perry: true,  Node: false
for (const k in Array.prototype) { /* ... */ }                      // Perry: 42 methods, Node: 0
Object.keys(Array.prototype)                                        // Perry: listed them, Node: []

This fails Test262's pervasive verifyProperty checks (every built-in method test verifies the property is non-enumerable, via both getOwnPropertyDescriptor and a for-in scan).

Fix

Record a non-enumerable data-property descriptor at install time, via a new set_builtin_property_attrs helper. Like the existing #2060 built-in accessor path, it inserts into PROPERTY_DESCRIPTORS without flipping the process-wide GLOBAL_DESCRIPTORS_IN_USE hot-path gate.

This works because getOwnPropertyDescriptor, Object.keys, and for-in (which lowers to Object.keys) each read PROPERTY_DESCRIPTORS per-object and unconditionally — so they now observe enumerable:false — while the object get/set hot path keeps skipping the descriptor table for every program (no perf regression).

Testing

Now matches Node exactly:

for (k in Array.prototype) → 0 keys
Object.getOwnPropertyDescriptor(Array.prototype,"map") → { writable:true, enumerable:false, configurable:true }

User-object for-in / Object.keys unaffected ({a,b,c}['a','b','c']). cargo fmt clean.

Scope

Foundational spec-correctness for the reflective layer. On its own it moves the Test262 conformance % only marginally (each verifyProperty test also gates on writable-via-write, configurable-via-delete, exact value, and .name/.length own-properties — all of which must pass together), but the non-enumerable descriptor is a prerequisite that every built-in-method conformance test needs.

Version bump + CHANGELOG entry omitted — please fold in at merge.

…criptor)

`install_proto_method` stored every built-in prototype method
(Array/String/Number/Object/TypedArray/... .prototype.*) via the ordinary
field-set path, which defaults to { writable:true, enumerable:true,
configurable:true }. Per spec these are non-enumerable, so:

  Object.getOwnPropertyDescriptor(Array.prototype, 'map').enumerable  // was true
  for (k in Array.prototype) ...                                      // yielded 42 methods
  Object.keys(Array.prototype)                                        // listed them

all disagreed with Node and failed Test262's pervasive verifyProperty checks.

Record a non-enumerable data-property descriptor at install time via a new
set_builtin_property_attrs helper that, like the #2060 builtin-accessor path,
inserts into PROPERTY_DESCRIPTORS WITHOUT flipping the process-wide
GLOBAL_DESCRIPTORS_IN_USE hot-path gate. getOwnPropertyDescriptor, Object.keys
and for-in each read PROPERTY_DESCRIPTORS per-object and unconditionally, so all
three now observe enumerable:false — while the object get/set hot path keeps
skipping the descriptor table for every program. Now matches Node:
`for (k in Array.prototype)` yields 0; `...map` descriptor is
{ writable:true, enumerable:false, configurable:true }.
@proggeramlug
proggeramlug merged commit 639ad63 into main May 29, 2026
11 checks passed
@proggeramlug
proggeramlug deleted the fix/builtin-method-nonenumerable branch May 29, 2026 17:29
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