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:
amyanger
2026-10-22 19:28:53 +01:00
committed by GitHub
parent de00fdc17d
commit 7436ac8e20
5 changed files with 150 additions and 6 deletions
@@ -16,6 +16,7 @@ import {
} from "./javascriptSemanticState.js";
import { semanticBindingAtRead } from "./javascriptSemanticMutationInitializers.js";
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
import { isUnmodifiedGlobalReference } from "./javascriptSemanticAmbientWrites.js";
import { range } from "./javascriptStaticAnalysisHelpers.js";
import {
builtinDataEffectMethod,
@@ -333,7 +334,8 @@ const timerMethod = (
if (binding === undefined) {
const method = admittedTimerMethod(callee.name);
if (method === null) return null;
return semanticResolutionBlocked(state, callee, callee.name)
return semanticResolutionBlocked(state, callee, callee.name) ||
!isUnmodifiedGlobalReference(callee, state, callee.name)
? null
: method;
}