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
An immediately-constructed class expression — new (class extends Base {})() — never registers its Subclass → Base edge in the class registry when Base is a builtin. The instance comes out parentless: instanceof Base is false, and every inherited native method/field reads as undefined.
The same class expression works fine as soon as it's bound to a name first, which is what makes this a registration-timing hole rather than a class-expression hole.
Repro
// ── builtin parent, immediately constructed: BROKEN ──constev=new(classextendsEvent{})("tick");console.log(evinstanceofEvent,ev.type);// perry: false undefined | node: true tickconstet=new(classextendsEventTarget{})();console.log(etinstanceofEventTarget);// perry: false | node: trueconsole.log(typeofet.addEventListener);// perry: undefined | node: functionet.addEventListener("q",()=>{});// perry: TypeError: value is not a function// ── same shape, USER parent: works ──classBase{hello(){return"hi";}}constp=new(classextendsBase{})();console.log(pinstanceofBase,typeofp.hello);// perry: true function | node: true function// ── builtin parent, bound to a const first: works ──constK=classextendsEventTarget{};console.log(newK()instanceofEventTarget);// perry: true | node: true
Also affected, and worth noting because it's a different symptom of the same area:
constE2=classextendsEvent{};newE2("x");// perry: TypeError: Event is not a function
Expected
All of the above match node --experimental-strip-types: instanceof is true and the inherited surface resolves.
The registry edge for a builtin parent is normally wired at class-definition time by js_register_class_parent_dynamic, which resolves the parent through global_builtin_constructor_class_id (crates/perry-runtime/src/object/instanceof.rs). For a class expression in callee position (new (class … {})()) that definition-time registration appears never to run, so get_parent_class_id(subclass_id) returns nothing and every chain walk (class_chain_is_event_target, is_event_instance, instanceof) bails.
Consequence: any feature that identifies a receiver by class chain silently degrades to "not a member of the base" for this shape. That's the shared failure mode behind both the Event and EventTarget rows above.
Found while verifying a review finding on #6311 (EventTarget subclassing); filing separately rather than growing that PR's scope, since it equally affects Event on main.
Summary
An immediately-constructed class expression —
new (class extends Base {})()— never registers itsSubclass → Baseedge in the class registry whenBaseis a builtin. The instance comes out parentless:instanceof Baseisfalse, and every inherited native method/field reads asundefined.The same class expression works fine as soon as it's bound to a name first, which is what makes this a registration-timing hole rather than a class-expression hole.
Repro
Also affected, and worth noting because it's a different symptom of the same area:
Expected
All of the above match
node --experimental-strip-types:instanceofistrueand the inherited surface resolves.Notes / where to look
Event, which long predates theEventTargetwork in fix(runtime): EventTarget methods must live on the prototype so subclasses inherit them (#6301) #6311/runtime:class X extends EventTargetdoesn't inherit addEventListener/dispatchEvent (root cause of #5931, breaks cac) #6301.ErrorandArraydo not reproduce (they have their own construct paths), which suggests the gap is in how the class-expression lowering resolves a builtinextendstarget by name.js_register_class_parent_dynamic, which resolves the parent throughglobal_builtin_constructor_class_id(crates/perry-runtime/src/object/instanceof.rs). For a class expression in callee position (new (class … {})()) that definition-time registration appears never to run, soget_parent_class_id(subclass_id)returns nothing and every chain walk (class_chain_is_event_target,is_event_instance,instanceof) bails.EventandEventTargetrows above.Found while verifying a review finding on #6311 (EventTarget subclassing); filing separately rather than growing that PR's scope, since it equally affects
Eventonmain.