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
{{ message }}
Repository navigation
class instance property reassignment ignored: this.method = X dispatched to prototype, not own property #620
obj.method = newFn writes an own property on the instance, but Perry's class-method dispatch always invokes the prototype/class-vtable method — the own-property override is ignored. Bites any code that uses the "first call sets up the fast path" pattern (SmartRouter in hono, memoized dispatch tables, polymorphic dispatch via assignment).
Minimal repro
classRouter{routes: any[]|undefined=[];match(path: string): string{console.log("[Router.match] original called");constinner=(p: string)=>`inner:${p}`;this.match=inner;returninner(path);}}constr=newRouter();console.log(r.match("/foo"));console.log(r.match("/bar"));
Expected (Node):
[Router.match] original called
inner:/foo
inner:/bar ← second call goes to `inner`
Actual (Perry):
[Router.match] original called
inner:/foo
[Router.match] original called ← second call ALSO hits the original method
inner:/bar
Why it matters
Common JS pattern. Hono's SmartRouter.match (node_modules/hono/dist/router/smart-router/router.js:23) does exactly this:
match(method,path){if(!this.#routes)thrownewError("Fatal error");// ... pick a sub-router, build it ...this.match=router.match.bind(router);// ← rebind for fast paththis.#routes =void0;returnres;}
On the second app.fetch(...) call, Perry calls the original SmartRouter.match again instead of the bound router.match. The body checks if (!this.#routes) throw new Error("Fatal error") and throws — surfacing in #603 as silent failure of every call after the first.
Same pattern in jQuery, Underscore, lodash's once/memoize, redux's lazy action creators, and any framework that uses dispatch-table promotion.
Fix shape
Class method dispatch should consult the instance's own-property dictionary BEFORE falling back to the class vtable. Currently the dispatch path in crates/perry-codegen/src/lower_call.rs and the js_object_call_method runtime helper short-circuit to the class vtable when the receiver is a known class instance.
This may be invasive — the fast path optimisation that skips the property dictionary is load-bearing for class-method-call performance. Possible mitigations:
Detect "instance has been mutated to override method" via a per-instance flag and disable the fast path for that instance only.
Add a write-barrier on this.<methodName> = X for known class methods that flips a "shadowed methods" bitset on the receiver, consulted by the dispatch path.
Less invasively: special-case the assignment shape this.<bound-method-name> = X inside a class method body and emit a runtime call that records the override in the instance's overflow property dictionary, plus a runtime arm in js_native_call_method / class method dispatch that checks the dict first.
Closed in v0.5.715: class-method dispatch fast path now consults instance own-properties before falling back to the static method. Implementation in crates/perry-codegen/src/lower_call.rs::emit_own_method_override_check + new runtime probe js_object_get_own_field_or_undef. Pattern this.method = X; ... this.method(args) correctly routes to the override on subsequent calls. Test: test-files/test_issue_620.ts.
Activity
proggeramlug commentedon May 9, 2026
Closed in v0.5.715: class-method dispatch fast path now consults instance own-properties before falling back to the static method. Implementation in
crates/perry-codegen/src/lower_call.rs::emit_own_method_override_check+ new runtime probejs_object_get_own_field_or_undef. Patternthis.method = X; ... this.method(args)correctly routes to the override on subsequent calls. Test:test-files/test_issue_620.ts.fix: closes #619 #620 #621 — async sync-throw routing + this.method o…
fix: #620 followup — own-property override probe on dynamic-dispatch …
fix: closes #632 — bind IMPLICIT_THIS around override-path js_native_…
fix(runtime): a subclass override of a native base method must win ov…
perf: learned receiver words for class-method sites; setPrototypeOf o…