mirror of
https://github.com/morluto/rea.git
synced 2026-10-27 01:45:32 +00:00
refactor(javascript): group application workflows and verification (#896)
This commit is contained in:
1 parent
faaee693d5
commit
0804722cdf
129 files changed
+402
-367
No files matched your search
@@ -0,0 +1,269 @@
|
||||
import { createJavaScriptSemanticGraphUnknown } from "../../domain/javascriptSemanticGraph.js";
|
||||
import type { JavaScriptSemanticGraphNode } from "../../domain/javascriptSemanticGraph.js";
|
||||
import type {
|
||||
JavaScriptSemanticChildProcessInteraction,
|
||||
JavaScriptSemanticChildProcessSpawn,
|
||||
} from "../../domain/javascriptSemanticIr.js";
|
||||
import {
|
||||
addSemanticGraphNode,
|
||||
addSemanticGraphRelation,
|
||||
addSemanticGraphUnknown,
|
||||
constructSemanticGraphNode,
|
||||
} from "./JavaScriptSemanticGraphConstruction.js";
|
||||
import { unknownSemanticEvidence } from "./JavaScriptSemanticGraphEvidence.js";
|
||||
import {
|
||||
semanticCallSiteAt,
|
||||
type SemanticFlowProjectionContext,
|
||||
} from "./JavaScriptSemanticGraphFlowProjection.js";
|
||||
|
||||
interface ChildUnknownInput {
|
||||
readonly context: SemanticFlowProjectionContext;
|
||||
readonly node: JavaScriptSemanticGraphNode | null;
|
||||
readonly location: JavaScriptSemanticChildProcessSpawn["location"];
|
||||
readonly relationKinds: readonly (
|
||||
| "spawns"
|
||||
| "forwards-signal"
|
||||
| "listens-exit"
|
||||
| "listens-error"
|
||||
)[];
|
||||
readonly detail: string;
|
||||
readonly candidateNodeIds: readonly string[];
|
||||
}
|
||||
|
||||
/** Project child creation, argv/env/stdio, listeners, and signals. */
|
||||
export const projectSemanticChildProcesses = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
): void => {
|
||||
const processNodes = new Map(
|
||||
context.ir.childProcessSpawns.flatMap((spawn) => {
|
||||
const node = addChildProcessNode(context, spawn);
|
||||
return node === null ? [] : [[spawn.processId, node] as const];
|
||||
}),
|
||||
);
|
||||
for (const spawn of context.ir.childProcessSpawns) {
|
||||
const child = processNodes.get(spawn.processId);
|
||||
if (child === undefined) continue;
|
||||
projectSpawn(context, spawn, child);
|
||||
}
|
||||
for (const interaction of context.ir.childProcessInteractions)
|
||||
projectInteraction(context, interaction, processNodes);
|
||||
};
|
||||
|
||||
const addChildProcessNode = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
spawn: JavaScriptSemanticChildProcessSpawn,
|
||||
): JavaScriptSemanticGraphNode | null =>
|
||||
addSemanticGraphNode(
|
||||
context.state,
|
||||
constructSemanticGraphNode(
|
||||
context.file,
|
||||
{
|
||||
kind: "child-process",
|
||||
roleKey: spawn.processId,
|
||||
location: spawn.location,
|
||||
label: spawn.command ?? spawn.method,
|
||||
functionNodeId:
|
||||
spawn.ownerCallableId === null
|
||||
? null
|
||||
: (context.callableNodes.get(spawn.ownerCallableId)?.node_id ??
|
||||
null),
|
||||
properties: {
|
||||
argv_count: spawn.argvCount,
|
||||
command: spawn.command,
|
||||
environment_supplied: spawn.environmentSupplied,
|
||||
method: spawn.method,
|
||||
stdio_mode: spawn.stdioMode,
|
||||
},
|
||||
},
|
||||
context.state,
|
||||
),
|
||||
);
|
||||
|
||||
const projectSpawn = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
spawn: JavaScriptSemanticChildProcessSpawn,
|
||||
child: JavaScriptSemanticGraphNode,
|
||||
): void => {
|
||||
const callSite = semanticCallSiteAt(context, spawn.location);
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSite,
|
||||
target: child,
|
||||
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,
|
||||
},
|
||||
});
|
||||
if (spawn.argvCount !== null)
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSite,
|
||||
target: child,
|
||||
relation: "supplies-argv",
|
||||
resolution: "resolved",
|
||||
properties: { argument_count: spawn.argvCount },
|
||||
});
|
||||
if (spawn.environmentSupplied)
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: callSite,
|
||||
target: child,
|
||||
relation: "supplies-env",
|
||||
resolution: "resolved",
|
||||
});
|
||||
const stdio = addStdioNode(context, spawn, child);
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: child,
|
||||
target: stdio,
|
||||
relation: "connects-stdio",
|
||||
resolution: spawn.stdioMode === "dynamic" ? "candidate" : "resolved",
|
||||
});
|
||||
if (spawn.resolution !== "complete")
|
||||
addChildUnknown({
|
||||
context,
|
||||
node: child,
|
||||
location: spawn.location,
|
||||
relationKinds: ["spawns"],
|
||||
detail: `Static ${spawn.method} command remains unresolved.`,
|
||||
candidateNodeIds: [],
|
||||
});
|
||||
};
|
||||
|
||||
const addStdioNode = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
spawn: JavaScriptSemanticChildProcessSpawn,
|
||||
child: JavaScriptSemanticGraphNode,
|
||||
): JavaScriptSemanticGraphNode | null =>
|
||||
addSemanticGraphNode(
|
||||
context.state,
|
||||
constructSemanticGraphNode(
|
||||
context.file,
|
||||
{
|
||||
kind: "stdio",
|
||||
roleKey: `stdio:${spawn.processId}`,
|
||||
location: spawn.location,
|
||||
label: spawn.stdioMode,
|
||||
functionNodeId: child.function_node_id,
|
||||
properties: { mode: spawn.stdioMode },
|
||||
},
|
||||
context.state,
|
||||
),
|
||||
);
|
||||
|
||||
const projectInteraction = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
interaction: JavaScriptSemanticChildProcessInteraction,
|
||||
processNodes: ReadonlyMap<string, JavaScriptSemanticGraphNode>,
|
||||
): void => {
|
||||
const children = interaction.linkedProcessIds.flatMap((identifier) => {
|
||||
const child = processNodes.get(identifier);
|
||||
return child === undefined ? [] : [child];
|
||||
});
|
||||
const effect =
|
||||
interaction.kind === "listener"
|
||||
? addChildListenerNode(context, interaction)
|
||||
: addSignalNode(context, interaction);
|
||||
for (const child of children)
|
||||
addSemanticGraphRelation(context.state, {
|
||||
source: interaction.kind === "listener" ? child : effect,
|
||||
target: interaction.kind === "listener" ? effect : child,
|
||||
relation:
|
||||
interaction.kind === "signal"
|
||||
? "forwards-signal"
|
||||
: interaction.eventName === "exit"
|
||||
? "listens-exit"
|
||||
: "listens-error",
|
||||
resolution:
|
||||
interaction.resolution === "complete" ? "resolved" : "candidate",
|
||||
});
|
||||
if (interaction.resolution !== "complete")
|
||||
addChildUnknown({
|
||||
context,
|
||||
node: effect,
|
||||
location: interaction.location,
|
||||
relationKinds: [
|
||||
interaction.kind === "signal"
|
||||
? "forwards-signal"
|
||||
: interaction.eventName === "exit"
|
||||
? "listens-exit"
|
||||
: "listens-error",
|
||||
],
|
||||
detail: `Static child ${interaction.method} ownership is ambiguous.`,
|
||||
candidateNodeIds: children.map(({ node_id: identifier }) => identifier),
|
||||
});
|
||||
};
|
||||
|
||||
const addChildListenerNode = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
interaction: JavaScriptSemanticChildProcessInteraction,
|
||||
): JavaScriptSemanticGraphNode | null =>
|
||||
addSemanticGraphNode(
|
||||
context.state,
|
||||
constructSemanticGraphNode(
|
||||
context.file,
|
||||
{
|
||||
kind: "listener",
|
||||
roleKey: interaction.interactionId,
|
||||
location: interaction.listenerLocation ?? interaction.location,
|
||||
label: interaction.eventName,
|
||||
functionNodeId:
|
||||
interaction.ownerCallableId === null
|
||||
? null
|
||||
: (context.callableNodes.get(interaction.ownerCallableId)
|
||||
?.node_id ?? null),
|
||||
properties: {
|
||||
event_name: interaction.eventName,
|
||||
method: interaction.method,
|
||||
},
|
||||
},
|
||||
context.state,
|
||||
),
|
||||
);
|
||||
|
||||
const addSignalNode = (
|
||||
context: SemanticFlowProjectionContext,
|
||||
interaction: JavaScriptSemanticChildProcessInteraction,
|
||||
): JavaScriptSemanticGraphNode | null =>
|
||||
addSemanticGraphNode(
|
||||
context.state,
|
||||
constructSemanticGraphNode(
|
||||
context.file,
|
||||
{
|
||||
kind: "signal",
|
||||
roleKey: interaction.interactionId,
|
||||
location: interaction.location,
|
||||
label: interaction.signalName,
|
||||
functionNodeId:
|
||||
interaction.ownerCallableId === null
|
||||
? null
|
||||
: (context.callableNodes.get(interaction.ownerCallableId)
|
||||
?.node_id ?? null),
|
||||
properties: { signal_name: interaction.signalName },
|
||||
},
|
||||
context.state,
|
||||
),
|
||||
);
|
||||
|
||||
const addChildUnknown = (input: ChildUnknownInput): void => {
|
||||
const { context, node, location, relationKinds, detail, candidateNodeIds } =
|
||||
input;
|
||||
addSemanticGraphUnknown(
|
||||
context.state,
|
||||
createJavaScriptSemanticGraphUnknown({
|
||||
node_id: node?.node_id ?? null,
|
||||
family: "child-process",
|
||||
relation_kinds: relationKinds,
|
||||
reason: "ambiguous-target",
|
||||
detail,
|
||||
candidate_node_ids: candidateNodeIds,
|
||||
evidence: unknownSemanticEvidence(context.file, location),
|
||||
}),
|
||||
);
|
||||
};
|
||||
Reference in new issue
Block a user