Skip to content

Fix Proxy [[OwnPropertyKeys]] check for integer-index keys (#1609)#2095

Open
tangtaizong666 wants to merge 1 commit into
facebook:static_hfrom
tangtaizong666:fix-proxy-ownkeys-numeric-index-key
Open

Fix Proxy [[OwnPropertyKeys]] check for integer-index keys (#1609)#2095
tangtaizong666 wants to merge 1 commit into
facebook:static_hfrom
tangtaizong666:fix-proxy-ownkeys-numeric-index-key

Conversation

@tangtaizong666

Copy link
Copy Markdown

Summary

Reflect.ownKeys / Object.keys / Object.getOwnPropertyNames on a Proxy
whose target is non-extensible and has an integer-index own key (e.g.
"12345") throws

TypeError: ownKeys target is non-extensible but key is missing from trap result

even when the trap faithfully returns the target's own keys. This breaks common
state libraries such as Valtio (see #1609), where a frozen snapshot object with
numeric string keys can no longer be enumerated.

Root cause. JSProxy::ownPropertyKeys enforces the ES [[OwnPropertyKeys]]
invariant (steps 19 and 21) by checking that every own key of the target is
present in the trap result using SameValue. Hermes represents integer-index
own-property keys internally as numbers (JSObject::getOwnPropertyKeys
returns 12345 as a Number), whereas the trap result is validated to contain
only Strings and Symbols and therefore holds the key as the String "12345".
SameValue(12345, "12345") is always false, so the invariant check fails and
throws. The comparison only runs for non-extensible targets, which is why the
bug only surfaces once the target is frozen / preventExtensions'd.

Fix. Before the invariant comparisons, normalize any numeric index keys in
targetKeys to their canonical string form — the same conversion that for-in
enumeration already performs for numeric array indices
(Interpreter-slowpaths.cpp, "We must return the property as a string"). The
returned key list is unchanged (it still comes from the trap result), so this
only removes the spurious invariant failure.

Test Plan

New regression test test/hermes/regress-proxy-ownkeys-numeric-key.js covering:

  • an integer-index key on a preventExtensions target (configurable-keys loop),
  • integer-index keys on a Object.freezed target (non-configurable-keys loop),
  • mixed integer/string key ordering, and
  • integer-index keys alongside symbols.
$ ./build-rel/bin/hermes -Xes6-proxy -non-strict -O -target=HBC \
    test/hermes/regress-proxy-ownkeys-numeric-key.js
case1: 12345
case2: 0,42
case3: 1,2,b,a
case4: 7,Symbol(s)

The test passes with this change and fails on the unpatched engine with the
reported TypeError. Existing test/hermes/proxy.js continues to pass.

Fixes #1609

…1609)

JSProxy::ownPropertyKeys enforces the ES [[OwnPropertyKeys]] invariant
by checking that every own key of the target is present in the ownKeys
trap result using SameValue. Hermes represents integer-index
own-property keys (e.g. "12345") internally as numbers, while the trap
result is validated to hold only Strings and Symbols. SameValue(Number,
String) is always false, so any integer-index own key on a
non-extensible target spuriously failed the invariant and threw
"ownKeys target is non-extensible but key is missing from trap result".

Normalize the numeric index keys in targetKeys to their canonical string
form before the invariant comparisons, mirroring how for-in enumeration
already converts numeric array indices to strings. The returned key list
is unchanged, so this only removes the spurious failure.

Fixes facebook#1609.
@meta-cla meta-cla Bot added the CLA Signed Do not delete this pull request or issue due to inactivity. label Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CLA Signed Do not delete this pull request or issue due to inactivity.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TypeError: ownKeys target is non-extensible but key is missing from trap result error when using numeric string keys on Hermes

1 participant