You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Perry rejects typed specialization for many imported class methods solely because they capture this. In the public perform-ecs/destroy workload, all major adapter/ECS calls therefore traverse generic runtime method lookup, closure dispatch, and ABI/vtable trampolines even though the receiver classes and method identities are stable.
This blocks both the direct call win and downstream inlining, representation propagation, constant folding, scalar replacement, and allocation removal.
This ticket is distinct from #8672: that PR preserves already-proven direct-method guards across unrelated prototype writes. This issue covers sites where Perry never selects a typed/direct clone because the imported method captures this or its receiver proof is not propagated through the package graph.
Measured on Perry 7ad718ab4287641cb2b29dce3a056edc45d4c7f8, Node 26.5.1, Apple M1 Mac mini.
Runtime
median
Node
1.024601 us/op
Perry
33.904990 us/op
Perry/Node: 33.091x. Every process returned component IDs ["0", "1"] and zero retained view entries.
A fresh three-second Perry profile attributed the top-level workload branches as follows:
entity creation: 33.4%
add position: 26.0%
add velocity: 22.7%
destroy: 16.0%
Thus approximately 82% is create/add work, not destruction. Every top-level branch enters js_typed_feedback_native_call_method_by_id / js_native_call_method, then runtime handle dispatch, js_native_call_value, and a closure/vtable trampoline.
The lowering report contains 494 records, zero typed-path selections, zero scalar replacements, 61 inserted boxes, and 227 typed-path rejections. captures_this alone rejects 56 typed-clone candidates.
Proposed direction
Treat a known class receiver plus stable method identity as sufficient to build a guarded specialized clone even when the body reads/writes this.
Pass a typed/native receiver representation to the clone and preserve field/shape facts across the call.
Propagate class and method metadata through ESM/npm import/export boundaries and through stable adapter objects.
Emit a guarded direct call when the receiver class/shape, prototype method identity, and relevant invalidation generations match.
Inline profitable small/medium methods after direct-call selection so downstream field, array, and allocation optimizations can see the body.
Retain generic lookup/call fallback when any receiver or method proof fails.
Explain selections/rejections, including receiver provenance and this representation, in lowering artifacts.
Semantic constraints
Preserve this binding for extracted, rebound, call/apply, arrow, accessor, proxy, and bound methods.
Correctly invalidate on method replacement/deletion/recreation, prototype replacement/mutation, own-property shadowing, descriptor changes, proxy insertion, and class reevaluation.
Preserve inheritance, super, private brands, derived constructors, and exceptions.
Remain correct under moving/forced GC and refresh receiver/captured roots across collecting calls.
Acceptance criteria
Add the two-file reproduction as a registered semantic and compiler-output fixture; Node and Perry must finish with remaining: 0.
The stable fast arm calls Registry.add/remove and Group.pushEntity/removeEntity directly or through typed clones, without js_native_call_method_by_id, js_typed_feedback_native_call_method_by_id, or generic closure/vtable dispatch.
At least one method that captures this is selected as a typed clone, and --explain-lowering no longer records captures_this as the rejection for that valid site.
Explicit fallback remains in artifacts and is exercised by receiver/method/prototype mutation fixtures.
Tests cover imports/re-exports, inheritance, own shadowing, extraction/rebinding, accessors/proxies, private fields, exceptions, and forced-moving GC.
Re-run ddmills/js-ecs-benchmarksperform-ecs/destroy with the exact state oracle. Require at least a 10% median improvement, at least 9/11 paired wins on the quiet M1 protocol, and no regression in the complete ECS adapter suite.
Report profile deltas for generic method-dispatch samples, executable size, peak RSS, and lowering counts for typed clone selections/rejections.
Summary
Perry rejects typed specialization for many imported class methods solely because they capture
this. In the publicperform-ecs/destroyworkload, all major adapter/ECS calls therefore traverse generic runtime method lookup, closure dispatch, and ABI/vtable trampolines even though the receiver classes and method identities are stable.This blocks both the direct call win and downstream inlining, representation propagation, constant folding, scalar replacement, and allocation removal.
This ticket is distinct from #8672: that PR preserves already-proven direct-method guards across unrelated prototype writes. This issue covers sites where Perry never selects a typed/direct clone because the imported method captures
thisor its receiver proof is not propagated through the package graph.Self-contained reproduction
Create two ESM files:
{ "type": "module" }Build with:
The output must end with
remaining: 0in Node and Perry.The public integration reproduction is
ddmills/js-ecs-benchmarkswithperform-ecs@0.7.8, suiteDestroy:Current evidence
Measured on Perry
7ad718ab4287641cb2b29dce3a056edc45d4c7f8, Node 26.5.1, Apple M1 Mac mini.Perry/Node: 33.091x. Every process returned component IDs
["0", "1"]and zero retained view entries.A fresh three-second Perry profile attributed the top-level workload branches as follows:
Thus approximately 82% is create/add work, not destruction. Every top-level branch enters
js_typed_feedback_native_call_method_by_id/js_native_call_method, then runtime handle dispatch,js_native_call_value, and a closure/vtable trampoline.The lowering report contains 494 records, zero typed-path selections, zero scalar replacements, 61 inserted boxes, and 227 typed-path rejections.
captures_thisalone rejects 56 typed-clone candidates.Proposed direction
this.thisrepresentation, in lowering artifacts.Semantic constraints
thisbinding for extracted, rebound,call/apply, arrow, accessor, proxy, and bound methods.super, private brands, derived constructors, and exceptions.Acceptance criteria
remaining: 0.Registry.add/removeandGroup.pushEntity/removeEntitydirectly or through typed clones, withoutjs_native_call_method_by_id,js_typed_feedback_native_call_method_by_id, or generic closure/vtable dispatch.thisis selected as a typed clone, and--explain-loweringno longer recordscaptures_thisas the rejection for that valid site.ddmills/js-ecs-benchmarksperform-ecs/destroywith the exact state oracle. Require at least a 10% median improvement, at least 9/11 paired wins on the quiet M1 protocol, and no regression in the complete ECS adapter suite.Related work
captures_thisselection failure demonstrated here.for...inenumeration overhead inside the registry methods exposed by this work.