Skip to content

fix(runtime): run ToPropertyKey for every in-operator key type (#6944) - #7127

Merged
proggeramlug merged 3 commits into
mainfrom
fix/6944-in-to-property-key
Jul 31, 2026
Merged

fix(runtime): run ToPropertyKey for every in-operator key type (#6944)#7127
proggeramlug merged 3 commits into
mainfrom
fix/6944-in-to-property-key

Conversation

@proggeramlug

@proggeramlug proggeramlug commented Jul 31, 2026

Copy link
Copy Markdown
Contributor

Closes #6944.

Root cause

Per spec, RelationalExpression in ShiftExpression is ToPropertyKey(lval) for every key type, but js_object_has_property only ran the coercion for number keys (the arm added for Next.js's Number(digest.at(-2)) in RedirectStatusCode). An object key was compared as a raw pointer and never matched, and its Symbol.toPrimitive / toString / valueOf never ran — observable even when the property is absent.

Fix

crates/perry-runtime/src/object/field_get_set/has_property.rs: every non-string, non-symbol key now goes through js_to_property_key before the lookup. Strings (heap or SSO) and symbols are already property keys — coercion is identity and allocates nothing — so they keep the pre-fix fast path verbatim.

The coercion allocates and (for object keys) runs user JS, so it can GC and evacuate the receiver. The receiver is rooted across it via RuntimeHandleScope::root_heap_word_u64 and read back through the handle — the same idiom as js_object_get_property_key / js_object_set_property_key (#6935) and the number arm's #6941 rooting. The user coercion runs exactly once, before the lookup, so it fires even when the property is absent.

Tests

New gap test test-files/test_gap_in_operator_to_property_key.ts, byte-identical to node 26.5.1 (the pinned oracle): object keys via toString / valueOf fallback / Symbol.toPrimitive, exactly-once coercion on an absent property, object→Symbol coercion matching a symbol-keyed property, primitive keys (true / null / undefined / 1n), int32-vs-f64 numeric-key agreement on a plain object, and a proxy has trap receiving the coerced key.

Validation

Note: scripts/check_file_size.sh currently reports 15 pre-existing >2000-line files on main (none touched by this PR; has_property.rs is 1321 lines). Flagging so a red lint job on this PR is not misread — the offenders are identical on main.

Summary by CodeRabbit

  • Bug Fixes

    • Improved the in operator so object and primitive property keys are consistently converted before property checks.
    • Preserved correct coercion behavior for custom conversion methods, symbols, numbers, and missing properties.
    • Ensured proxy checks receive the converted property key.
  • Tests

    • Added regression coverage for coercion order, single invocation, symbol and numeric keys, and proxy behavior.

Ralph Küpper added 3 commits July 31, 2026 09:39
Per spec, `RelationalExpression in ShiftExpression` is ToPropertyKey(lval)
for every key type, but js_object_has_property only coerced number keys.
An object key was compared as a raw pointer and never matched, and its
Symbol.toPrimitive / toString / valueOf never ran — observable even when
the property is absent.

Widen the coercion to every non-string, non-symbol key (strings and
symbols are already property keys; coercion is identity and allocates
nothing, so they keep the fast path). The receiver stays rooted across
the GC-capable coercion via RuntimeHandleScope, mirroring
js_object_get_property_key / js_object_set_property_key (#6935/#6941).

New gap test test_gap_in_operator_to_property_key.ts covers object keys
(toString / valueOf fallback / Symbol.toPrimitive), exactly-once
coercion on absent properties, object-to-Symbol coercion, primitive
keys (boolean/null/undefined/bigint), int32-vs-f64 numeric agreement,
and proxy has-trap receiving the coerced key — byte-identical to node
26.5.1.
@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The in operator now applies ToPropertyKey to all non-string and non-symbol keys. The runtime preserves GC-safe receiver handling. Tests cover coercion order, primitive keys, absent properties, numeric keys, symbols, and proxy traps.

Changes

in-operator property-key coercion

Layer / File(s) Summary
Runtime property-key coercion
crates/perry-runtime/src/object/field_get_set/has_property.rs
js_object_has_property now coerces all non-string and non-symbol keys. The receiver remains rooted during coercion and is reloaded afterward.
Regression coverage and changelog
test-files/test_gap_in_operator_to_property_key.ts, changelog.d/7127-in-operator-to-property-key.md
Tests validate coercion order, single invocation counts, primitive and symbol keys, numeric-key behavior, absent properties, and proxy has traps. The changelog documents the fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

  • PerryTS/perry#6757: Both changes modify js_object_has_property and the in-operator path.
  • PerryTS/perry#6941: This change extends its GC-safe property-key coercion from numeric keys to all non-string and non-symbol keys.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The changes satisfy issue #6944 by coercing all applicable keys exactly once and rooting the receiver across GC-capable coercion.
Out of Scope Changes check ✅ Passed The changelog entry, runtime fix, and regression tests directly support the linked issue and stated objectives.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly and concisely describes the runtime fix for applying ToPropertyKey to every in-operator key type.
Description check ✅ Passed The description explains the root cause, fix, related issue, tests, and validation, but it does not follow the template headings or include the checklist.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/6944-in-to-property-key

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/perry-runtime/src/object/field_get_set/has_property.rs (1)

211-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use property_key_coercion_is_inert() here too.

Replace the inline String/Symbol check with super::super::property_key_coercion_is_inert(key) so this coercs-as-is case stays aligned with the existing helper used by other property-key paths.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/perry-runtime/src/object/field_get_set/has_property.rs` around lines
211 - 222, In the key normalization block, replace the inline string-or-symbol
condition with super::super::property_key_coercion_is_inert(key). Preserve the
existing unchanged-key and coercion branches, including object rooting and
property-key conversion.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/perry-runtime/src/object/field_get_set/has_property.rs`:
- Around line 211-222: In the key normalization block, replace the inline
string-or-symbol condition with
super::super::property_key_coercion_is_inert(key). Preserve the existing
unchanged-key and coercion branches, including object rooting and property-key
conversion.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f80da387-51c3-4837-8948-599dacb86f23

📥 Commits

Reviewing files that changed from the base of the PR and between ed6c2f4 and 9ddd6e6.

📒 Files selected for processing (3)
  • changelog.d/7127-in-operator-to-property-key.md
  • crates/perry-runtime/src/object/field_get_set/has_property.rs
  • test-files/test_gap_in_operator_to_property_key.ts

@proggeramlug
proggeramlug merged commit 0044b1c into main Jul 31, 2026
7 checks passed
@proggeramlug
proggeramlug deleted the fix/6944-in-to-property-key branch July 31, 2026 09:07
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.

runtime: objectKey in obj never runs ToPropertyKey (only number keys are coerced)

1 participant