Skip to content

EventEmitter listeners: mutable captures don't receive numeric/object payload arguments (NaN/undefined) #1274

Description

@proggeramlug

Summary

EventEmitter listeners that capture an outer-scope mutable variable and write the listener's argument into it (a normal closure pattern) write `NaN` (for numeric args) or `undefined` (for object property reads) instead of the actual argument. Booleans assigned from the closure are unaffected.

Repro 1 — `test_express_mount`

```ts
const ee = new EventEmitter();
let hitCount = 0;
ee.on("hit", (n: number) => { hitCount = n; });
ee.emit("hit", 7);
console.log("hitCount:", hitCount);
// Node: hitCount: 7
// Perry: hitCount: NaN

let mounted = false;
let parentName = "";
child.on("mount", function (this: any, parent: { name: string }) {
mounted = true; // <-- boolean: works
parentName = parent.name; // <-- property read: undefined
});
child.emit("mount", { name: "parent-app" });
// Node: parentName: parent-app
// Perry: parentName: undefined
```

Repro 2 — `test_issue_850_eventemitter`

```ts
const onceVals: number[] = [];
em.once("solo", (n: number) => { onceVals.push(n); });
em.emit("solo", 7);
// Node: once values: [ 7 ]
// Perry: once values: [ NaN ]

const addSeen: number[] = [];
const handler = (n: number) => { addSeen.push(n); };
em.addListener("alias", handler);
em.emit("alias", 1);
// Node: addListener/removeListener: [ 1 ]
// Perry: addListener/removeListener: [ NaN ]
```

Pattern

Listener body Result
`x = true` ✅ works
`x = numericArg` ❌ writes NaN
`x = objArg.field` ❌ writes undefined
`arr.push(numericArg)` ❌ pushes NaN

The bool case writing successfully suggests it's not a closure-capture-back-into-outer-scope bug per se — the closure is invoked and does write to the captured slot. The numeric argument arriving as NaN points at the EventEmitter→listener argument-passing path, which is dropping or mis-tagging the f64 payload on dispatch.

Suspected root cause

The EventEmitter shim (`__perry_ee_init`-style lazy init) was rewritten in #986. Likely the listener-call dispatch in the shim doesn't NaN-box numeric args correctly when forwarding from `emit(name, ...args)` to the stored callback.

Surfaced by

CI parity gate on tag `v0.5.1019` (run 26236443275). Both `test_express_mount` and `test_issue_850_eventemitter` fail with this same shape.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions