Repository navigation
Conversation
…tter
An emitter used both through its static type and through an untyped
reference aborted: a typed `on` followed by an untyped `emit` (or a typed
`emit` from a class body reaching a listener added through `any`) hit
"computed event collided with a fixed event", and an untyped
`emit('error', ...)` trapped outright. Listener entries held either a
typed adapter or a checked-dynamic thunk, and the emit core trapped when
an entry lacked the form the emit carried.
Entries may now carry both forms. Listeners registered through
scr_emitter_on_dyn keep their thunk beside the typed adapter; typed
emits call the adapter and untyped emits call the thunk with the
arguments as given. The class dispatch of compiled emitters registers
untyped listeners under an event typed code fixes with a typed adapter
for its tuple, and converts the arguments of an untyped emit of such an
event to the tuple for listeners with typed parameters
(scr_emitter_emit_mixed; a mismatch throws the checked conversion's
TypeError). Registrations and emits through untyped values otherwise go
through scr_emitter_on_any/emit_any: runtime adapters under 'error' and
the meta events, and Node's unhandled-'error' contract. The emit core
throws instead of trapping when an entry has no usable form. The
dynamic half of the registry sits behind a hook only on_any installs,
and on_flex/emit_flex keep their plain behavior, so programs without
untyped emitter calls link none of it.
listenerCount, eventNames, removeAllListeners, listeners, rawListeners,
setMaxListeners and getMaxListeners through an untyped value on a
compiled emitter answered "is not a function"; they now dispatch to the
native registry.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Reading an EventEmitter.prototype method off a compiled emitter (a program class extending EventEmitter, or new EventEmitter()) through an untyped value answered undefined (`typeof o.on`, `const f = o.on; f.call(o, ...)`, `o.emit.bind(o)`), and EventEmitter.prototype.on in JavaScript was a fence. Such reads, typed value reads, computed keys, `in`, and EventEmitter.prototype.<method> now answer the runtime's EventEmitter.prototype functions, the ones constructor-function emitters inherit, so `o.on === o.addListener === EventEmitter.prototype.on` and names and lengths match Node. The first read installs a program dispatch that those functions call for a compiled emitter receiver; it invokes the method through the existing untyped dispatch with the call's argument count (emit forwards up to 16 arguments after the name, then fences). An untyped emit on, or read of emit from, a class that overrides emit now fences instead of bypassing the override for the native emit. A subclass visited under the boxed EventEmitter capsule keeps its own dispatch plan for values boxed as itself. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The dispatch instantiates one bridged emit per argument count and emitter class; capping it at four keeps programs that only read emitter members through computed keys from growing by the full table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch was successfully deployed
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.
An EventEmitter used both through its static type and through an untyped reference aborted the process. A typed
onfollowed by an untypedemitof the same event, or a typedemitfrom a class body reaching a listener added throughany(the shape of function-local class expressions whose instances escape untyped), hitscr_trap("scriptc: computed event collided with a fixed event"), and an untypedemit('error', ...)trapped unconditionally.Root cause: a listener entry held either a typed adapter (called with the emit site's fixed tuple as C variadics) or a checked-dynamic thunk (called with the untyped argument vector), and the emit core trapped when an entry lacked the form the emit carried.
Runtime (
scr_events_emitter.c):scr_emitter_on_dynkeeps the listener's thunk beside its typed adapter, so typed emits call the adapter and untyped emits call the thunk with the arguments as given (extra arguments included).scr_emitter_emit_mixedcarries both the untyped arguments and a compiler-converted typed tuple, for untyped emits of an event that has listeners with typed parameters.scr_emitter_has_typedsays whether the conversion is needed.scr_emitter_on_any/scr_emitter_emit_anyserve registrations and emits through untyped values: runtime-built typed adapters undererrorand the meta events;emit('error')emits to errorMonitor first, then to listeners, and with none throws an Error payload itself and primitive payloads asERR_UNHANDLED_ERROR. UntypednewListener/removeListenerlisteners receive the listener function when it was registered through an untyped value.scr_emitter_on_anyinstalls, andscr_emitter_on_flex/emit_flexkeep their plain behavior for computed names and the worker registries, so programs without untyped emitter calls link none of the new dynamic code.Compiler:
on/once/prepend*under an event typed code emits or listens to with typed parameters throughemitter.onDynwith a typed adapter for the event's tuple, and untypedemitof such an event converts its arguments to the tuple (a mismatch throws the checked conversion'sTypeError) whenhasTypedsays a typed listener is registered. These helpers exist only in programs that have such events and untyped emitter calls.listenerCount,eventNames,removeAllListeners,listeners,rawListeners,setMaxListenersandgetMaxListenerscalled through an untyped value on a compiled emitter (a program class extending EventEmitter, ornew EventEmitter()) answered "is not a function"; they now dispatch to the native registry with Node's results. In TypeScript the last four names join the dyn-dispatch method set so such calls lower to a dispatch.Explicit refusals remain for what is not modeled, documented on the EventEmitter limitations page:
listeners()through an untyped value of an event with a typed listener, untypedemit('error')of a non-Error object with no listener, untyped emits whose arguments do not convert to a typed listener's tuple (TypeError before any listener runs), and typed listener functions as the second meta-event argument (undefined).Evidence:
Overlap: #1544 (
opt/ee-class-value-crash) also touches EventEmitter handling through untyped values (builtin-class-dyn.ts, relational.ts, lowerer.ts, lower-classes.ts); this change touches none of those files.Follow-up: EventEmitter methods read as values (merged origin/main c635475 first):
new EventEmitter()) through an untyped value answeredundefined(typeof o.listeners,const f = o.on; f.call(o, …),o.emit.bind(o)), andEventEmitter.prototype.onin JavaScript was a fence. Those reads, typed value reads (const f: unknown = emitter.on), dynamic keys,in, andEventEmitter.prototype.<method>now answer the runtime's EventEmitter.prototype functions (the ones constructor-function emitters already inherit), soo.on === o.addListener === EventEmitter.prototype.on, andname/lengthmatch Node.dyn.emitterMethod(scr_dyn_builtin_class.c, dynBuiltinClass feature): the first read installs a lifted program dispatch; the runtime prototype methods hand a typed-ref receiver to it (after the stream half), and it invokes the method through the existing untyped dispatch with the call's argument count. A detachedemitforwards up to 4 arguments after the event name and fences above that.emiton, or read ofemitfrom, a class that overridesemitnow throws the SC2020 refusal; before, the untyped emit bypassed the override for the native emit.%EventEmittercapsule keeps its own dispatch plan for values boxed as itself (before,new EventEmitter()boxed first left such subclasses without arms: untyped calls answered "is not a function").undefineddivergence and lists the new refusals and the stream gap (a stream'semitand introspection methods still read asundefinedthrough an untyped value).🤖 Generated with Claude Code