Skip to content

Class method body not executed when class is imported from another module #83

@proggeramlug

Description

@proggeramlug

Summary

On Perry 0.5.93, when a class is defined in module A and its method is called on an instance obtained in module B, the method body never executes. Perry returns a stub value of the declared return type. Same-file class method calls work correctly.

This is the final blocker for @perry/postgres: every conn.query(...) call on the driver's Connection instance returns an empty QueryResult stub instead of actually running the query. The class is defined in src/connection.ts, re-exported from src/index.ts, imported by user scripts — that import boundary is enough to trigger this.

Repro

perry-classlib/
├── lib.ts
└── main.ts

lib.ts:

export class Thing {
    private readonly _id: number;
    constructor(id: number) { this._id = id; }

    doWork(s: string): Promise<string> {
        console.log('[Thing.doWork] entered id=' + this._id + ' s=' + s);
        return new Promise<string>((resolve) => {
            setTimeout(() => resolve('work-done-' + s), 10);
        });
    }
}

export function makeThing(): Promise<Thing> {
    return new Promise<Thing>((resolve) => {
        setTimeout(() => resolve(new Thing(42)), 10);
    });
}

main.ts:

import { makeThing } from './lib';

async function main(): Promise<void> {
    const t = await makeThing();
    console.log('typeof t=' + typeof t + ' typeof doWork=' + typeof t.doWork);
    const r = await t.doWork('hi');
    console.log('r=' + r);
}

main().then(() => console.log('[main] done'));

Observed on Perry 0.5.93

typeof t=object typeof doWork=undefined
r=[object Object]
[main] done
  • [Thing.doWork] entered is never logged — the method body never ran.
  • r is [object Object] — Perry returned a raw stub object instead of await-ing the Promise chain to a string.

Expected (Bun / Node output)

typeof t=object typeof doWork=function
[Thing.doWork] entered id=42 s=hi
r=work-done-hi
[main] done

Same-file case works fine

Moving Thing and makeThing into the same file as main() makes the method body execute correctly. The bug is specifically about the import boundary.

Likely connected to

  • typeof obj.method returning 'undefined' even when the method is callable — probably the same method-lookup path misses on imported classes (unrelated but-also-broken symptom: on a same-file class, typeof obj.method returns 'undefined' but the call still works).
  • await returns stub before setTimeout fires when resolved value is an object literal #77 (fixed) gave me a working path for Promises resolving object literals — that exposed this, because the stub return value from cross-module method calls is what the driver was getting as QueryResult (all fields undefined).

Impact

Every TS library that exports a class with methods and expects consumers to import it is currently non-functional on Perry. This includes @perry/postgres (blocks all queries).

Environment

  • Perry 0.5.93
  • macOS 26.4
  • perry run main.ts

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions