Found by the package audit (while fixing #10434) on Perry 7661bc0 (v0.5.1587→v0.5.1589 baseline), Linux x64.
A module-level function f compared by identity inside its defining module is not === to the f value an importer
receives, although two importers' views agree with each other (ns.f === f is true in the importer).
Reproduction
lib/fns.ts:
function f() { return 1; }
export function isSame(x: unknown) { return x === f; }
export { f };
main.ts:
import { f, isSame } from "./lib/fns.ts";
import * as ns from "./lib/fns.ts";
console.log(isSame(f), isSame(ns.f), ns.f === f);
node main.ts
PERRY_NO_AUTO_OPTIMIZE=1 perry compile main.ts -o main && ./main
Expected (Node 26.5.1)
Actual (Perry)
Impact
Identity checks against a module's own exported functions are common: registries and caches keyed by function
(map.get(handler)), "is this our default implementation" checks (if (opts.serialize === defaultSerialize)),
removeEventListener/off(fn) with an exported listener, memoization keyed by function, and plugin dedup. With
Perry these silently take the wrong branch or fail to remove listeners.
Notes
Found by the package audit (while fixing #10434) on Perry 7661bc0 (v0.5.1587→v0.5.1589 baseline), Linux x64.
A module-level function
fcompared by identity inside its defining module is not===to thefvalue an importerreceives, although two importers' views agree with each other (
ns.f === fis true in the importer).Reproduction
lib/fns.ts:main.ts:node main.ts PERRY_NO_AUTO_OPTIMIZE=1 perry compile main.ts -o main && ./mainExpected (Node 26.5.1)
Actual (Perry)
Impact
Identity checks against a module's own exported functions are common: registries and caches keyed by function
(
map.get(handler)), "is this our default implementation" checks (if (opts.serialize === defaultSerialize)),removeEventListener/off(fn)with an exported listener, memoization keyed by function, and plugin dedup. WithPerry these silently take the wrong branch or fail to remove listeners.
Notes
functions they reference (so the clone's
fis a different value). Not confirmed: in this repro the result isidentical with
PERRY_NO_INLINE=1set, which may not be the right knob — check which transform/lowering gives thein-module reference to
fa different value than the export slot (in-module direct reference vs exported wrapperclosure
__perry_wrap_*, or inlined clone).function F(){}; export default F;exports a different function object: importers lose F's prototype methods and statics, and missing arguments arrive as garbage instead of undefined #10434 (export default Fhanded importers a wrapper instead ofF), fixed by PR fix(hir): export default F exports the declared function's own binding #10548.