fix(javascript): bound semantic expansion and resolve lexical receivers

This commit is contained in:
morluto
2026-10-09 00:20:48 +08:00
parent eaf5734083
commit 16f4609d68
26 changed files with 868 additions and 78 deletions
@@ -27,6 +27,10 @@ import {
type JavaScriptAnalysisAccumulator as AnalysisAccumulator,
} from "./javascriptStaticAnalysisState.js";
import type { JavaScriptStaticAnalysis } from "./javascriptStaticAnalysisTypes.js";
import {
classifyParsedJavaScriptOpenReceivers,
type JavaScriptOpenReceiverFact,
} from "./javascriptSemanticAnalysis.js";
import {
parseJavaScriptSource,
type ParsedJavaScriptSource,
@@ -39,16 +43,21 @@ export const analyzeJavaScriptStaticSource = (
const file = parseJavaScriptSource(source);
return file === null
? failedJavaScriptStaticAnalysis()
: analyzeParsedJavaScriptStaticSource(source, file);
: analyzeParsedJavaScriptStaticSource(
source,
file,
classifyParsedJavaScriptOpenReceivers(file),
);
};
/** Recover static structure from an already parsed JavaScript artifact. */
export const analyzeParsedJavaScriptStaticSource = (
source: string,
file: ParsedJavaScriptSource,
openReceiverFacts?: ReadonlyMap<number, JavaScriptOpenReceiverFact>,
): JavaScriptStaticAnalysis => {
const accumulator = createJavaScriptAnalysisAccumulator();
traverseStaticSource(source, file, accumulator);
traverseStaticSource(source, file, accumulator, openReceiverFacts);
addSourceMapDirectives(source, file.comments ?? [], accumulator);
return finalizeStaticAnalysis(source, file, accumulator);
};
@@ -57,11 +66,12 @@ const traverseStaticSource = (
source: string,
file: ParsedJavaScriptSource,
accumulator: AnalysisAccumulator,
openReceiverFacts?: ReadonlyMap<number, JavaScriptOpenReceiverFact>,
): void => {
traverseJavaScriptAst(file, {
enter: (node) => {
accumulator.visitedNodes += 1;
inspectNode(source, node, accumulator);
inspectNode(source, node, accumulator, openReceiverFacts);
return undefined;
},
});
@@ -140,16 +150,20 @@ const inspectNode = (
source: string,
node: t.Node,
accumulator: AnalysisAccumulator,
openReceiverFacts?: ReadonlyMap<number, JavaScriptOpenReceiverFact>,
): void => {
const findings = {
source,
accumulator,
...(openReceiverFacts === undefined ? {} : { openReceiverFacts }),
};
inspectElectronStaticNode(node, findings);
if (t.isCallExpression(node)) {
inspectBundlerRegistration(source, node, accumulator);
inspectEsbuildWrapper(source, node, accumulator);
inspectCall(source, node, findings);
} else if (t.isOptionalCallExpression(node)) {
inspectCall(source, node, findings);
} else if (t.isNewExpression(node)) inspectCall(source, node, findings);
if (
(t.isImportDeclaration(node) || t.isExportAllDeclaration(node)) &&