fix(javascript): track public CommonJS export receiver lifetimes (#3567)

* fix(javascript): track public CommonJS export receiver lifetimes

* fix(javascript): retain exports across uncertain identity effects
This commit is contained in:
鸡哥
2026-10-14 11:46:14 -07:00
committed by GitHub
parent 357996cc89
commit 75104040df
10 changed files with 502 additions and 71 deletions
@@ -393,7 +393,105 @@ describe("JavaScript semantic analysis: structure 2", () => {
]);
},
);
});
describe("CommonJS receiver lifetimes", () => {
it.each([
'Object.defineProperty(module.exports, "other", { value: exports });',
"Object.assign(module.exports, { other: exports });",
])(
"keeps the receiver identity intact when mutating its contents: %s",
(mutation) => {
const ir = analyzeJavaScriptSemantics(
`exports.parse = () => 1; ${mutation}`,
);
const exported = ir.moduleLinks.find(
({ exportedName }) => exportedName === "parse",
);
expect(exported?.callableId).toEqual(expect.any(String));
},
);
it.each([
"module.exports = {}; exports.parse = () => 1;",
"exports = {}; exports.parse = () => 1;",
"exports.parse = () => 1; module.exports = {};",
"module.exports.parse = () => 1; module.exports = [];",
'module.exports = {}; exports.status = "enabled";',
"module.exports = exports = {}; module.exports = {}; exports.parse = () => 1;",
'module.exports = {}; Reflect.set(module, "other", exports); exports.parse = () => 1;',
'module.exports = {}; Object.defineProperty(globalThis.module, "exports", { value: exports }); exports.parse = () => 1;',
"module.exports = {}; let wrapper = module; wrapper = {}; Object.assign(wrapper, { exports }); exports.parse = () => 1;",
])(
"excludes a detached CommonJS receiver across statements: %s",
(source) => {
const ir = analyzeJavaScriptSemantics(source);
expect(
ir.moduleLinks.filter(
({ exportedName }) =>
exportedName === "parse" || exportedName === "status",
),
).toEqual([]);
},
);
it.each([
"exports.parse = () => 1;",
"module.exports = {}; module.exports.parse = () => 1;",
"module.exports = {}; exports = module.exports; exports.parse = () => 1;",
"module.exports = exports = {}; exports.parse = () => 1;",
"exports = module.exports = {}; exports.parse = () => 1;",
"module.exports = module.exports; exports.parse = () => 1;",
"module.exports = external; exports = module.exports; exports.parse = () => 1;",
"module.exports = external; module.exports.parse = () => 1;",
"exports.parse = (exports = {} , () => 1);",
"exports.parse = (module.exports = {}); module.exports = exports;",
"(module.exports = {}, exports = module.exports, exports.parse = () => 1);",
])("retains a connected CommonJS receiver: %s", (source) => {
expect(analyzeJavaScriptSemantics(source).moduleLinks).toContainEqual(
expect.objectContaining({
kind: "commonjs-export",
exportedName: "parse",
}),
);
});
it.each([
"module.exports = external; exports.parse = () => 1;",
"if (flag) module.exports = {}; exports.parse = () => 1;",
"flag && (module.exports = {}); exports.parse = () => 1;",
"holder ||= (module.exports = {}); exports.parse = () => 1;",
"invoke?.(module.exports = {}); exports.parse = () => 1;",
"const { value = (module.exports = {}) } = source; exports.parse = () => 1;",
"module[key] = {}; exports.parse = () => 1;",
"exports.parse = () => 1; if (flag) module.exports = {};",
"function replace() { module.exports = {}; } exports.parse = () => 1; replace();",
"function replace() { exports = {}; } replace(); exports.parse = () => 1;",
"module.exports = {}; function install() { exports.parse = () => 1; } install();",
"for (exports of values) {} exports.parse = () => 1;",
"({ exports } = external); exports.parse = () => 1;",
"module = {}; module.exports = {}; module.exports.parse = () => 1;",
'const holder = {}; ({ [(module.exports = exports, "x")]: holder.x } = (module.exports = {})); exports.parse = () => 1;',
'const { [(module.exports = exports, "x")]: value } = (module.exports = {}); exports.parse = () => 1;',
"const [value = (module.exports = exports)] = (module.exports = []); exports.parse = () => 1;",
"module.exports = {}; Object.assign(module, { exports }); exports.parse = () => 1;",
'module.exports = {}; Object.defineProperty(module, "exports", { value: exports }); exports.parse = () => 1;',
'module.exports = {}; Reflect.set(module, "exports", exports); exports.parse = () => 1;',
"module.exports = {}; const assign = Object.assign; assign.call(null, module, { exports }); exports.parse = () => 1;",
"module.exports = {}; const wrapper = module; Object.assign(wrapper, { exports }); exports.parse = () => 1;",
])("keeps uncertain CommonJS receiver identity unresolved: %s", (source) => {
expect(analyzeJavaScriptSemantics(source).moduleLinks).toContainEqual(
expect.objectContaining({
kind: "commonjs-export",
exportedName: "parse",
callableId: null,
localName: null,
}),
);
});
});
describe("JavaScript semantic analysis: write references", () => {
it("classifies destructuring and loop targets as writes, not reads", () => {
const ir = analyzeJavaScriptSemantics(`
let a, b, c;