Timer, request, coercion and JSON.parse effects recognized unbound globals without consulting visible source-local writes, so `setTimeout = f` or `self.fetch = f` still produced Node timer and fetch operations. The ambient write owner now classifies a global reference as unmodified, possibly replaced (a write under an unknown key, such as `globalThis[k] = v`), or replaced (an exact visible write). Replaced globals no longer produce built-in effects; possibly replaced ones keep the effect with partial resolution instead of dropping it. The member-specific `isUnmodifiedGlobalMember` helper is superseded and removed. Fixes #3367 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
58 lines
1.5 KiB
TypeScript
58 lines
1.5 KiB
TypeScript
import { expect, it } from "vitest";
|
|
|
|
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
|
|
|
|
const effects = (source: string) => {
|
|
const ir = analyzeJavaScriptSemantics(source);
|
|
return {
|
|
timers: ir.timerOperations.map(
|
|
({ method, resolution }) => `${method}:${resolution}`,
|
|
),
|
|
requests: ir.requestOperations
|
|
.filter(({ kind }) => kind === "request")
|
|
.map(({ method, resolution }) => `${method}:${resolution}`),
|
|
parses: ir.boundaryOperations
|
|
.filter(({ kind }) => kind === "parse")
|
|
.map(({ method, resolution }) => `${method}:${resolution}`),
|
|
};
|
|
};
|
|
|
|
const calls = `
|
|
function work() {}
|
|
setTimeout(work, 100);
|
|
fetch("https://example.invalid");
|
|
JSON.parse("null");
|
|
`;
|
|
|
|
it("does not classify calls to replaced globals as built-in effects", () => {
|
|
expect(
|
|
effects(`
|
|
setTimeout = () => null;
|
|
fetch = () => null;
|
|
JSON.parse = () => null;
|
|
${calls}
|
|
`),
|
|
).toEqual({ timers: [], requests: [], parses: ["parse:partial"] });
|
|
});
|
|
|
|
it("keeps built-in effects partial after a write under an unknown key", () => {
|
|
expect(
|
|
effects(`
|
|
globalThis[Symbol.for("registry")] = new Map();
|
|
${calls}
|
|
`),
|
|
).toEqual({
|
|
timers: ["setTimeout:partial"],
|
|
requests: ["fetch:partial"],
|
|
parses: ["JSON.parse:partial"],
|
|
});
|
|
});
|
|
|
|
it("keeps built-in effects for unmodified globals", () => {
|
|
expect(effects(calls)).toEqual({
|
|
timers: ["setTimeout:complete"],
|
|
requests: ["fetch:complete"],
|
|
parses: ["JSON.parse:complete"],
|
|
});
|
|
});
|