fix(proxy): Array.prototype methods on a Proxy-wrapped array (#5196)#5208
Conversation
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
🚧 Files skipped from review as they are similar to previous changes (6)
📝 WalkthroughWalkthroughFixes ChangesProxy array method call dispatch
Sequence Diagram(s)sequenceDiagram
actor Source as JS Source
participant lower_call
participant try_lower_proxy_method_call
participant js_native_call_method
participant dispatch_arraylike_read_method
Source->>lower_call: proxy.map(fn) call site
lower_call->>try_lower_proxy_method_call: callee=ProxyGet(proxy, "map"), args=[fn]
try_lower_proxy_method_call-->>lower_call: emits js_native_call_method(proxy, "map", args)
lower_call->>js_native_call_method: proxy + "map" + args at runtime
js_native_call_method->>dispatch_arraylike_read_method: object=proxy, method="map", refreshed_args
dispatch_arraylike_read_method-->>js_native_call_method: Option<f64> result
js_native_call_method-->>Source: mapped array value
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
A generic Array method (`map`, `reduce`, `forEach`, `filter`, …) called on a Proxy-wrapped array threw `TypeError: Cannot convert undefined or null to object` or SIGSEGV'd. Two distinct gaps: 1. HIR: `arr.<method>(...)` on a proxy local was eagerly folded to dense `Expr::Array*` nodes (`ArrayReduce`/`ArrayForEach`/`ArrayJoin`/…) that dereference the proxy id as a real `ArrayHeader`. `map`/`filter`/`find` only escaped by luck via the `is_class_instance` gate. Skip the array fast path entirely for proxy locals so the call routes through the proxy member-call path. 2. codegen: a fused `proxy.method(args)` (callee `ProxyGet`) for a non call/apply name fell through to the closure-call path, losing the `this` receiver. Route it through `js_native_call_method`, whose Proxy arm binds `this` to the proxy. 3. runtime: the Proxy arm of `js_native_call_method` now short-circuits the non-mutating generic Array methods to the spec-generic array-like engine (extracted as `dispatch_arraylike_read_method`), whose `al_get`/`al_length` already route element/length reads through the proxy `get` trap. This both gives correct semantics and avoids the depth-guard recursion the generic Get→Call fused path would hit on a built-in method value.
…st drift
- cargo fmt the reordered `pub use` block in array/mod.rs (lint gate).
- Add the missing `method("commander", "args", true, None)` manifest row.
`commander::args` landed in NATIVE_MODULE_TABLE via #5137 but only a
`property` entry existed, so the manifest-drift guard
(every_dispatch_entry_has_manifest_counterpart) failed for ANY branch off
current main. Pre-existing, unrelated to #5196, but it blocks the cargo-test
gate.
f6faf2c to
4822477
Compare
Fixes #5196.
Problem
A generic
Array.prototypemethod (map,reduce,forEach,filter,join,sort, …) called on a Proxy-wrapped array either threwTypeError: Cannot convert undefined or null to objector SIGSEGV'd. Individual proxy traps worked; the array-method-on-proxy path did not.Two distinct gaps:
arr.<method>(...)on a proxy local was eagerly folded into denseExpr::Array*nodes (ArrayReduce/ArrayForEach/ArrayJoin/…) that dereference the proxy id as a realArrayHeader→ SIGSEGV.map/filter/findonly escaped by luck via theis_class_instancegate (a proxy local is typedNamed("Proxy")), which is why the reportedmapcase threw rather than crashed.mapcase lowered toCall{callee: ProxyGet}and fell through to the plain closure-call path, losing thethisreceiver, so the method ran withthis = undefined.Fix
local_array_methods.rs— skip the array fast path entirely for proxy locals (ctx.proxy_locals), so all methods route uniformly through the proxy member-call path.proxy_reflect.rs+lower_call/mod.rs— newtry_lower_proxy_method_call: routeproxy.method(args)(noncall/apply) throughjs_native_call_method, whose Proxy arm bindsthisto the proxy.native_call_method.rs— the Proxy arm short-circuits the non-mutating Array methods to the spec-generic array-like engine, whoseal_get/al_lengthalready route element/length reads through the proxygettrap. This gives correct semantics and avoids the depth-guard recursion the generic Get→Call fused path would hit on a built-in method value.generic.rs+array/mod.rs— extracted the array-like dispatch table asdispatch_arraylike_read_methodso both the proto-chain path and the proxy path share it.Verification
10,20,30.gettrap) is byte-identical tonode --experimental-strip-types.call/apply(runtime: implement Proxy apply/construct trap dispatch #3656) still pass.perry-runtime(1041 pass) andperry-hir(full suite) green. The lonebuiltin_prototype_methods_reject_dynamic_newfailure is a known parallel-flake (passes isolated); thecommander::argsmanifest-drift failure is pre-existing onmain.Out of scope
Spreading a proxy array
[...pa]still SIGSEGVs — that's the iterator-protocol (Symbol.iterator) path, independent of array-method dispatch and untouched here. Worth a separate follow-up.No changelog entry or version bump (maintainer folds those in at merge).
Summary by CodeRabbit
New Features
proxy.method(args)).Bug Fixes