From dbcec4cf56e762748bfe6175b156bb6edd3b8c7d Mon Sep 17 00:00:00 2001 From: morluto <76467478+morluto@users.noreply.github.com> Date: Thu, 22 Oct 2026 10:33:18 +0800 Subject: [PATCH] refactor(javascript): share result binding projection and facts --- .../JavaScriptSemanticGraphAsyncProjection.ts | 22 +++++++--------- ...riptSemanticGraphChildProcessProjection.ts | 18 ++++--------- .../JavaScriptSemanticGraphDataProjection.ts | 22 +++++++--------- .../JavaScriptSemanticGraphFlowProjection.ts | 25 ++++++++++++++++++- .../javascriptSemanticAsyncEffects.ts | 7 ++---- .../javascriptSemanticChildProcesses.ts | 7 ++---- .../javascriptSemanticDataEffectHelpers.ts | 12 ++++----- 7 files changed, 57 insertions(+), 56 deletions(-) diff --git a/src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts b/src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts index fa2053d833..6c8ac206d0 100644 --- a/src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts +++ b/src/application/javascript/JavaScriptSemanticGraphAsyncProjection.ts @@ -10,7 +10,10 @@ import { addSemanticGraphUnknown, } from "./JavaScriptSemanticGraphConstruction.js"; import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js"; -import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js"; +import { + projectSemanticResultBindings, + type SemanticFlowProjectionContext, +} from "./JavaScriptSemanticGraphFlowProjection.js"; /** Project literal EventEmitter registrations, removals, and dispatches. */ export const projectSemanticEvents = ( @@ -180,18 +183,11 @@ const projectTimerSchedule = ( relation: "schedules-timer", resolution: "resolved", }); - for (const projected of operation.projectedResultBindings) - addSemanticGraphRelation(context.state, { - source: timerNodes.get(operation.timerId), - target: context.bindingNodes.get(projected.bindingId), - relation: "destructures", - resolution: - projected.resolution === "complete" ? "resolved" : "candidate", - properties: { - projection_path: [...projected.projectionPath], - projection_resolution: projected.resolution, - }, - }); + projectSemanticResultBindings( + context, + timerNodes.get(operation.timerId), + operation.projectedResultBindings, + ); }; const projectTimerCancellation = ( diff --git a/src/application/javascript/JavaScriptSemanticGraphChildProcessProjection.ts b/src/application/javascript/JavaScriptSemanticGraphChildProcessProjection.ts index df3f932ff4..de01c7af3d 100644 --- a/src/application/javascript/JavaScriptSemanticGraphChildProcessProjection.ts +++ b/src/application/javascript/JavaScriptSemanticGraphChildProcessProjection.ts @@ -9,7 +9,10 @@ import { addSemanticGraphUnknown, } from "./JavaScriptSemanticGraphConstruction.js"; import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js"; -import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js"; +import { + projectSemanticResultBindings, + type SemanticFlowProjectionContext, +} from "./JavaScriptSemanticGraphFlowProjection.js"; interface ChildUnknownInput { readonly context: SemanticFlowProjectionContext; @@ -80,18 +83,7 @@ const projectSpawn = ( relation: "spawns", resolution: spawn.resolution === "complete" ? "resolved" : "candidate", }); - for (const projected of spawn.projectedResultBindings) - addSemanticGraphRelation(context.state, { - source: child, - target: context.bindingNodes.get(projected.bindingId), - relation: "destructures", - resolution: - projected.resolution === "complete" ? "resolved" : "candidate", - properties: { - projection_path: [...projected.projectionPath], - projection_resolution: projected.resolution, - }, - }); + projectSemanticResultBindings(context, child, spawn.projectedResultBindings); if (spawn.argvCount !== null) addSemanticGraphRelation(context.state, { source: callSite, diff --git a/src/application/javascript/JavaScriptSemanticGraphDataProjection.ts b/src/application/javascript/JavaScriptSemanticGraphDataProjection.ts index b1862168f9..c5b7e9abb0 100644 --- a/src/application/javascript/JavaScriptSemanticGraphDataProjection.ts +++ b/src/application/javascript/JavaScriptSemanticGraphDataProjection.ts @@ -10,7 +10,10 @@ import { addSemanticGraphUnknown, } from "./JavaScriptSemanticGraphConstruction.js"; import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js"; -import type { SemanticFlowProjectionContext } from "./JavaScriptSemanticGraphFlowProjection.js"; +import { + projectSemanticResultBindings, + type SemanticFlowProjectionContext, +} from "./JavaScriptSemanticGraphFlowProjection.js"; import { semanticResourceLimitReason } from "../../domain/javascript/javascriptSemanticResourceLimits.js"; /** Project environment, argv, file, default, and precedence candidates. */ @@ -179,18 +182,11 @@ const projectRequestConstruction = ( relation: "constructs-request", resolution: operation.resolution === "complete" ? "resolved" : "candidate", }); - for (const projected of operation.projectedResultBindings) - addSemanticGraphRelation(context.state, { - source: request, - target: context.bindingNodes.get(projected.bindingId), - relation: "destructures", - resolution: - projected.resolution === "complete" ? "resolved" : "candidate", - properties: { - projection_path: [...projected.projectionPath], - projection_resolution: projected.resolution, - }, - }); + projectSemanticResultBindings( + context, + request, + operation.projectedResultBindings, + ); for (const field of operation.fields) addSemanticGraphRelation(context.state, { source: diff --git a/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts b/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts index 59ab8b52de..6b2acc8de6 100644 --- a/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts +++ b/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts @@ -1,6 +1,9 @@ import type { JavaScriptSemanticGraphNode } from "../../domain/javascript/javascriptSemanticGraphSchemas.js"; import { javaScriptSemanticNodeId } from "../../domain/javascript/javascriptSemanticGraph.js"; -import type { JavaScriptSemanticIr } from "../../domain/javascript/javascriptSemanticIr.js"; +import type { + JavaScriptSemanticIr, + JavaScriptSemanticProjectedResultBinding, +} from "../../domain/javascript/javascriptSemanticIr.js"; import { sourceRangesEqual } from "../../domain/javascript/javascriptStaticAnalysisHelpers.js"; import type { JavaScriptSourceRange } from "../../domain/javascript/javascriptStaticAnalysisTypes.js"; import type { JavaScriptArtifactFile } from "../../domain/javascript/javascriptArtifactFiles.js"; @@ -34,6 +37,26 @@ export interface SemanticFlowProjectionContext { ) => JavaScriptSemanticGraphNode | undefined; } +/** Project result destructuring consistently for timers, child processes, and requests. */ +export const projectSemanticResultBindings = ( + context: Pick, + source: JavaScriptSemanticGraphNode | undefined, + bindings: readonly JavaScriptSemanticProjectedResultBinding[], +): void => { + for (const projected of bindings) + addSemanticGraphRelation(context.state, { + source, + target: context.bindingNodes.get(projected.bindingId), + relation: "destructures", + resolution: + projected.resolution === "complete" ? "resolved" : "candidate", + properties: { + projection_path: [...projected.projectionPath], + projection_resolution: projected.resolution, + }, + }); +}; + /** Project explicit static Promise/task ownership without runtime claims. */ export const projectSemanticPromises = ( context: SemanticFlowProjectionContext, diff --git a/src/domain/javascript/javascriptSemanticAsyncEffects.ts b/src/domain/javascript/javascriptSemanticAsyncEffects.ts index 99fe538738..bb2b6876d2 100644 --- a/src/domain/javascript/javascriptSemanticAsyncEffects.ts +++ b/src/domain/javascript/javascriptSemanticAsyncEffects.ts @@ -2,6 +2,7 @@ import * as t from "@babel/types"; import type { JavaScriptSemanticCallable, + JavaScriptSemanticProjectedResultBinding, JavaScriptSemanticEventOperation, JavaScriptSemanticTimerOperation, } from "./javascriptSemanticIr.js"; @@ -71,11 +72,7 @@ interface TimerCandidate { readonly kind: JavaScriptSemanticTimerOperation["kind"]; readonly ownerCallableId: string | null; readonly handleBindingId: string | null; - readonly projectedResultBindings: readonly { - readonly bindingId: string; - readonly projectionPath: readonly (string | number | null)[]; - readonly resolution: "complete" | "partial"; - }[]; + readonly projectedResultBindings: readonly JavaScriptSemanticProjectedResultBinding[]; readonly handleRead: { readonly origins: readonly t.Node[]; readonly complete: boolean; diff --git a/src/domain/javascript/javascriptSemanticChildProcesses.ts b/src/domain/javascript/javascriptSemanticChildProcesses.ts index 639be8268a..3140e08bf1 100644 --- a/src/domain/javascript/javascriptSemanticChildProcesses.ts +++ b/src/domain/javascript/javascriptSemanticChildProcesses.ts @@ -2,6 +2,7 @@ import * as t from "@babel/types"; import type { JavaScriptSemanticCallable, + JavaScriptSemanticProjectedResultBinding, JavaScriptSemanticChildProcessInteraction, JavaScriptSemanticChildProcessSpawn, } from "./javascriptSemanticIr.js"; @@ -49,11 +50,7 @@ interface SpawnCandidate { readonly method: JavaScriptSemanticChildProcessSpawn["method"]; readonly ownerCallableId: string | null; readonly resultBindingId: string | null; - readonly projectedResultBindings: readonly { - readonly bindingId: string; - readonly projectionPath: readonly (string | number | null)[]; - readonly resolution: "complete" | "partial"; - }[]; + readonly projectedResultBindings: readonly JavaScriptSemanticProjectedResultBinding[]; } interface ChildInteractionContext { diff --git a/src/domain/javascript/javascriptSemanticDataEffectHelpers.ts b/src/domain/javascript/javascriptSemanticDataEffectHelpers.ts index 6c0f3c210d..e014149f55 100644 --- a/src/domain/javascript/javascriptSemanticDataEffectHelpers.ts +++ b/src/domain/javascript/javascriptSemanticDataEffectHelpers.ts @@ -8,7 +8,10 @@ import { semanticGlobalProcessOrigin, semanticMutableBuiltinOrigin, } from "./javascriptSemanticRequireOrigin.js"; -import type { JavaScriptModuleOrigin } from "./javascriptSemanticIr.js"; +import type { + JavaScriptModuleOrigin, + JavaScriptSemanticProjectedResultBinding, +} from "./javascriptSemanticIr.js"; import { evaluateSemanticProvenance, evaluateSemanticProvenanceAtRead, @@ -35,11 +38,8 @@ import { unwrapJavaScriptExpression, } from "./javascriptAstValues.js"; -export interface SemanticProjectedBinding { - readonly bindingId: string; +export interface SemanticProjectedBinding extends JavaScriptSemanticProjectedResultBinding { readonly identifier: t.Identifier; - readonly projectionPath: readonly (string | number | null)[]; - readonly resolution: "complete" | "partial"; } /** Resolve bindings written by the nearest variable or assignment owner. */ @@ -136,7 +136,7 @@ const projectedPatternBindings = ( export const semanticProjectedBindingFacts = ( bindings: readonly SemanticProjectedBinding[], -): Omit[] => +): JavaScriptSemanticProjectedResultBinding[] => bindings.map(({ identifier: _identifier, ...fact }) => fact); /** Return a scalar owner only for an unprojected identifier result. */