Context
I'm running a NestJS compatibility probe to map the current Perry → NestJS state, using the existing tests/release/packages/nestjs-hello fixture (@nestjs/common + @nestjs/core + @nestjs/platform-express 11.1.x, compiled via perry.compilePackages). After #4872 the fixture links (42.1 MB executable), but the binary dies during module init, before the server starts:
TypeError: Cannot convert undefined or null to object
at <anonymous>
Reproduced today on main (6378dbd). This is "Wall 2" in the fixture's WALLS.md, previously bisected to @nestjs/common/services/logger.service.js: tsc's class-decorator output calls Object.getOwnPropertyDescriptor(Logger.prototype, "error") inside tslib.__decorate, and Logger.prototype reads as undefined while typeof Logger is "function".
Minimal repro (no node_modules)
const r = (function () {
var L_1: any;
let L: any = (L_1 = class L {
get gi() { return L_1.staticRef; } // member captures outer var → ClassExprFresh
e(m: any) { return m; }
});
return [typeof L, typeof L.prototype];
})();
console.log(JSON.stringify(r));
|
Node (--experimental-strip-types) |
Perry (main, 6378dbd) |
| output |
["function","object"] |
["function","undefined"] |
Trigger matrix (narrows WALLS.md)
The trigger is any class member capturing an outer variable — not just getters:
| variant |
Node |
Perry |
| class expr, no capture |
object |
object ✓ |
| plain method captures outer var |
object |
undefined |
| getter captures outer var |
object |
undefined |
Object.getOwnPropertyDescriptor(L.prototype, "error") (tslib __decorate shape) |
descriptor object |
throws Cannot convert undefined or null to object ← the exact NestJS crash |
Root cause / fix direction
A capturing member routes the class through the ClassExprFresh lowering (captured_args: [LocalGet(..)] in HIR), and .prototype access on a ClassExprFresh value is not wired. The fix needs to give ClassExprFresh values a live .prototype object — and since tslib's __decorate then mutates it via Object.defineProperty, instances must observe the decorated methods (a read-only snapshot prototype is not enough).
This is the single remaining blocker between "links" and "boots" for the NestJS hello fixture; once fixed, the next wall (if any) surfaces at runtime.
Context
I'm running a NestJS compatibility probe to map the current Perry → NestJS state, using the existing
tests/release/packages/nestjs-hellofixture (@nestjs/common+@nestjs/core+@nestjs/platform-express11.1.x, compiled viaperry.compilePackages). After #4872 the fixture links (42.1 MB executable), but the binary dies during module init, before the server starts:Reproduced today on
main(6378dbd). This is "Wall 2" in the fixture'sWALLS.md, previously bisected to@nestjs/common/services/logger.service.js: tsc's class-decorator output callsObject.getOwnPropertyDescriptor(Logger.prototype, "error")insidetslib.__decorate, andLogger.prototypereads asundefinedwhiletypeof Loggeris"function".Minimal repro (no node_modules)
--experimental-strip-types)["function","object"]["function","undefined"]Trigger matrix (narrows WALLS.md)
The trigger is any class member capturing an outer variable — not just getters:
objectobject✓objectundefinedobjectundefinedObject.getOwnPropertyDescriptor(L.prototype, "error")(tslib__decorateshape)Cannot convert undefined or null to object← the exact NestJS crashRoot cause / fix direction
A capturing member routes the class through the
ClassExprFreshlowering (captured_args: [LocalGet(..)]in HIR), and.prototypeaccess on aClassExprFreshvalue is not wired. The fix needs to giveClassExprFreshvalues a live.prototypeobject — and since tslib's__decoratethen mutates it viaObject.defineProperty, instances must observe the decorated methods (a read-only snapshot prototype is not enough).This is the single remaining blocker between "links" and "boots" for the NestJS hello fixture; once fixed, the next wall (if any) surfaces at runtime.