fix(runtime): bind this to undefined for sort/reduce callbacks (#11419) - #11514
Conversation
) Comparators and reduce callbacks were invoked through plain closure calls without resetting the per-thread implicit-`this` cell, so a callback run from inside `obj.m()` saw `obj` instead of `undefined`. The forEach/map/ filter family already bound `undefined`; the array, typed-array and array-like sort/toSorted/reduce/reduceRight entry points now do too, via a new `ImplicitThisScope::bind_undefined` (rooted, restored on unwind).
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (10)
Included review availability: This review used your included allowance. Your plan provides up to 8 included reviews per hour; 2 remain after this review. 📝 WalkthroughWalkthroughArray and typed-array sort comparator and reduce callback entry points now bind implicit ChangesCallback this binding
Priority: ⬇️ Low Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Low Merge Risk: ⚪ Minimal · up to No actionable issue remains from the reviewed changes; the callback regression is covered by output comparison. Mergeable after normal checks. Security Architecture ReviewSecurity architecture risk: 🔵 Low · up to The change corrects callback receiver behavior without adding a callback route or privilege transition. The receiver-restoration mechanism is established, although failure and accessor behavior are not tested through every changed operation. Retained concerns Security review detailsSecurity Blast Radius
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 61.90% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 21 functions across 9 files. (1 skipped: 1 unsupported.)
✨ Finishing Touches 💡 2📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
🛠️ Fix failing CI checks 💡
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 |
876468f to
364b1c9
Compare
Summary
sortcomparators andreduce/reduceRightcallbacks run from inside a method saw that method's receiver asthis, notundefined. The runtime called them through plainjs_closure_callNcalls and never reset the per-thread implicit-thiscell. TheforEach/map/filter/find*/some/every/flatMapfamily already boundundefinedaround their callbacks. This PR does the same at the sort and reduce entry points.Changes
object/this_binding.rs: newImplicitThisScope::bind_undefined(scope). It is the existing rooted guard, so the caller's receiver is restored from a handle slot on return and on unwind.iter_methods.rs'sbind_undefined_thisnow uses it.undefinedat these entry points:js_array_sort_with_comparator, which also servestoSorted(cmp)and the plain-object/typed-array reroutes;js_array_reduce;js_array_reduce_rightjs_typed_array_sort_with_comparator,js_typed_array_to_sorted_with_comparator(both including BigInt lanes),js_typed_array_reduce,js_typed_array_reduce_rightArray.prototype.*.call(obj, …)):object_sort,js_arraylike_reduce,js_arraylike_reduceRightconst t: any = two; t(1, 2)) already resetthisin codegen (runtime/object-model: implement strict and sloppy function this binding #3576). The issue'splain2line already printedundefinedonmain. The new test covers arities 0–3 and direct calls so this stays true.thisstale) is already covered by theimplicit_thistry-savepoint from fix(runtime): root the displaced implicitthisin every guard that restores it (#10490) #10564.this-as-parameter plan. This PR closes the observable leak for these callbacks.Related issue
Fixes #11419
Test plan
New
test-files/test_gap_11419_callback_this_undefined.tscovers array / typed-array (Float64, Int32toSorted, BigInt64) / array-like sort and reduce, plain calls of arities 0–3, and a class method whose ownthismust survive the callbacks.main, with the runtime changes stashed and rebuilt, every surface leaks (leaks sort:object … reduce:object …, dozens of observations).node --experimental-strip-typesbyte for byte.The issue's repro prints only
plain2 undefined, matching Node.34 existing gap tests matching
*this*/*sort*/*reduce*match Node.test_gap_9445_implicit_this_restore_sweepcan't run under local Node 22, but all 34 of its lines reportbad=0under Perry.RUST_TEST_THREADS=1 cargo test --profile perry-dev -p perry-runtime --lib -- sort reduce implicit_this this_binding typed_array: 134 passed.rustfmt --checkon the changed files is clean.scripts/check_file_size.shis OK.Not run locally: the full workspace
cargo test. Left to CI.Added a test under
test-files/Did NOT bump the workspace version or edit CLAUDE.md / CHANGELOG.md
Generated by Claude Code
Summary by CodeRabbit
undefinedasthis, rather than inheriting the enclosing method’s receiver.