mirror of
https://github.com/morluto/rea
synced 2026-10-27 14:42:18 -04:00
fix(javascript): drop built-in effects for visibly replaced globals (#4565)
The semantic collectors admitted Node timers, fetch, WebSocket, JSON.parse and global coercions from the identifier spelling alone, so a source that replaced those globals (`setTimeout = () => null`) still produced resolved built-in effects. Require the existing isUnmodifiedGlobalReference guard, which the static analyzer already uses for fetch and WebSocket, before admitting these effects. The guard treats any visible possible write as a replacement without evaluating order or reachability, so computed global writes and guarded polyfills also remove the resolved effect. The workflow docs now say so. Fixes #3367
This commit is contained in:
@@ -218,3 +218,61 @@ describe("JavaScript semantic analysis: event dispatch data", () => {
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("JavaScript semantic analysis: replaced built-in globals", () => {
|
||||
it.each([
|
||||
["setTimeout", "setTimeout = local; setTimeout(work, 1);", "timer"],
|
||||
[
|
||||
"globalThis.setTimeout",
|
||||
"globalThis.setTimeout = local; setTimeout(work, 1);",
|
||||
"timer",
|
||||
],
|
||||
["fetch", "fetch = local; fetch('https://example.test');", "request"],
|
||||
[
|
||||
"WebSocket",
|
||||
"WebSocket = local; new WebSocket('wss://example.test');",
|
||||
"request",
|
||||
],
|
||||
// A replaced JSON.parse keeps only the generic, partial `.parse` candidate.
|
||||
["JSON.parse", "JSON.parse = local; JSON.parse('null');", "boundary"],
|
||||
["JSON", "JSON = { parse: local }; JSON.parse('null');", "boundary"],
|
||||
["String", "String = local; String(value);", "boundary"],
|
||||
] as const)(
|
||||
"does not admit built-in effects after a visible %s replacement",
|
||||
(_name, source, family) => {
|
||||
const ir = analyzeJavaScriptSemantics(source);
|
||||
const operations = {
|
||||
timer: ir.timerOperations,
|
||||
request: ir.requestOperations,
|
||||
boundary: ir.boundaryOperations,
|
||||
};
|
||||
expect(
|
||||
operations[family].filter(
|
||||
({ resolution }) => resolution === "complete",
|
||||
),
|
||||
).toEqual([]);
|
||||
},
|
||||
);
|
||||
|
||||
it("keeps built-in effects for unmodified globals beside unrelated writes", () => {
|
||||
const ir = analyzeJavaScriptSemantics(`
|
||||
JSON.stringify = local; other.setTimeout = local;
|
||||
setTimeout(work, 1);
|
||||
fetch('https://example.test');
|
||||
new WebSocket('wss://example.test');
|
||||
JSON.parse('null');
|
||||
String(value);
|
||||
`);
|
||||
expect(ir.timerOperations).toMatchObject([
|
||||
{ method: "setTimeout", resolution: "complete" },
|
||||
]);
|
||||
expect(ir.requestOperations.map(({ method }) => method)).toEqual([
|
||||
"fetch",
|
||||
"WebSocket",
|
||||
]);
|
||||
expect(ir.boundaryOperations).toMatchObject([
|
||||
{ method: "JSON.parse", resolution: "complete" },
|
||||
{ method: "String", resolution: "complete" },
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user