fix(runtime): run ToPropertyKey for every in-operator key type (#6944) - #7127
Conversation
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.
📝 WalkthroughWalkthroughThe Changes
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
crates/perry-runtime/src/object/field_get_set/has_property.rs (1)
211-222: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winUse
property_key_coercion_is_inert()here too.Replace the inline
String/Symbolcheck withsuper::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
📒 Files selected for processing (3)
changelog.d/7127-in-operator-to-property-key.mdcrates/perry-runtime/src/object/field_get_set/has_property.rstest-files/test_gap_in_operator_to_property_key.ts
Closes #6944.
Root cause
Per spec,
RelationalExpression in ShiftExpressionisToPropertyKey(lval)for every key type, butjs_object_has_propertyonly ran the coercion for number keys (the arm added for Next.js'sNumber(digest.at(-2)) in RedirectStatusCode). An object key was compared as a raw pointer and never matched, and itsSymbol.toPrimitive/toString/valueOfnever 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 throughjs_to_property_keybefore 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_u64and read back through the handle — the same idiom asjs_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 viatoString/valueOffallback /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 proxyhastrap receiving the coerced key.Validation
node --experimental-strip-types(16 lines).in-operator gap tests (test_gap_in_operator_numeric_key,test_gap_in_operator_closure,test_gap_fetch_handle_in_operator): match node, before and after rebase onto currentmain.test_issue_3558_computed_accessors(computed static string setter), fails identically on pristinemain(verified by stash-rebuild A/B) — pre-existing, unrelated; the static-accessor set path has nojs_object_has_propertycaller.cargo test -p perry-runtime: 1563 passed; the 5 failures under default parallelism all pass serially and are the known parallel-isolation issues (test: namespace_members_exist_with_expected_shapes fails deterministically under default-parallel cargo test (green serially) #6965/flaky: global_this_webassembly::namespace_members_exist_with_expected_shapes fails under parallel cargo test on current main (6/6), passes serial/solo #6926), none in property-key code.origin/main@ ed6c2f4 and re-verified.Note:
scripts/check_file_size.shcurrently reports 15 pre-existing >2000-line files onmain(none touched by this PR;has_property.rsis 1321 lines). Flagging so a redlintjob on this PR is not misread — the offenders are identical onmain.Summary by CodeRabbit
Bug Fixes
inoperator so object and primitive property keys are consistently converted before property checks.Tests