refactor(javascript): break the semantic analysis import cycle (#8063)

* refactor(javascript): break the semantic analysis import cycle

Split shared semantic facts out of the analysis hub so projectors of
effects, evaluation, and identity no longer import one another in a cycle.

* refactor(javascript): share the import specifier reader

Read static imports in one module used by the boundary checker and both
cycle guards, so those rules cannot drift apart.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
This commit is contained in:
morlutoandCursor Agent authored and GitHub committed 2026-10-30 06:07:49 +08:00
1 parent 940d71a45a
commit 249b90c5e3
33 files changed
+1043 -772

No files matched your search

@@ -10,16 +10,15 @@ import {
} from "./javascriptElectronMemberWrites.js";
import { createSemanticModuleInterop } from "./javascriptModuleInterop.js";
import {
isUnshadowedGlobal,
resolveSemanticBindingState,
semanticStableInitializer,
type JavaScriptSemanticAnalysisState,
} from "./javascriptSemanticState.js";
import { isImportMetaUrl } from "./javascriptStaticAnalysisHelpers.js";
import {
isUnshadowedGlobal,
semanticBindingAtRead,
semanticEffectTiming,
semanticDefiniteEffectPrecedesReference,
semanticEffectTiming,
} from "./javascriptSemanticMutationInitializers.js";
import {
semanticIterationSources,
@@ -30,20 +29,24 @@ import {
semanticPropertyPathKeyMatches,
} from "./javascriptSemanticPropertyPaths.js";
import { createSemanticReturnedReferences } from "./javascriptSemanticReturnedReferences.js";
import { semanticRequireLoaderAt } from "./javascriptSemanticRequireOrigin.js";
import {
GLOBAL_OBJECT_NAMES,
globalObjectName,
globalReferenceNamespace,
isUnmodifiedGlobalReference,
isUnmodifiedImportMetaUrl,
} from "./javascriptSemanticGlobalReference.js";
import { readSemanticRequireLoader } from "./javascriptSemanticRequireOriginSlot.js";
import {
semanticCopiedReferencePath,
semanticReferenceIterationUnknown,
semanticReferenceSourcesInactive,
} from "./javascriptSemanticReferenceSources.js";
const GLOBAL_OBJECT_NAMES = ["globalThis", "global", "window", "self"];
const globalObjectName = (
node: t.Node,
state: JavaScriptSemanticAnalysisState,
): string | undefined =>
GLOBAL_OBJECT_NAMES.find((name) => isUnshadowedGlobal(node, state, name));
export {
isUnmodifiedGlobalReference,
isUnmodifiedImportMetaUrl,
} from "./javascriptSemanticGlobalReference.js";
/** Global self references retain object identity, rather than adding a property. */
const canonicalAmbientPath = (
@@ -801,7 +804,7 @@ export function* indexJavaScriptAmbientWritesSteps(
(t.isCallExpression(node) || t.isOptionalCallExpression(node)) &&
!state.ambientWrites.covers(["global", "require", "resolve"]) &&
!state.ambientEscapes.covers(["global", "require", "resolve"]) &&
semanticRequireLoaderAt(node, state, new Set()) === "commonjs-loader"
readSemanticRequireLoader(node, state, new Set()) === "commonjs-loader"
)
continue;
retainEscapeEffect(effect);
@@ -835,42 +838,6 @@ export function* indexJavaScriptAmbientWritesSteps(
state.ambientEscapes.retain(["global", null]);
}
/** Match lexical global identity; null denotes a direct global root. */
const globalReferenceNamespace = (
callee: t.Node,
state: JavaScriptSemanticAnalysisState,
path: readonly [string, ...string[]],
): string | null | undefined => {
const { root, members } = electronMemberPath(callee);
const matches = (expected: readonly string[]) =>
members.length === expected.length &&
members.every((key, index) => key === expected[index]);
if (isUnshadowedGlobal(root, state, path[0]) && matches(path.slice(1)))
return null;
const namespace = globalObjectName(root, state);
return namespace !== undefined && matches(path) ? namespace : undefined;
};
/**
* Recognize an ambient property path, optionally through a global object, without a visible
* source-local overwrite. This is a syntax-local identity fact, not proof that
* the runtime's globals have never been patched by other code.
*/
export const isUnmodifiedGlobalReference = (
callee: t.Node,
state: JavaScriptSemanticAnalysisState,
name: string | readonly [string, ...string[]],
): boolean => {
const path: readonly [string, ...string[]] =
typeof name === "string" ? [name] : name;
if (state.ambientWrites.covers(["global", ...path])) return false;
const namespace = globalReferenceNamespace(callee, state, path);
return (
namespace !== undefined &&
(namespace === null || !state.ambientWrites.covers(["global", namespace]))
);
};
export type GlobalReferenceRead =
| { readonly status: "unmodified" | "unrelated" | "unknown" }
| { readonly status: "replacement"; readonly value: t.Expression };
@@ -975,10 +942,3 @@ export const createGlobalReferenceReader = (
return { status: "replacement", value: confirmed.right };
};
};
/** A module URL base is known only while its property has no visible write. */
export const isUnmodifiedImportMetaUrl = (
node: t.Node | undefined,
state: JavaScriptSemanticAnalysisState,
): boolean =>
isImportMetaUrl(node) && !state.ambientWrites.covers(["import-meta", "url"]);