refactor(javascript): share semantic call-site lookup
This commit is contained in:
@@ -11,7 +11,10 @@ import {
|
||||
constructSemanticGraphNode,
|
||||
} from "./JavaScriptSemanticGraphConstruction.js";
|
||||
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
import {
|
||||
semanticCallSiteAt,
|
||||
type SemanticFlowProjectionContext,
|
||||
} from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
|
||||
/** Project literal EventEmitter registrations, removals, and dispatches. */
|
||||
export const projectSemanticEvents = (
|
||||
@@ -194,7 +197,7 @@ const projectTimerSchedule = (
|
||||
timerNodes: ReadonlyMap<string, JavaScriptSemanticGraphNode>,
|
||||
): void => {
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSiteAt(context, operation.location),
|
||||
source: semanticCallSiteAt(context, operation.location),
|
||||
target: timerNodes.get(operation.timerId),
|
||||
relation: "schedules-timer",
|
||||
resolution: "resolved",
|
||||
@@ -206,7 +209,7 @@ const projectTimerCancellation = (
|
||||
operation: JavaScriptSemanticTimerOperation,
|
||||
timerNodes: ReadonlyMap<string, JavaScriptSemanticGraphNode>,
|
||||
): void => {
|
||||
const callSite = callSiteAt(context, operation.location);
|
||||
const callSite = semanticCallSiteAt(context, operation.location);
|
||||
const target =
|
||||
operation.linkedTimerId === null
|
||||
? undefined
|
||||
@@ -231,26 +234,3 @@ const projectTimerCancellation = (
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const callSiteAt = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
location: JavaScriptTimerLocation,
|
||||
): JavaScriptSemanticGraphNode | undefined => {
|
||||
const call = context.ir.callSites.find(({ location: candidate }) =>
|
||||
rangesEqual(candidate, location),
|
||||
);
|
||||
return call === undefined
|
||||
? undefined
|
||||
: context.callSiteNodes.get(call.callSiteId);
|
||||
};
|
||||
|
||||
type JavaScriptTimerLocation = JavaScriptSemanticTimerOperation["location"];
|
||||
|
||||
const rangesEqual = (
|
||||
left: JavaScriptTimerLocation,
|
||||
right: JavaScriptTimerLocation,
|
||||
): boolean =>
|
||||
left.start.line === right.start.line &&
|
||||
left.start.column === right.start.column &&
|
||||
left.end.line === right.end.line &&
|
||||
left.end.column === right.end.column;
|
||||
|
||||
@@ -11,7 +11,10 @@ import {
|
||||
constructSemanticGraphNode,
|
||||
} from "./JavaScriptSemanticGraphConstruction.js";
|
||||
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
import {
|
||||
semanticCallSiteAt,
|
||||
type SemanticFlowProjectionContext,
|
||||
} from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
|
||||
interface ChildUnknownInput {
|
||||
readonly context: SemanticFlowProjectionContext;
|
||||
@@ -81,7 +84,7 @@ const projectSpawn = (
|
||||
spawn: JavaScriptSemanticChildProcessSpawn,
|
||||
child: JavaScriptSemanticGraphNode,
|
||||
): void => {
|
||||
const callSite = callSiteAt(context, spawn.location);
|
||||
const callSite = semanticCallSiteAt(context, spawn.location);
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSite,
|
||||
target: child,
|
||||
@@ -252,24 +255,3 @@ const addChildUnknown = (input: ChildUnknownInput): void => {
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const callSiteAt = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
location: JavaScriptSemanticChildProcessSpawn["location"],
|
||||
): JavaScriptSemanticGraphNode | undefined => {
|
||||
const call = context.ir.callSites.find(({ location: candidate }) =>
|
||||
rangesEqual(candidate, location),
|
||||
);
|
||||
return call === undefined
|
||||
? undefined
|
||||
: context.callSiteNodes.get(call.callSiteId);
|
||||
};
|
||||
|
||||
const rangesEqual = (
|
||||
left: JavaScriptSemanticChildProcessSpawn["location"],
|
||||
right: JavaScriptSemanticChildProcessSpawn["location"],
|
||||
): boolean =>
|
||||
left.start.line === right.start.line &&
|
||||
left.start.column === right.start.column &&
|
||||
left.end.line === right.end.line &&
|
||||
left.end.column === right.end.column;
|
||||
|
||||
@@ -12,7 +12,10 @@ import {
|
||||
constructSemanticGraphNode,
|
||||
} from "./JavaScriptSemanticGraphConstruction.js";
|
||||
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
import {
|
||||
semanticCallSiteAt,
|
||||
type SemanticFlowProjectionContext,
|
||||
} from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
|
||||
/** Project environment, argv, file, default, and precedence candidates. */
|
||||
export const projectSemanticConfiguration = (
|
||||
@@ -170,7 +173,7 @@ const projectRequestConstruction = (
|
||||
operation: JavaScriptSemanticRequestOperation,
|
||||
request: JavaScriptSemanticGraphNode,
|
||||
): void => {
|
||||
const callSite = callSiteAt(context, operation.location);
|
||||
const callSite = semanticCallSiteAt(context, operation.location);
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSite,
|
||||
target: request,
|
||||
@@ -197,7 +200,7 @@ const projectResponseConsumer = (
|
||||
): void => {
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: response,
|
||||
target: callSiteAt(context, operation.location),
|
||||
target: semanticCallSiteAt(context, operation.location),
|
||||
relation: "consumed-by",
|
||||
resolution: operation.resolution === "complete" ? "resolved" : "candidate",
|
||||
properties: { method: operation.method },
|
||||
@@ -328,24 +331,3 @@ const addDataUnknown = (input: DataUnknownInput): void => {
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const callSiteAt = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
location: JavaScriptSemanticBoundaryOperation["location"],
|
||||
): JavaScriptSemanticGraphNode | undefined => {
|
||||
const call = context.ir.callSites.find(({ location: candidate }) =>
|
||||
rangesEqual(candidate, location),
|
||||
);
|
||||
return call === undefined
|
||||
? undefined
|
||||
: context.callSiteNodes.get(call.callSiteId);
|
||||
};
|
||||
|
||||
const rangesEqual = (
|
||||
left: JavaScriptSemanticBoundaryOperation["location"],
|
||||
right: JavaScriptSemanticBoundaryOperation["location"],
|
||||
): boolean =>
|
||||
left.start.line === right.start.line &&
|
||||
left.start.column === right.start.column &&
|
||||
left.end.line === right.end.line &&
|
||||
left.end.column === right.end.column;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { createJavaScriptSemanticGraphUnknown } from "../domain/javascriptSemanticGraph.js";
|
||||
import type { JavaScriptSemanticGraphNode } from "../domain/javascriptSemanticGraph.js";
|
||||
import type { JavaScriptSemanticIr } from "../domain/javascriptSemanticIr.js";
|
||||
import type { JavaScriptSourceRange } from "../domain/javascriptStaticAnalysisTypes.js";
|
||||
import type { JavaScriptArtifactAnalysis } from "./JavaScriptArtifactAnalysisTypes.js";
|
||||
import type { JavaScriptArtifactFile } from "./JavaScriptArtifactFiles.js";
|
||||
import {
|
||||
@@ -14,7 +15,10 @@ import {
|
||||
inferredSemanticEvidenceAt,
|
||||
unknownSemanticEvidence,
|
||||
} from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import { semanticNodesWithinRange } from "./JavaScriptSemanticGraphProjection.js";
|
||||
import {
|
||||
semanticNodesWithinRange,
|
||||
semanticRangesEqual,
|
||||
} from "./JavaScriptSemanticGraphProjection.js";
|
||||
|
||||
/** File-local graph state needed by return, capture, and frontier projection. */
|
||||
export interface SemanticFlowProjectionContext {
|
||||
@@ -29,6 +33,19 @@ export interface SemanticFlowProjectionContext {
|
||||
readonly referenceNodes: readonly JavaScriptSemanticGraphNode[];
|
||||
}
|
||||
|
||||
/** Find the retained call-site node at one exact semantic source range. */
|
||||
export const semanticCallSiteAt = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
location: JavaScriptSourceRange,
|
||||
): JavaScriptSemanticGraphNode | undefined => {
|
||||
const call = context.ir.callSites.find(({ location: candidate }) =>
|
||||
semanticRangesEqual(candidate, location),
|
||||
);
|
||||
return call === undefined
|
||||
? undefined
|
||||
: context.callSiteNodes.get(call.callSiteId);
|
||||
};
|
||||
|
||||
/** Project explicit static Promise/task ownership without runtime claims. */
|
||||
export const projectSemanticPromises = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
@@ -89,14 +106,8 @@ const projectPromiseCreation = (
|
||||
promise: JavaScriptSemanticGraphNode,
|
||||
): void => {
|
||||
if (operation.kind === "awaited-expression") return;
|
||||
const call = context.ir.callSites.find(({ location }) =>
|
||||
rangesEqual(location, operation.location),
|
||||
);
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source:
|
||||
call === undefined
|
||||
? undefined
|
||||
: context.callSiteNodes.get(call.callSiteId),
|
||||
source: semanticCallSiteAt(context, operation.location),
|
||||
target: promise,
|
||||
relation: "creates-promise",
|
||||
resolution:
|
||||
@@ -220,7 +231,7 @@ export const projectSemanticReturnValues = (
|
||||
relation: "aliases",
|
||||
resolution:
|
||||
site.identityReferenceLocation !== null &&
|
||||
rangesEqual(
|
||||
semanticRangesEqual(
|
||||
reference.identity.source_range,
|
||||
site.identityReferenceLocation,
|
||||
)
|
||||
@@ -230,17 +241,6 @@ export const projectSemanticReturnValues = (
|
||||
}
|
||||
};
|
||||
|
||||
const rangesEqual = (
|
||||
left: JavaScriptSemanticGraphNode["identity"]["source_range"],
|
||||
right: JavaScriptSemanticGraphNode["identity"]["source_range"],
|
||||
): boolean =>
|
||||
left !== null &&
|
||||
right !== null &&
|
||||
left.start.line === right.start.line &&
|
||||
left.start.column === right.start.column &&
|
||||
left.end.line === right.end.line &&
|
||||
left.end.column === right.end.column;
|
||||
|
||||
/** Link each captured binding to the callable at the exact reference site. */
|
||||
export const projectSemanticClosureCaptures = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
|
||||
@@ -8,7 +8,10 @@ import {
|
||||
constructSemanticGraphNode,
|
||||
} from "./JavaScriptSemanticGraphConstruction.js";
|
||||
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
import {
|
||||
semanticCallSiteAt,
|
||||
type SemanticFlowProjectionContext,
|
||||
} from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
|
||||
/** Project built-in resource acquisition and exact local release handles. */
|
||||
export const projectSemanticResources = (
|
||||
@@ -24,7 +27,7 @@ export const projectSemanticResources = (
|
||||
for (const operation of context.ir.resourceOperations)
|
||||
if (operation.kind === "acquire")
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSiteAt(context, operation.location),
|
||||
source: semanticCallSiteAt(context, operation.location),
|
||||
target: resourceNodes.get(operation.resourceId),
|
||||
relation: "acquires",
|
||||
resolution: "resolved",
|
||||
@@ -61,7 +64,7 @@ const projectRelease = (
|
||||
operation: JavaScriptSemanticResourceOperation,
|
||||
resourceNodes: ReadonlyMap<string, JavaScriptSemanticGraphNode>,
|
||||
): void => {
|
||||
const callSite = callSiteAt(context, operation.location);
|
||||
const callSite = semanticCallSiteAt(context, operation.location);
|
||||
const resources = operation.linkedResourceIds.flatMap((identifier) => {
|
||||
const resource = resourceNodes.get(identifier);
|
||||
return resource === undefined ? [] : [resource];
|
||||
@@ -90,24 +93,3 @@ const projectRelease = (
|
||||
}),
|
||||
);
|
||||
};
|
||||
|
||||
const callSiteAt = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
location: JavaScriptSemanticResourceOperation["location"],
|
||||
): JavaScriptSemanticGraphNode | undefined => {
|
||||
const call = context.ir.callSites.find(({ location: candidate }) =>
|
||||
rangesEqual(candidate, location),
|
||||
);
|
||||
return call === undefined
|
||||
? undefined
|
||||
: context.callSiteNodes.get(call.callSiteId);
|
||||
};
|
||||
|
||||
const rangesEqual = (
|
||||
left: JavaScriptSemanticResourceOperation["location"],
|
||||
right: JavaScriptSemanticResourceOperation["location"],
|
||||
): boolean =>
|
||||
left.start.line === right.start.line &&
|
||||
left.start.column === right.start.column &&
|
||||
left.end.line === right.end.line &&
|
||||
left.end.column === right.end.column;
|
||||
|
||||
Reference in New Issue
Block a user