fix(hir+runtime): Constructor.prototype method dispatch on instances#979
Merged
Conversation
Ramda's transducer scaffolding (`XWrap.prototype['@@transducer/step'] = fn`) — and any pre-ES6 library that attaches instance methods through a computed string-literal key — silently fell through to a generic PropertySet because the assignment-side recogniser only matched the `MemberProp::Ident` shape. The dispatch hot path was capable of finding the method via `CLASS_PROTOTYPE_METHODS`; the side-table just never got populated. Two coordinated arms: 1. HIR assignment recogniser (`lower/expr_assign.rs`) — extend the `<funcDecl>.prototype.<key>` matcher to accept `MemberProp::Computed(Lit::Str)` alongside the existing `MemberProp::Ident`. Static string-literal keys only; dynamic computed keys still fall through to PropertySet. 2. HIR read recogniser (`lower/expr_member.rs`) + new HIR variant `Expr::GetFunctionPrototypeMethod` + runtime helper `js_get_function_prototype_method`. `(Foo.prototype as any).method` reads on a function-classic shape now resolve to the registered closure (or `undefined` if none registered), so the spec idiom `typeof Foo.prototype.method === 'function'` returns 'function'. Verified: `test-files/test_constructor_prototype_method.ts` matches `node --experimental-strip-types` line-for-line (5/10/function/15). A standalone IIFE XWrap mirror also passes. Individual ramda module imports work; full `import * as R from 'ramda'` still hits a separate cross-module init issue (documented as next blocker in CHANGELOG).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Foo.prototype['method'] = fn), not justFoo.prototype.method = fn. Lights up ramda's transducer pattern (XWrap.prototype['@@transducer/step'] = fn) and any pre-ES6 lib that uses keys with dashes/slashes/special chars.Expr::GetFunctionPrototypeMethodand runtime helperjs_get_function_prototype_method.(Foo.prototype as any).methodreads on the function-classic shape now resolve to the registered closure, so the spec idiomtypeof Foo.prototype.method === 'function'returns'function'.test-files/test_constructor_prototype_method.tsmatches Node byte-for-byte (5/10/function/15).Background
Perry's existing
CLASS_PROTOTYPE_METHODSside-table already powers instance-method dispatch ((new Foo()).method()reaches the registered closure withthisbound to the receiver — issue #838 + followups). Two narrow gaps remained:Assignment side missed computed string-literal keys. The recogniser in
lower/expr_assign.rsonly matchedMemberProp::Ident. ramda's_xwrap.js(and any TypeScript-subset code that uses keys like@@transducer/step) routes throughMemberProp::Computed, so the assignment fell through to a generic PropertySet on an unobserved proxy and the instance dispatch found nothing. Fix mirrors the same key-resolution shapelower/expr_object.rsalready uses for object-literal computed keys.Read side was always
undefined. Even after a successful Ident-prop registration,(Foo.prototype as any).methodevaluated toundefinedbecause no synthetic prototype object was ever materialised.typeof Foo.prototype.methodcame back'undefined'despite instances dispatching correctly. New recogniser inlower/expr_member.rsemitsExpr::GetFunctionPrototypeMethod, which codegen lowers to a direct lookup into the side-table via the new runtime helper. Skipsclass C {}blocks (those have a real proto object) and named native imports (module-managed).Known next blocker
import * as R from 'ramda'(or any named import of a ramda export) still throwsTypeError: value is not a functionat module init, before any user code runs. Every individual ramda module (import sum from 'ramda/src/sum.js') compiles and runs cleanly — verified by bisecting all 269 entries. So the failure is a cross-module init-time interaction, separate from the prototype-method shape addressed here.Test plan
test-files/test_constructor_prototype_method.tsmatches Node line-for-line:5 / 10 / function / 15new XWrap(add)['@@transducer/step'](10, 5)import sum from 'ramda/src/sum.js'; sum([1,2,3,4,5])returns 15import * as R from 'ramda'— known follow-up, unrelated init-time issue