Repository navigation
node:events: fix ReferenceError in EventEmitter.prototype.listenerCount - #34667
Conversation
efab3b2 removed the listenerCountSlow helper but left a call to it in EventEmitterPrototype.listenerCount, so any emitter.listenerCount(type) call (without the second listener argument) threw ReferenceError. Inline the lookup into the prototype method, structured like Node's own implementation: read _events[type] once, then handle the bare-function and array storage shapes for both the 1-arg and 2-arg forms.
|
Reproduced with |
Walkthrough
ChangesListener count behavior
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@test/js/node/events/event-emitter.test.ts`:
- Around line 919-944: Extend the EventEmitter.prototype.listenerCount test
immediately after the first ee.on("x", a) call with null and undefined
listener-argument assertions expecting a count of 1, covering count-all behavior
when a single function is stored. Keep the existing array-storage assertions
unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: b87db90b-121d-4cda-8cf7-00483452ed18
📒 Files selected for processing (2)
src/js/node/events.tstest/js/node/events/event-emitter.test.ts
There was a problem hiding this comment.
LGTM — straightforward regression fix for a dangling reference to a deleted helper.
What was reviewed:
- New
listenerCountbody matches Node's reference implementation structure (bare-function vs array storage,method != nullgating both branches). handlers === undefinedcheck covers the shape-mode case where removed listeners are stored asundefinedrather than deleted.- Test covers all storage shapes (empty / single function / array), the
.listenerunwrap foronce()wrappers, and null/undefined method arg — verified expectations against Node.
Extended reasoning...
Overview
EventEmitterPrototype.listenerCount was calling listenerCountSlow, a helper deleted in #34605, so every 1-arg emitter.listenerCount(type) call threw a ReferenceError on main. This PR inlines the lookup: read this._events?.[type] once, then branch on function-vs-array storage shape for both the 1-arg (count all) and 2-arg (count matching method) forms. A new test in event-emitter.test.ts covers all combinations.
Security risks
None. Pure JS, no untrusted input parsing, no privilege boundaries.
Level of scrutiny
Low-to-medium. This is a hot-path built-in module, but the change is a small, mechanical inline of previously-working logic that mirrors Node's own listenerCount almost line-for-line. The old code's falsy check (!handlers) became a strict === undefined check, which matches Node and correctly handles the kShapeMode path where cleared slots hold undefined. Handlers that aren't undefined or a function are always arrays (per _addListener), so handlers.length is safe.
Other factors
The regression was already breaking 38 tests in node-http2.test.js on main, so this is a build-fixer. The new test would have failed on main with the exact ReferenceError, and the PR reports all 26 test-event-emitter-*.js parallel tests pass. No prior reviewer comments to address.
Repro
Every single-argument
emitter.listenerCount(type)call hits this, which broketest/js/node/http2/node-http2.test.js(38 fail) viaemitSessionCloseNTcallinglistenerCount("close"), along with anything else that counts listeners.Cause
#34605 (efab3b2) removed the
listenerCountSlowhelper when it dropped the fallback path from the staticevents.listenerCount, butEventEmitterPrototype.listenerCount(added in a227ad9) still called it for the common 1-argument form.Fix
Inline the lookup into the prototype method, structured the same way as Node's own
listenerCount: read_events[type]once, then handle the bare-function and array storage shapes for both the 1-arg (return 1/handlers.length) and 2-arg (match againstmethod) forms.Verification
bun bd test test/js/node/events/event-emitter.test.ts: 70 passEventEmitter.prototype.listenerCounttest fails on main withReferenceError: listenerCountSlow is not defined, passes with the fixtest/js/node/test/parallel/test-event-emitter-*.jspass