rea-agents 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/README.md +84 -18
  2. package/dist/application/AndroidApplicationService.js +17 -0
  3. package/dist/application/AppleApplicationService.js +17 -0
  4. package/dist/application/ApplicationWorkflowEvidenceResolver.js +162 -0
  5. package/dist/application/BinarySession.js +9 -2
  6. package/dist/application/BinarySessionStatus.js +39 -1
  7. package/dist/application/CapabilityInventory.js +15 -0
  8. package/dist/application/ClientRegistrationStatus.js +11 -4
  9. package/dist/application/CommandShimReplay.js +6 -3
  10. package/dist/application/CompositeProvider.js +5 -2
  11. package/dist/application/DirectAnalysis.js +1 -20
  12. package/dist/application/DirectAnalysisStatus.js +83 -0
  13. package/dist/application/Doctor.js +34 -25
  14. package/dist/application/DoctorDiagnostics.js +1 -4
  15. package/dist/application/DoctorProjection.js +43 -0
  16. package/dist/application/DoctorScope.js +102 -0
  17. package/dist/application/EvidenceReferenceResolver.js +26 -0
  18. package/dist/application/InventoryProjectionEvidence.js +41 -0
  19. package/dist/application/InvestigationProviders.js +12 -0
  20. package/dist/application/JavaScriptApplicationEvidence.js +1 -1
  21. package/dist/application/JavaScriptApplicationEvidenceGraph.js +10 -1
  22. package/dist/application/JavaScriptApplicationService.js +3 -2
  23. package/dist/application/JavaScriptApplicationWorkflowEvidence.js +26 -0
  24. package/dist/application/JavaScriptApplicationWorkflowService.js +49 -1
  25. package/dist/application/JavaScriptArtifactReconstruction.js +7 -0
  26. package/dist/application/JavaScriptModuleRelationships.js +25 -1
  27. package/dist/application/JavaScriptReturnShapeProjection.js +111 -0
  28. package/dist/application/JavaScriptSemanticGraphBuilder.js +294 -0
  29. package/dist/application/JavaScriptSemanticGraphConstruction.js +144 -0
  30. package/dist/application/JavaScriptSemanticGraphEvidence.js +86 -0
  31. package/dist/application/JavaScriptSemanticGraphFlowProjection.js +72 -0
  32. package/dist/application/JavaScriptSemanticGraphProjection.js +33 -0
  33. package/dist/application/JavaScriptSemanticTraceService.js +45 -0
  34. package/dist/application/LoopbackReplay.js +277 -133
  35. package/dist/application/LoopbackReplayRecorder.js +101 -0
  36. package/dist/application/PermissionAuthority.js +7 -3
  37. package/dist/application/ProcessCaptureJournal.js +95 -0
  38. package/dist/application/ProcessCaptureLifecycle.js +24 -13
  39. package/dist/application/ProcessCheckpoints.js +30 -4
  40. package/dist/application/ProcessCli.js +26 -4
  41. package/dist/application/ProcessHarness.js +20 -50
  42. package/dist/application/ProcessNormalization.js +3 -2
  43. package/dist/application/ProcessPairedExperiment.js +72 -0
  44. package/dist/application/ProcessReactiveCoordinator.js +218 -0
  45. package/dist/application/ProcessReactiveEffects.js +133 -0
  46. package/dist/application/ProcessReactiveObservations.js +20 -0
  47. package/dist/application/ProcessSampling.js +2 -0
  48. package/dist/application/SessionProviderRouter.js +7 -6
  49. package/dist/application/Setup.js +14 -13
  50. package/dist/application/SetupClientConfiguration.js +10 -6
  51. package/dist/application/SetupPlan.js +4 -3
  52. package/dist/application/SetupRegistrationEnvironment.js +12 -0
  53. package/dist/application/SetupSkill.js +44 -40
  54. package/dist/application/TerminalRenderer.js +3 -1
  55. package/dist/application/Uninstall.js +7 -1
  56. package/dist/application/Upgrade.js +16 -1
  57. package/dist/browser/CdpEndpoint.js +37 -4
  58. package/dist/catalogIdentity.js +16 -7
  59. package/dist/cli/coreAnalysisCommands.js +34 -2
  60. package/dist/cli/javascriptApplicationAnalysis.js +19 -0
  61. package/dist/cli/setupCommands.js +84 -30
  62. package/dist/cli/utilityCommands.js +10 -2
  63. package/dist/cliApplicationCommands.js +79 -16
  64. package/dist/cliCommandNames.js +3 -0
  65. package/dist/cliElectronCommands.js +23 -33
  66. package/dist/cliObservationOptions.js +1 -1
  67. package/dist/cliOutput.js +56 -0
  68. package/dist/cliProcessCommands.js +32 -1
  69. package/dist/cliSetup.js +55 -29
  70. package/dist/contracts/applicationToolContracts.js +54 -7
  71. package/dist/contracts/applicationWorkflowInputContracts.js +119 -0
  72. package/dist/contracts/electronToolContracts.js +73 -4
  73. package/dist/contracts/javascriptApplicationWorkflowExamples.js +32 -9
  74. package/dist/contracts/managedToolContracts.js +7 -2
  75. package/dist/contracts/processCaptureExample.js +1 -0
  76. package/dist/contracts/replayMachineExample.js +37 -0
  77. package/dist/contracts/sessionStatusContract.js +26 -0
  78. package/dist/contracts/toolContractExamples.js +2 -0
  79. package/dist/contracts/toolContractTypes.js +12 -1
  80. package/dist/contracts/toolContracts.js +8 -11
  81. package/dist/contracts/toolEffects.js +3 -0
  82. package/dist/contracts/toolOutputSchemaGroups.js +53 -9
  83. package/dist/contracts/toolOutputSchemaPrimitives.js +73 -32
  84. package/dist/doctorRuntime.js +41 -0
  85. package/dist/domain/analysisErrorPresentation.js +5 -3
  86. package/dist/domain/analysisErrorProjection.js +7 -1
  87. package/dist/domain/androidApplication.js +206 -0
  88. package/dist/domain/appleApplication.js +221 -0
  89. package/dist/domain/boundedCartesianProjection.js +15 -0
  90. package/dist/domain/changedBehavior.js +5 -4
  91. package/dist/domain/completionLedgerGeneration.js +190 -0
  92. package/dist/domain/errors.js +4 -2
  93. package/dist/domain/evidenceCompletionLedger.js +113 -0
  94. package/dist/domain/hopperStartupFailure.js +9 -0
  95. package/dist/domain/javascriptApplicationAnalysis.js +44 -2
  96. package/dist/domain/javascriptExportShapeComparison.js +145 -0
  97. package/dist/domain/javascriptExportShapeComparisonIdentity.js +11 -0
  98. package/dist/domain/javascriptExportShapeComparisonSchemas.js +277 -0
  99. package/dist/domain/javascriptExportShapeSelection.js +132 -0
  100. package/dist/domain/javascriptExportShapeVariants.js +272 -0
  101. package/dist/domain/javascriptRuntimeReconciliationParsing.js +2 -2
  102. package/dist/domain/javascriptSemanticAnalysis.js +17 -36
  103. package/dist/domain/javascriptSemanticCallResolution.js +129 -0
  104. package/dist/domain/javascriptSemanticCalls.js +269 -0
  105. package/dist/domain/javascriptSemanticGraph.js +356 -0
  106. package/dist/domain/javascriptSemanticGraphSchemas.js +329 -0
  107. package/dist/domain/javascriptSemanticGraphSerialization.js +23 -0
  108. package/dist/domain/javascriptSemanticIr.js +14 -1
  109. package/dist/domain/javascriptSemanticPrimitives.js +36 -0
  110. package/dist/domain/javascriptSemanticProjection.js +36 -4
  111. package/dist/domain/javascriptSemanticProvenance.js +29 -0
  112. package/dist/domain/javascriptSemanticQuery.js +294 -0
  113. package/dist/domain/javascriptSemanticQueryAssessment.js +74 -0
  114. package/dist/domain/javascriptSemanticQueryIdentity.js +86 -0
  115. package/dist/domain/javascriptSemanticQueryRelations.js +15 -0
  116. package/dist/domain/javascriptSemanticQuerySchemas.js +188 -0
  117. package/dist/domain/javascriptSemanticReturns.js +187 -0
  118. package/dist/domain/javascriptSemanticState.js +32 -0
  119. package/dist/domain/javascriptSemanticTraceSchemas.js +14 -0
  120. package/dist/domain/javascriptSemanticValues.js +54 -70
  121. package/dist/domain/processCapture.js +36 -0
  122. package/dist/domain/processCaptureValidation.js +101 -12
  123. package/dist/domain/processComparison.js +96 -24
  124. package/dist/domain/processObservation.js +153 -0
  125. package/dist/domain/processPairedExperiment.js +128 -0
  126. package/dist/domain/processReactiveCheckpointDataflow.js +32 -0
  127. package/dist/domain/processReactiveMatching.js +210 -0
  128. package/dist/domain/processReactiveRuntime.js +109 -0
  129. package/dist/domain/processReactiveScenario.js +361 -0
  130. package/dist/domain/processReactiveScenarioPreflight.js +120 -0
  131. package/dist/domain/processReactiveTransition.js +159 -0
  132. package/dist/domain/processScenario.js +12 -0
  133. package/dist/domain/processTraceComparison.js +43 -0
  134. package/dist/domain/processTraceDimensionProjection.js +50 -0
  135. package/dist/domain/processTraceEvaluation.js +312 -0
  136. package/dist/domain/processTraceSpecification.js +373 -0
  137. package/dist/domain/reconstructionVerification.js +5 -4
  138. package/dist/domain/replayMachine.js +332 -0
  139. package/dist/domain/replayMachineRun.js +216 -0
  140. package/dist/domain/replayMachineRuntime.js +294 -0
  141. package/dist/domain/runtimeIdentification.js +210 -0
  142. package/dist/domain/staticRuntimeCorrelation.js +4 -2
  143. package/dist/evaluation/CodexAgentEval.js +156 -0
  144. package/dist/generatedPackageMetadata.js +2 -2
  145. package/dist/ghidra/GhidraClient.js +13 -2
  146. package/dist/ghidra/GhidraDiagnostics.js +1 -0
  147. package/dist/ghidra/GhidraLauncher.js +7 -3
  148. package/dist/ghidra/GhidraProvider.js +8 -1
  149. package/dist/hopper/BridgeLauncher.js +56 -14
  150. package/dist/hopper/HopperClient.js +23 -10
  151. package/dist/hopper/HopperDiagnostics.js +14 -0
  152. package/dist/hopper/HopperProcessDiagnostic.js +14 -0
  153. package/dist/hopper/HopperProvider.js +8 -1
  154. package/dist/hopper/LinuxPrivateDisplayDiagnostic.js +72 -0
  155. package/dist/hopper/LinuxPrivateDisplayProbe.js +217 -0
  156. package/dist/identity.js +2 -1
  157. package/dist/main/transport.js +2 -0
  158. package/dist/mcpDoctor.js +310 -0
  159. package/dist/process/ProcessOwnership.js +200 -53
  160. package/dist/process/ProviderRunLineage.js +21 -0
  161. package/dist/server/createServer.js +8 -2
  162. package/dist/server/javascriptApplicationResult.js +106 -0
  163. package/dist/server/registerApplicationTools/characterization.js +3 -3
  164. package/dist/server/registerApplicationTools/compareExportShapes.js +37 -0
  165. package/dist/server/registerApplicationTools/compareVersions.js +12 -6
  166. package/dist/server/registerApplicationTools/controlledReplay.js +2 -2
  167. package/dist/server/registerApplicationTools/coverage.js +3 -3
  168. package/dist/server/registerApplicationTools/helpers.js +37 -25
  169. package/dist/server/registerApplicationTools/traceFeature.js +9 -5
  170. package/dist/server/registerApplicationTools/traceSemantics.js +30 -0
  171. package/dist/server/registerApplicationTools.js +4 -0
  172. package/dist/server/registerElectronTools.js +19 -5
  173. package/dist/server/registerEvidenceResources.js +7 -1
  174. package/dist/server/registerJavaScriptApplicationGraphResource.js +69 -0
  175. package/dist/server/registerManagedTools.js +18 -1
  176. package/dist/server/registerProcessComparisonTool.js +17 -7
  177. package/dist/server/registerReplayMachineTool.js +20 -0
  178. package/dist/server/registerSessionStatusTool.js +95 -25
  179. package/dist/server/registerSessionTools.js +12 -7
  180. package/dist/server/sessionAvailabilityPolicy.js +2 -1
  181. package/dist/server/toolResult.js +10 -4
  182. package/dist/serverIdentity.js +19 -0
  183. package/package.json +62 -29
  184. package/scripts/hopper-demo-x11.py +459 -61
  185. package/scripts/prepack.mjs +55 -0
  186. package/scripts/prepare.mjs +28 -0
  187. package/scripts/rea.mjs +26 -5
  188. package/skills/reverse-engineer-anything/SKILL.md +69 -228
  189. package/skills/reverse-engineer-anything/references/controlled-replay.md +12 -0
  190. package/skills/reverse-engineer-anything/references/evidence-workflows.md +33 -0
  191. package/skills/reverse-engineer-anything/references/javascript-applications.md +35 -0
  192. package/skills/reverse-engineer-anything/references/native-and-artifacts.md +36 -0
  193. package/skills/reverse-engineer-anything/references/runtime-observation.md +24 -0
@@ -22,16 +22,16 @@ export const digestCanonical = (value) => {
22
22
  };
23
23
  const parseStaticLayer = (layer) => {
24
24
  const evidence = parseEvidence(layer.analysis);
25
+ const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
25
26
  assertEvidenceIdentity(evidence, {
26
27
  operation: "analyze_javascript_application",
27
- predicate: "rea.javascript-application-analysis/v1",
28
+ predicate: `rea.javascript-application-analysis/v${String(result.schema_version)}`,
28
29
  providerId: "rea-javascript-application",
29
30
  providerName: "REA JavaScript application analyzer",
30
31
  providerVersion: "1",
31
32
  authority: "shipped-artifact",
32
33
  confidence: "derived",
33
34
  });
34
- const result = javascriptApplicationAnalysisResultSchema.parse(evidence.normalized_result);
35
35
  if (evidence.subject === null ||
36
36
  evidence.subject.digest.sha256 !== result.root_artifact_sha256 ||
37
37
  evidence.subject.format !== result.format ||
@@ -2,8 +2,10 @@ import { parse } from "@babel/parser";
2
2
  import * as t from "@babel/types";
3
3
  import { DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS, failedJavaScriptSemanticIr, } from "./javascriptSemanticIr.js";
4
4
  import { collectSemanticModuleLink, collectSemanticCallable, collectSemanticReferences, immutableSemanticBindings, immutableSemanticScopes, reachSemanticLimit, semanticModuleOrigin, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
5
- import { currentSemanticScope, semanticScopeId, } from "./javascriptSemanticState.js";
5
+ import { currentSemanticScope, resolveSemanticBindingFromScope, semanticScopeId, semanticVariableScope, } from "./javascriptSemanticState.js";
6
6
  import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
7
+ import { collectSemanticReturns, resolveSemanticModuleCallables, } from "./javascriptSemanticReturns.js";
8
+ import { collectJavaScriptSemanticCalls } from "./javascriptSemanticCalls.js";
7
9
  import { compareCodePoints, propertyName, range, } from "./javascriptStaticAnalysisHelpers.js";
8
10
  /** Recover bounded lexical bindings, aliases, constants, and module links. */
9
11
  export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
@@ -23,14 +25,19 @@ export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
23
25
  collectDefinitions(file.program, state);
24
26
  const references = collectSemanticReferences(file.program, state);
25
27
  const parserPartial = file.errors.length > 0;
28
+ const bindings = immutableSemanticBindings(state);
29
+ const callables = collectSemanticReturns(file.program, state, parserPartial);
30
+ const moduleLinks = resolveSemanticModuleCallables(state, callables);
31
+ const calls = collectJavaScriptSemanticCalls(file.program, state, callables);
26
32
  return {
27
33
  schema: "JavaScriptSemanticIR",
28
- schemaVersion: 1,
34
+ schemaVersion: 3,
29
35
  scopes: immutableSemanticScopes(state),
30
- bindings: immutableSemanticBindings(state),
31
- callables: [...state.callables],
36
+ bindings,
37
+ callables,
32
38
  references,
33
- moduleLinks: [...state.moduleLinks],
39
+ moduleLinks,
40
+ ...calls,
34
41
  coverage: {
35
42
  status: state.limitsReached.size > 0
36
43
  ? "truncated"
@@ -50,6 +57,8 @@ export const analyzeJavaScriptSemantics = (source, inputLimits = {}) => {
50
57
  ? []
51
58
  : ["Semantic recovery stopped retaining facts at explicit limits."]),
52
59
  "Values and aliases were recovered from inert syntax only; no JavaScript was executed.",
60
+ "Return sites include only direct callable returns; nested callable returns remain separate.",
61
+ "Local call, argument, return, and closure relations are static candidates and do not prove runtime invocation.",
53
62
  "Cross-function mutation and dynamic property resolution remain unknown.",
54
63
  ],
55
64
  };
@@ -70,6 +79,7 @@ const createState = (program, limits) => {
70
79
  scopeByNode: new WeakMap([[program, root]]),
71
80
  bindingsById: new Map(),
72
81
  callables: [],
82
+ callableNodesById: new Map(),
73
83
  moduleLinks: [],
74
84
  limitsReached: new Set(),
75
85
  omittedCount: 0,
@@ -136,7 +146,7 @@ const bindInnerDeclaration = (node, parent, scope, state) => {
136
146
  bindPattern({
137
147
  pattern: node.id,
138
148
  initializer: node.init ?? null,
139
- scope: variableScope(scope, parent, state),
149
+ scope: semanticVariableScope(scope, parent, state),
140
150
  state,
141
151
  mutable: parent !== null && t.isVariableDeclaration(parent)
142
152
  ? parent.kind !== "const"
@@ -348,39 +358,10 @@ const createBinding = (scope, name, kind, mutable) => ({
348
358
  directOrigins: [],
349
359
  });
350
360
  const addAssignment = (identifier, initializer, scope, state) => {
351
- const binding = resolveFromScope(scope, identifier.name, state);
361
+ const binding = resolveSemanticBindingFromScope(scope, identifier.name, state);
352
362
  if (binding === undefined)
353
363
  return;
354
364
  binding.mutable = true;
355
365
  binding.definitions.push({ kind: "assignment", location: range(identifier) });
356
366
  binding.initializers.push({ node: initializer, projection: [] });
357
367
  };
358
- const resolveFromScope = (scope, name, state) => {
359
- let candidate = scope;
360
- while (candidate !== undefined) {
361
- const binding = candidate.bindings.get(name);
362
- if (binding !== undefined)
363
- return binding;
364
- if (!candidate.bindingsComplete)
365
- return undefined;
366
- candidate =
367
- candidate.parentScopeId === null
368
- ? undefined
369
- : state.scopesById.get(candidate.parentScopeId);
370
- }
371
- return undefined;
372
- };
373
- const variableScope = (scope, parent, state) => {
374
- if (!(parent !== null && t.isVariableDeclaration(parent, { kind: "var" })))
375
- return scope;
376
- let candidate = scope;
377
- while (candidate.kind === "block" || candidate.kind === "catch") {
378
- const outer = candidate.parentScopeId === null
379
- ? undefined
380
- : state.scopesById.get(candidate.parentScopeId);
381
- if (outer === undefined)
382
- break;
383
- candidate = outer;
384
- }
385
- return candidate;
386
- };
@@ -0,0 +1,129 @@
1
+ import * as t from "@babel/types";
2
+ import { reachSemanticLimit, semanticCallableIdForNode, } from "./javascriptSemanticProjection.js";
3
+ import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
4
+ import { compareCodePoints, range } from "./javascriptStaticAnalysisHelpers.js";
5
+ /** Resolve bounded local callable candidates without following dynamic properties. */
6
+ export const resolveLocalCallables = (input) => resolveCallables(input.node, {
7
+ state: input.state,
8
+ callableById: input.callableById,
9
+ seenBindings: input.seenBindings,
10
+ depth: input.depth,
11
+ bindingCache: input.bindingCache,
12
+ });
13
+ const resolveCallables = (node, context) => {
14
+ if (context.depth >= context.state.limits.maxValueDepth) {
15
+ reachSemanticLimit(context.state, "maxValueDepth");
16
+ return {
17
+ callableIds: [],
18
+ complete: false,
19
+ reason: "Call target depth limit reached.",
20
+ };
21
+ }
22
+ const direct = semanticCallableIdForNode(node);
23
+ if (direct !== null && context.callableById.has(direct))
24
+ return {
25
+ callableIds: [direct],
26
+ complete: true,
27
+ reason: "Direct local callable.",
28
+ };
29
+ if (t.isIdentifier(node))
30
+ return resolveIdentifier(node, context);
31
+ if (t.isTSAsExpression(node) ||
32
+ t.isTSTypeAssertion(node) ||
33
+ t.isTSNonNullExpression(node))
34
+ return resolveCallables(node.expression, nestedContext(context));
35
+ if (t.isConditionalExpression(node) || t.isLogicalExpression(node))
36
+ return resolveAlternatives(t.isConditionalExpression(node) ? node.consequent : node.left, t.isConditionalExpression(node) ? node.alternate : node.right, context);
37
+ return {
38
+ callableIds: [],
39
+ complete: false,
40
+ reason: `Unsupported ${node.type} call target.`,
41
+ };
42
+ };
43
+ const resolveIdentifier = (node, context) => {
44
+ const binding = resolveSemanticBindingState(context.state, node, node.name);
45
+ return binding === undefined
46
+ ? {
47
+ callableIds: [],
48
+ complete: false,
49
+ reason: `Unresolved call target ${node.name}.`,
50
+ }
51
+ : resolveBindingCallables(binding, nestedContext(context));
52
+ };
53
+ const resolveAlternatives = (left, right, context) => {
54
+ const candidates = [left, right].map((candidate) => resolveCallables(candidate, nestedContext(context)));
55
+ return {
56
+ callableIds: uniqueCallableIds(candidates.flatMap(({ callableIds }) => callableIds)),
57
+ complete: candidates.every(({ complete }) => complete),
58
+ reason: "Conditional or logical call target has multiple candidates.",
59
+ };
60
+ };
61
+ const resolveBindingCallables = (binding, context) => {
62
+ const cached = context.bindingCache.get(binding.bindingId);
63
+ if (cached !== undefined)
64
+ return cached;
65
+ if (context.seenBindings.has(binding.bindingId))
66
+ return {
67
+ callableIds: [],
68
+ complete: false,
69
+ reason: `Call target alias cycle at ${binding.name}.`,
70
+ };
71
+ if (binding.initializers.length === 0)
72
+ return missingBindingInitializer(binding);
73
+ const candidateContext = {
74
+ ...nestedContext(context),
75
+ seenBindings: new Set([...context.seenBindings, binding.bindingId]),
76
+ };
77
+ const candidates = binding.initializers.map(({ node, projection }) => projection.length === 0
78
+ ? resolveCallables(node, candidateContext)
79
+ : {
80
+ callableIds: [],
81
+ complete: false,
82
+ reason: `Projected call target ${binding.name} is unresolved.`,
83
+ });
84
+ const callableIds = uniqueCallableIds(candidates.flatMap((candidate) => candidate.callableIds));
85
+ const resolution = {
86
+ callableIds,
87
+ complete: candidates.every(({ complete }) => complete),
88
+ reason: callableIds.length === 1 && candidates.every(({ complete }) => complete)
89
+ ? "Unique local callable target."
90
+ : `Call target ${binding.name} has ambiguous or unsupported assignments.`,
91
+ };
92
+ if (resolution.complete)
93
+ context.bindingCache.set(binding.bindingId, resolution);
94
+ return resolution;
95
+ };
96
+ const missingBindingInitializer = (binding) => ({
97
+ callableIds: [],
98
+ complete: false,
99
+ reason: binding.kind === "import"
100
+ ? `Call target ${binding.name} is imported from outside this source.`
101
+ : `Call target ${binding.name} has no local initializer.`,
102
+ });
103
+ /** Find all bindings introduced by one positional parameter pattern. */
104
+ export const parameterBindings = (callable, index, state) => {
105
+ if (callable.bodyScopeId === null)
106
+ return [];
107
+ const scope = state.scopesById.get(callable.bodyScopeId);
108
+ if (scope === undefined)
109
+ return [];
110
+ return [...scope.bindings.values()]
111
+ .filter((binding) => binding.kind === "parameter" &&
112
+ binding.definitions.some(({ kind, location }) => kind === "parameter" &&
113
+ parameterIndex(callable, location, state) === index))
114
+ .sort((left, right) => compareCodePoints(left.bindingId, right.bindingId));
115
+ };
116
+ const parameterIndex = (callable, location, state) => {
117
+ const node = state.callableNodesById.get(callable.callableId);
118
+ if (!t.isFunction(node))
119
+ return -1;
120
+ return node.params.findIndex((parameter) => rangeContains(range(parameter), location));
121
+ };
122
+ const rangeContains = (outer, inner) => (outer.start.line < inner.start.line ||
123
+ (outer.start.line === inner.start.line &&
124
+ outer.start.column <= inner.start.column)) &&
125
+ (outer.end.line > inner.end.line ||
126
+ (outer.end.line === inner.end.line &&
127
+ outer.end.column >= inner.end.column));
128
+ const nestedContext = (context) => ({ ...context, depth: context.depth + 1 });
129
+ const uniqueCallableIds = (values) => [...new Set(values)].sort(compareCodePoints);
@@ -0,0 +1,269 @@
1
+ import * as t from "@babel/types";
2
+ import { reachSemanticLimit, semanticCallableIdForNode, semanticReferenceRole, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
3
+ import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
4
+ import { parameterBindings, resolveLocalCallables, } from "./javascriptSemanticCallResolution.js";
5
+ import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
6
+ import { range } from "./javascriptStaticAnalysisHelpers.js";
7
+ /** Recover bounded direct-call and lexical-capture candidates from inert syntax. */
8
+ export const collectJavaScriptSemanticCalls = (program, state, callables) => {
9
+ const output = {
10
+ callSites: [],
11
+ argumentFlows: [],
12
+ callReturnFlows: [],
13
+ callResultFlows: [],
14
+ closureCaptures: [],
15
+ frontiers: [],
16
+ retainedArguments: 0,
17
+ };
18
+ const callableById = new Map(callables.map((callable) => [callable.callableId, callable]));
19
+ const context = {
20
+ state,
21
+ callableById,
22
+ output,
23
+ bindingResolutionCache: new Map(),
24
+ parameterBindingCache: new Map(),
25
+ };
26
+ const callableStack = [];
27
+ traverseJavaScriptAst(program, {
28
+ enter: (node, parent) => {
29
+ const callableId = semanticCallableIdForNode(node);
30
+ const callable = callableId === null ? undefined : callableById.get(callableId);
31
+ if (callable !== undefined)
32
+ callableStack.push(callable);
33
+ const owner = enclosingFunction(callableStack);
34
+ collectCapture(node, parent, owner, context);
35
+ collectDynamicProperty(node, owner, state, output);
36
+ if (t.isCallExpression(node) ||
37
+ t.isOptionalCallExpression(node) ||
38
+ t.isNewExpression(node))
39
+ collectCallSite(node, parent, owner, context);
40
+ },
41
+ exit: (node) => {
42
+ const callableId = semanticCallableIdForNode(node);
43
+ if (callableId !== null &&
44
+ callableStack.at(-1)?.callableId === callableId)
45
+ callableStack.pop();
46
+ },
47
+ });
48
+ return {
49
+ callSites: output.callSites,
50
+ argumentFlows: output.argumentFlows,
51
+ callReturnFlows: output.callReturnFlows,
52
+ callResultFlows: output.callResultFlows,
53
+ closureCaptures: output.closureCaptures,
54
+ frontiers: output.frontiers,
55
+ };
56
+ };
57
+ const collectCallSite = (node, parent, owner, context) => {
58
+ const { state, callableById, output } = context;
59
+ if (output.callSites.length >= state.limits.maxCallSites) {
60
+ reachSemanticLimit(state, "maxCallSites");
61
+ return;
62
+ }
63
+ const resolution = resolveLocalCallables({
64
+ node: node.callee,
65
+ state,
66
+ callableById,
67
+ seenBindings: new Set(),
68
+ depth: 0,
69
+ bindingCache: context.bindingResolutionCache,
70
+ });
71
+ const callSiteId = semanticCallSiteId(node);
72
+ const argumentsValue = retainedArguments(node.arguments, state, output);
73
+ const site = {
74
+ callSiteId,
75
+ kind: t.isNewExpression(node) ? "construct" : "call",
76
+ callerCallableId: owner?.callableId ?? null,
77
+ location: range(node),
78
+ calleeLocation: range(node.callee),
79
+ resolution: resolution.callableIds.length === 1 && resolution.complete
80
+ ? "exact"
81
+ : resolution.callableIds.length > 0
82
+ ? "ambiguous"
83
+ : "unresolved",
84
+ calleeCallableIds: resolution.callableIds,
85
+ arguments: argumentsValue,
86
+ };
87
+ output.callSites.push(site);
88
+ collectCallResultFlow(site, node, parent, context);
89
+ if (site.resolution !== "exact")
90
+ addFrontier({
91
+ kind: "dynamic-call",
92
+ callableId: owner?.callableId ?? null,
93
+ location: range(node.callee),
94
+ reason: resolution.reason,
95
+ }, state, output);
96
+ collectArgumentFlows(site, node, context);
97
+ if (site.kind === "call")
98
+ collectReturnFlows(site, callableById, state, output);
99
+ };
100
+ const collectCallResultFlow = (site, node, parent, context) => {
101
+ const identifier = t.isVariableDeclarator(parent) &&
102
+ parent.init === node &&
103
+ t.isIdentifier(parent.id)
104
+ ? parent.id
105
+ : t.isAssignmentExpression(parent) &&
106
+ parent.right === node &&
107
+ t.isIdentifier(parent.left)
108
+ ? parent.left
109
+ : null;
110
+ if (identifier === null)
111
+ return;
112
+ const binding = resolveSemanticBindingState(context.state, identifier, identifier.name);
113
+ const identifierRange = range(identifier);
114
+ const definition = binding?.definitions.find(({ location }) => location.start.line === identifierRange.start.line &&
115
+ location.start.column === identifierRange.start.column &&
116
+ location.end.line === identifierRange.end.line &&
117
+ location.end.column === identifierRange.end.column);
118
+ if (binding === undefined || definition === undefined)
119
+ return;
120
+ context.output.callResultFlows.push({
121
+ callSiteId: site.callSiteId,
122
+ bindingId: binding.bindingId,
123
+ definitionLocation: definition.location,
124
+ });
125
+ };
126
+ const retainedArguments = (nodes, state, output) => {
127
+ const retained = [];
128
+ for (const [index, node] of nodes.entries()) {
129
+ if (output.retainedArguments >= state.limits.maxCallArguments) {
130
+ reachSemanticLimit(state, "maxCallArguments");
131
+ break;
132
+ }
133
+ output.retainedArguments += 1;
134
+ retained.push({
135
+ index,
136
+ location: range(node),
137
+ spread: t.isSpreadElement(node),
138
+ });
139
+ }
140
+ return retained;
141
+ };
142
+ const collectArgumentFlows = (site, node, context) => {
143
+ const { state, callableById, output } = context;
144
+ const retainedIndexes = new Set(site.arguments.map(({ index }) => index));
145
+ let positionIsExact = true;
146
+ for (const [index, argument] of node.arguments.entries()) {
147
+ if (t.isSpreadElement(argument)) {
148
+ positionIsExact = false;
149
+ continue;
150
+ }
151
+ if (!positionIsExact ||
152
+ !retainedIndexes.has(index) ||
153
+ !t.isExpression(argument))
154
+ continue;
155
+ for (const callableId of site.calleeCallableIds) {
156
+ const callable = callableById.get(callableId);
157
+ if (callable === undefined)
158
+ continue;
159
+ for (const parameter of cachedParameterBindings(callable, index, context)) {
160
+ if (output.argumentFlows.length >= state.limits.maxArgumentFlows) {
161
+ reachSemanticLimit(state, "maxArgumentFlows");
162
+ return;
163
+ }
164
+ const definition = parameter.definitions.find(({ kind }) => kind === "parameter");
165
+ if (definition === undefined)
166
+ continue;
167
+ output.argumentFlows.push({
168
+ callSiteId: site.callSiteId,
169
+ argumentIndex: index,
170
+ argumentLocation: range(argument),
171
+ callableId,
172
+ parameterBindingId: parameter.bindingId,
173
+ parameterLocation: definition.location,
174
+ });
175
+ }
176
+ }
177
+ }
178
+ };
179
+ const cachedParameterBindings = (callable, index, context) => {
180
+ const key = `${callable.callableId}:${String(index)}`;
181
+ const cached = context.parameterBindingCache.get(key);
182
+ if (cached !== undefined)
183
+ return cached;
184
+ const bindings = parameterBindings(callable, index, context.state);
185
+ context.parameterBindingCache.set(key, bindings);
186
+ return bindings;
187
+ };
188
+ const collectReturnFlows = (site, callableById, state, output) => {
189
+ for (const callableId of site.calleeCallableIds) {
190
+ const callable = callableById.get(callableId);
191
+ if (callable === undefined)
192
+ continue;
193
+ for (const returnSite of callable.returnSites) {
194
+ if (output.callReturnFlows.length >= state.limits.maxCallReturnFlows) {
195
+ reachSemanticLimit(state, "maxCallReturnFlows");
196
+ return;
197
+ }
198
+ output.callReturnFlows.push({
199
+ callSiteId: site.callSiteId,
200
+ callableId,
201
+ returnSiteId: returnSite.returnSiteId,
202
+ returnLocation: returnSite.location,
203
+ });
204
+ }
205
+ }
206
+ };
207
+ const collectCapture = (node, parent, owner, context) => {
208
+ const { state, output } = context;
209
+ if (!t.isIdentifier(node) ||
210
+ parent === null ||
211
+ owner === undefined ||
212
+ owner.bodyScopeId === null)
213
+ return;
214
+ if (semanticReferenceRole(node, parent) === null)
215
+ return;
216
+ const binding = resolveSemanticBindingState(state, node, node.name);
217
+ if (binding === undefined ||
218
+ bindingIsWithinCallable(binding, owner.bodyScopeId, state))
219
+ return;
220
+ if (output.closureCaptures.length >= state.limits.maxClosureCaptures) {
221
+ reachSemanticLimit(state, "maxClosureCaptures");
222
+ return;
223
+ }
224
+ output.closureCaptures.push({
225
+ callableId: owner.callableId,
226
+ bindingId: binding.bindingId,
227
+ referenceLocation: range(node),
228
+ });
229
+ };
230
+ const collectDynamicProperty = (node, owner, state, output) => {
231
+ if ((t.isMemberExpression(node) || t.isOptionalMemberExpression(node)) &&
232
+ semanticStaticPropertyName(node.property, node.computed) === "")
233
+ addFrontier({
234
+ kind: "dynamic-property",
235
+ callableId: owner?.callableId ?? null,
236
+ location: range(node.property),
237
+ reason: "Computed member property is not a static string or number.",
238
+ }, state, output);
239
+ else if ((t.isObjectProperty(node) || t.isObjectMethod(node)) &&
240
+ node.computed &&
241
+ semanticStaticPropertyName(node.key, true) === "")
242
+ addFrontier({
243
+ kind: "dynamic-property",
244
+ callableId: owner?.callableId ?? null,
245
+ location: range(node.key),
246
+ reason: "Computed object property is not a static string or number.",
247
+ }, state, output);
248
+ };
249
+ const addFrontier = (frontier, state, output) => {
250
+ if (output.frontiers.length >= state.limits.maxFrontiers) {
251
+ reachSemanticLimit(state, "maxFrontiers");
252
+ return;
253
+ }
254
+ output.frontiers.push(frontier);
255
+ };
256
+ const bindingIsWithinCallable = (binding, bodyScopeId, state) => {
257
+ let scope = state.scopesById.get(binding.scopeId);
258
+ while (scope !== undefined) {
259
+ if (scope.scopeId === bodyScopeId)
260
+ return true;
261
+ scope =
262
+ scope.parentScopeId === null
263
+ ? undefined
264
+ : state.scopesById.get(scope.parentScopeId);
265
+ }
266
+ return false;
267
+ };
268
+ const enclosingFunction = (stack) => stack.findLast(({ kind }) => kind !== "class");
269
+ const semanticCallSiteId = (node) => `call:${t.isNewExpression(node) ? "construct" : "call"}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`;