rea-agents 2.4.0 → 3.0.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.
- package/README.md +91 -19
- package/bridge/ghidra/ReaGhidraBridge.java +524 -4
- package/bridge/hopper_bridge.py +313 -28
- package/dist/application/AnalysisClientCleanup.js +14 -0
- package/dist/application/AnalysisContextQueries.js +130 -0
- package/dist/application/AnalysisSnapshotCache.js +4 -2
- package/dist/application/ApplicationWorkflowEvidenceResolver.js +31 -13
- package/dist/application/ArtifactInventory/scanCanonical.js +1 -1
- package/dist/application/BinarySession.js +20 -8
- package/dist/application/BinarySessionComposition.js +21 -0
- package/dist/application/BinarySessionRecords.js +79 -9
- package/dist/application/BinarySessionStatus.js +31 -1
- package/dist/application/BrowserEvidence.js +14 -0
- package/dist/application/BrowserScenarioCapturePort.js +1 -0
- package/dist/application/BrowserScenarioCaptureService.js +53 -0
- package/dist/application/BrowserScenarioEvidence.js +40 -0
- package/dist/application/CallPathTracing.js +160 -0
- package/dist/application/CapabilityInventory.js +84 -14
- package/dist/application/ClientRegistrationStatus.js +4 -1
- package/dist/application/CompositeProvider.js +10 -1
- package/dist/application/DirectAnalysis.js +13 -4
- package/dist/application/EnhancedLiteralTracing.js +118 -0
- package/dist/application/EnhancedTools.js +71 -127
- package/dist/application/EvidenceLedger.js +45 -16
- package/dist/application/JavaScriptApplicationWorkflowEvidence.js +13 -0
- package/dist/application/JavaScriptApplicationWorkflowService.js +27 -1
- package/dist/application/JavaScriptArtifactAnalysis.js +13 -4
- package/dist/application/JavaScriptRuntimeObservationEvidence.js +34 -0
- package/dist/application/JavaScriptRuntimeObservationPort.js +1 -0
- package/dist/application/JavaScriptRuntimeObservationService.js +46 -0
- package/dist/application/JavaScriptSemanticGraphAsyncProjection.js +147 -0
- package/dist/application/JavaScriptSemanticGraphBuilder.js +34 -6
- package/dist/application/JavaScriptSemanticGraphChildProcessProjection.js +156 -0
- package/dist/application/JavaScriptSemanticGraphConstruction.js +2 -2
- package/dist/application/JavaScriptSemanticGraphDataProjection.js +203 -0
- package/dist/application/JavaScriptSemanticGraphFingerprintProjection.js +25 -0
- package/dist/application/JavaScriptSemanticGraphFlowProjection.js +129 -8
- package/dist/application/JavaScriptSemanticGraphObjectProjection.js +81 -0
- package/dist/application/JavaScriptSemanticGraphProjection.js +14 -7
- package/dist/application/JavaScriptSemanticGraphResourceProjection.js +59 -0
- package/dist/application/JavaScriptSemanticGraphValueProjection.js +77 -0
- package/dist/application/LazyAnalysisProvider.js +77 -0
- package/dist/application/LoopbackReplay.js +1 -1
- package/dist/application/NativeApiInspection.js +80 -0
- package/dist/application/ProcessReactiveEffects.js +6 -0
- package/dist/application/ProcessReactiveHarness.js +16 -1
- package/dist/application/ProjectPermissionStore.js +1 -0
- package/dist/application/ReconstructionObligationAuthority.js +72 -0
- package/dist/application/ReconstructionObligationCandidates.js +265 -0
- package/dist/application/ReconstructionObligationLedgerDiagnostics.js +47 -0
- package/dist/application/ReconstructionObligationLedgerEvaluation.js +263 -0
- package/dist/application/ReconstructionObligationLedgerProjection.js +86 -0
- package/dist/application/ReconstructionObligationLedgerService.js +72 -0
- package/dist/application/ReconstructionObligationOriginalCases.js +33 -0
- package/dist/application/ReconstructionObligationPolicies.js +132 -0
- package/dist/application/ReconstructionReadinessService.js +48 -0
- package/dist/application/SessionProviderRouter.js +6 -6
- package/dist/application/SetupClientConfiguration.js +4 -2
- package/dist/application/runtime.js +26 -9
- package/dist/artifacts/ArtifactProvider.js +87 -22
- package/dist/browser/BrowserScenarioSecrets.js +53 -0
- package/dist/browser/BrowserScenarioSessionPort.js +1 -0
- package/dist/browser/CdpBrowserProvider.js +2 -2
- package/dist/browser/CdpCaptureDocuments.js +57 -0
- package/dist/browser/CdpCaptureStorage.js +71 -29
- package/dist/browser/CdpCaptureStorageFingerprints.js +328 -0
- package/dist/browser/CdpEndpoint.js +8 -6
- package/dist/browser/CdpPageCapture.js +2 -0
- package/dist/browser/JavaScriptRuntimeScope.js +38 -0
- package/dist/browser/PlaywrightBrowserScenarioProvider.js +259 -0
- package/dist/browser/PlaywrightExecutionBoundary.js +42 -0
- package/dist/browser/PlaywrightScenarioActions.js +62 -0
- package/dist/browser/PlaywrightScenarioArtifacts.js +238 -0
- package/dist/browser/PlaywrightScenarioBrowser.js +144 -0
- package/dist/browser/PlaywrightScenarioEvents.js +301 -0
- package/dist/browser/PlaywrightScenarioSession.js +300 -0
- package/dist/browser/V8InspectorCaptureProjection.js +110 -0
- package/dist/browser/V8InspectorEndpoint.js +72 -0
- package/dist/browser/V8InspectorProvider.js +267 -0
- package/dist/catalogIdentity.js +11 -3
- package/dist/cli/artifactCommands.js +44 -3
- package/dist/cli/coreAnalysisCommands.js +78 -0
- package/dist/cli.js +4 -0
- package/dist/cliApplicationCommands.js +30 -2
- package/dist/cliBrowserAdvancedCommands.js +17 -5
- package/dist/cliBrowserCommands.js +1 -0
- package/dist/cliBrowserScenarioCommands.js +43 -0
- package/dist/cliCommandNames.js +9 -0
- package/dist/cliJavaScriptRuntimeCommands.js +164 -0
- package/dist/cliObservationOptions.js +4 -0
- package/dist/cliPolicyCommands.js +1 -0
- package/dist/config/environment.js +12 -0
- package/dist/config/parseConfig.js +88 -17
- package/dist/contracts/analysisSearchContract.js +14 -0
- package/dist/contracts/applicationToolContracts.js +86 -2
- package/dist/contracts/applicationWorkflowInputContracts.js +18 -0
- package/dist/contracts/artifactToolContracts.js +29 -2
- package/dist/contracts/browserProviderToolContracts.js +7 -0
- package/dist/contracts/browserScenarioToolContracts.js +81 -0
- package/dist/contracts/browserToolContracts.js +88 -98
- package/dist/contracts/enhancedInputs.js +35 -6
- package/dist/contracts/functionInstructionContract.js +8 -0
- package/dist/contracts/functionWorkflowToolContracts.js +30 -0
- package/dist/contracts/hopperMemoryContracts.js +22 -0
- package/dist/contracts/javascriptApplicationWorkflowExamples.js +42 -0
- package/dist/contracts/javascriptRuntimeObservationToolContracts.js +58 -0
- package/dist/contracts/promptContracts.js +14 -15
- package/dist/contracts/reconstructionReadinessExample.js +436 -0
- package/dist/contracts/toolContractExamples.js +8 -0
- package/dist/contracts/toolContractTypes.js +1 -0
- package/dist/contracts/toolContracts.js +34 -25
- package/dist/contracts/toolEffects.js +25 -0
- package/dist/contracts/toolOutputSchemaGroups.js +120 -30
- package/dist/contracts/toolOutputSchemaPrimitives.js +37 -0
- package/dist/domain/analysisErrorPresentation.js +8 -2
- package/dist/domain/analysisErrorProjection.js +9 -0
- package/dist/domain/artifactInspection.js +347 -0
- package/dist/domain/browserCaptureComparison.js +82 -0
- package/dist/domain/browserObservation.js +8 -1
- package/dist/domain/browserObservationErrors.js +1 -0
- package/dist/domain/browserObservationSchemas.js +26 -0
- package/dist/domain/browserScenario.js +165 -0
- package/dist/domain/browserScenarioCapture.js +57 -0
- package/dist/domain/browserScenarioCaptureValues.js +204 -0
- package/dist/domain/browserScenarioDiff.js +324 -0
- package/dist/domain/browserScenarioDiffValues.js +157 -0
- package/dist/domain/browserScenarioNormalization.js +45 -0
- package/dist/domain/browserScenarioValues.js +372 -0
- package/dist/domain/bytecodeProvider.js +179 -0
- package/dist/domain/conformancePackage.js +169 -0
- package/dist/domain/conformanceReplay.js +104 -0
- package/dist/domain/conformanceTrustGate.js +193 -0
- package/dist/domain/customProtocolCapture.js +137 -0
- package/dist/domain/eventProcessTree.js +214 -0
- package/dist/domain/evidenceBundle.js +47 -0
- package/dist/domain/hopperStartupFailure.js +1 -1
- package/dist/domain/hopperValues.js +14 -1
- package/dist/domain/inputIssueProjection.js +7 -1
- package/dist/domain/javascriptRuntimeObservation.js +170 -0
- package/dist/domain/javascriptRuntimeReconciliationParsing.js +120 -2
- package/dist/domain/javascriptRuntimeReconciliationResult.js +3 -3
- package/dist/domain/javascriptRuntimeReconciliationRuntime.js +2 -2
- package/dist/domain/javascriptRuntimeReconciliationSchemas.js +1 -1
- package/dist/domain/javascriptSemanticAnalysis.js +13 -16
- package/dist/domain/javascriptSemanticAsyncEffects.js +253 -0
- package/dist/domain/javascriptSemanticChildProcesses.js +237 -0
- package/dist/domain/javascriptSemanticDataEffectHelpers.js +125 -0
- package/dist/domain/javascriptSemanticDataEffects.js +284 -0
- package/dist/domain/javascriptSemanticDerivedAnalysis.js +41 -0
- package/dist/domain/javascriptSemanticFingerprints.js +174 -0
- package/dist/domain/javascriptSemanticIr.js +21 -1
- package/dist/domain/javascriptSemanticObjects.js +115 -0
- package/dist/domain/javascriptSemanticPromises.js +290 -0
- package/dist/domain/javascriptSemanticResources.js +136 -0
- package/dist/domain/javascriptSourceParser.js +15 -0
- package/dist/domain/javascriptStaticAnalysis.js +12 -15
- package/dist/domain/javascriptStaticAnalysisBundler.js +38 -0
- package/dist/domain/javascriptStaticAnalysisCalls.js +23 -1
- package/dist/domain/javascriptStaticAnalysisHelpers.js +8 -0
- package/dist/domain/mobileApplicationInvestigation.js +159 -0
- package/dist/domain/nativeApiBoundary.js +130 -0
- package/dist/domain/objcSwiftMetadata.js +150 -0
- package/dist/domain/packageAnalysis.js +123 -0
- package/dist/domain/peInspection.js +196 -0
- package/dist/domain/processCapture.js +8 -2
- package/dist/domain/processReactiveScenario.js +1 -0
- package/dist/domain/processReactiveTransition.js +9 -4
- package/dist/domain/protocolCapture.js +243 -0
- package/dist/domain/providerCleanupError.js +11 -0
- package/dist/domain/reconstructionObligationLedgerSchemas.js +278 -0
- package/dist/domain/reconstructionReadiness.js +121 -0
- package/dist/domain/reconstructionReadinessFindingHelpers.js +17 -0
- package/dist/domain/reconstructionReadinessIntegrityFindings.js +118 -0
- package/dist/domain/reconstructionReadinessJourneyFindings.js +193 -0
- package/dist/domain/reconstructionReadinessSchemas.js +240 -0
- package/dist/domain/sourceToBundleComparison.js +335 -0
- package/dist/domain/sourceToBundleComparisonSchemas.js +133 -0
- package/dist/domain/sourceToBundleSignals.js +202 -0
- package/dist/domain/webCaptureDiff.js +141 -27
- package/dist/domain/webScreenshot.js +1 -1
- package/dist/dotnet/ManagedStaticProvider.js +34 -5
- package/dist/generatedMcpToolCatalog.js +37 -0
- package/dist/generatedPackageMetadata.js +4 -4
- package/dist/ghidra/GhidraDefaults.js +1 -1
- package/dist/ghidra/GhidraFunctionValues.js +40 -2
- package/dist/ghidra/GhidraProviderCapabilities.js +47 -38
- package/dist/hopper/HopperCleanup.js +158 -0
- package/dist/hopper/HopperClient.js +93 -99
- package/dist/hopper/HopperProvider.js +61 -30
- package/dist/hopper/HopperRequestQueue.js +215 -0
- package/dist/hopper/HopperResponseStream.js +46 -0
- package/dist/hopper/protocol.js +63 -20
- package/dist/main/transport.js +24 -27
- package/dist/main.js +14 -6
- package/dist/mcpDoctor.js +27 -4
- package/dist/mcpStartupPolicy.js +14 -0
- package/dist/process/ProcessCommandIdentity.js +19 -0
- package/dist/process/ProcessOwnership.js +11 -8
- package/dist/server/DynamicToolAvailability.js +70 -0
- package/dist/server/LazyToolCatalog.js +134 -0
- package/dist/server/createServer.js +92 -154
- package/dist/server/hydrateServerTools.js +191 -0
- package/dist/server/lifecycleProgress.js +10 -0
- package/dist/server/permissionFailure.js +12 -0
- package/dist/server/registerApplicationTools/compareSourceToBundle.js +35 -0
- package/dist/server/registerApplicationTools/helpers.js +11 -7
- package/dist/server/registerApplicationTools/obligationLedger.js +27 -0
- package/dist/server/registerApplicationTools/readiness.js +55 -0
- package/dist/server/registerApplicationTools.js +6 -0
- package/dist/server/registerArtifactTools.js +9 -1
- package/dist/server/registerBrowserScenarioTool.js +25 -0
- package/dist/server/registerBrowserTools.js +2 -2
- package/dist/server/registerCloseLifecycleTool.js +57 -0
- package/dist/server/registerEnhancedTools.js +28 -7
- package/dist/server/registerEvidenceResources.js +54 -101
- package/dist/server/registerEvidenceTools.js +9 -2
- package/dist/server/registerInvestigationTools.js +1 -2
- package/dist/server/registerJavaScriptApplicationGraphResource.js +0 -4
- package/dist/server/registerJavaScriptRuntimeObservationTools.js +37 -0
- package/dist/server/registerReconstructionObligationLedgerResource.js +32 -0
- package/dist/server/registerReconstructionReadinessResource.js +32 -0
- package/dist/server/registerSessionRecordTools.js +82 -12
- package/dist/server/registerSessionStatusTool.js +11 -8
- package/dist/server/registerSessionTools.js +32 -58
- package/dist/server/sessionAvailabilityPolicy.js +10 -8
- package/dist/server/toolRegistrationOptions.js +21 -6
- package/dist/server/toolResult.js +12 -0
- package/package.json +28 -11
- package/scripts/package-runner-bootstrap.mjs +72 -0
- package/scripts/rea.mjs +11 -1
- package/skills/reverse-engineer-anything/SKILL.md +10 -3
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { collectJavaScriptSemanticAsyncEffects } from "./javascriptSemanticAsyncEffects.js";
|
|
2
|
+
import { collectJavaScriptSemanticCalls } from "./javascriptSemanticCalls.js";
|
|
3
|
+
import { collectJavaScriptSemanticChildProcesses } from "./javascriptSemanticChildProcesses.js";
|
|
4
|
+
import { collectJavaScriptSemanticFingerprints } from "./javascriptSemanticFingerprints.js";
|
|
5
|
+
import { collectJavaScriptSemanticDataEffects } from "./javascriptSemanticDataEffects.js";
|
|
6
|
+
import { collectJavaScriptSemanticPromises } from "./javascriptSemanticPromises.js";
|
|
7
|
+
import { collectJavaScriptSemanticObjects } from "./javascriptSemanticObjects.js";
|
|
8
|
+
import { collectJavaScriptSemanticResources } from "./javascriptSemanticResources.js";
|
|
9
|
+
/** Compose bounded semantic passes after lexical definitions are available. */
|
|
10
|
+
export const collectJavaScriptDerivedSemantics = (program, state, callables, parserPartial) => {
|
|
11
|
+
const calls = collectJavaScriptSemanticCalls(program, state, callables);
|
|
12
|
+
const promiseOperations = collectJavaScriptSemanticPromises(program, state, callables);
|
|
13
|
+
const asyncEffects = collectJavaScriptSemanticAsyncEffects(program, state, callables);
|
|
14
|
+
const childProcesses = collectJavaScriptSemanticChildProcesses(program, state, callables);
|
|
15
|
+
const dataEffects = collectJavaScriptSemanticDataEffects(program, state, callables);
|
|
16
|
+
const resourceOperations = collectJavaScriptSemanticResources(program, state, callables);
|
|
17
|
+
const objectOperations = collectJavaScriptSemanticObjects(program, state, callables);
|
|
18
|
+
const functionFingerprints = collectJavaScriptSemanticFingerprints({
|
|
19
|
+
state,
|
|
20
|
+
callables,
|
|
21
|
+
calls,
|
|
22
|
+
promises: promiseOperations,
|
|
23
|
+
events: asyncEffects.eventOperations,
|
|
24
|
+
timers: asyncEffects.timerOperations,
|
|
25
|
+
childProcessSpawns: childProcesses.childProcessSpawns,
|
|
26
|
+
childProcessInteractions: childProcesses.childProcessInteractions,
|
|
27
|
+
requestOperations: dataEffects.requestOperations,
|
|
28
|
+
resourceOperations,
|
|
29
|
+
parserPartial,
|
|
30
|
+
});
|
|
31
|
+
return {
|
|
32
|
+
...calls,
|
|
33
|
+
promiseOperations,
|
|
34
|
+
...asyncEffects,
|
|
35
|
+
...childProcesses,
|
|
36
|
+
...dataEffects,
|
|
37
|
+
resourceOperations,
|
|
38
|
+
objectOperations,
|
|
39
|
+
functionFingerprints,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import * as t from "@babel/types";
|
|
3
|
+
import { semanticCallableIdForNode } from "./javascriptSemanticProjection.js";
|
|
4
|
+
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
|
5
|
+
import { compareCodePoints } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
/** Build bounded rename- and formatting-resistant components per callable. */
|
|
7
|
+
export const collectJavaScriptSemanticFingerprints = (input) => {
|
|
8
|
+
const { state, callables, calls, promises, events, timers, childProcessSpawns, childProcessInteractions, requestOperations, resourceOperations, parserPartial, } = input;
|
|
9
|
+
return callables.flatMap((callable) => {
|
|
10
|
+
if (callable.kind === "class")
|
|
11
|
+
return [];
|
|
12
|
+
const node = state.callableNodesById.get(callable.callableId);
|
|
13
|
+
if (node === undefined)
|
|
14
|
+
return [];
|
|
15
|
+
const syntax = fingerprintSyntax(node, callable.callableId);
|
|
16
|
+
const promiseEffects = promises.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
|
|
17
|
+
const eventEffects = events.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
|
|
18
|
+
const timerEffects = timers.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
|
|
19
|
+
const childProcessEffects = [
|
|
20
|
+
...childProcessSpawns,
|
|
21
|
+
...childProcessInteractions,
|
|
22
|
+
].filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
|
|
23
|
+
const networkEffects = requestOperations.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
|
|
24
|
+
const resourceEffects = resourceOperations.filter(({ ownerCallableId }) => ownerCallableId === callable.callableId);
|
|
25
|
+
return [
|
|
26
|
+
{
|
|
27
|
+
callableId: callable.callableId,
|
|
28
|
+
status: parserPartial ||
|
|
29
|
+
state.limitsReached.size > 0 ||
|
|
30
|
+
callable.returnCoverage.status !== "complete"
|
|
31
|
+
? "partial"
|
|
32
|
+
: "complete",
|
|
33
|
+
components: {
|
|
34
|
+
parameterArity: callableParameterArity(node),
|
|
35
|
+
normalizedAstSha256: digest(syntax.normalizedTokens),
|
|
36
|
+
controlFlowSha256: digest(syntax.controlFlowTokens),
|
|
37
|
+
relationShapeSha256: digest(relationShape(callable, calls, promiseEffects)),
|
|
38
|
+
literalSetSha256: digest([...new Set(syntax.literals)].sort(compareCodePoints)),
|
|
39
|
+
effects: [
|
|
40
|
+
...(isAsyncCallable(node) ? ["async"] : []),
|
|
41
|
+
...(childProcessEffects.length > 0
|
|
42
|
+
? ["child-process"]
|
|
43
|
+
: []),
|
|
44
|
+
...(eventEffects.length > 0 ? ["event"] : []),
|
|
45
|
+
...(networkEffects.length > 0 ? ["network"] : []),
|
|
46
|
+
...(promiseEffects.length > 0 ? ["promise"] : []),
|
|
47
|
+
...(resourceEffects.length > 0 ? ["resource"] : []),
|
|
48
|
+
...(timerEffects.length > 0 ? ["timer"] : []),
|
|
49
|
+
],
|
|
50
|
+
},
|
|
51
|
+
limitations: [
|
|
52
|
+
"The fingerprint is a bounded static candidate and does not prove behavioral equivalence.",
|
|
53
|
+
...(parserPartial || state.limitsReached.size > 0
|
|
54
|
+
? ["Incomplete semantic recovery makes this fingerprint partial."]
|
|
55
|
+
: []),
|
|
56
|
+
],
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
});
|
|
60
|
+
};
|
|
61
|
+
const fingerprintSyntax = (root, targetCallableId) => {
|
|
62
|
+
const normalizedTokens = [];
|
|
63
|
+
const controlFlowTokens = [];
|
|
64
|
+
const literals = [];
|
|
65
|
+
const callableStack = [];
|
|
66
|
+
traverseJavaScriptAst(root, {
|
|
67
|
+
enter: (node, parent) => {
|
|
68
|
+
const callableId = semanticCallableIdForNode(node);
|
|
69
|
+
if (callableId !== null)
|
|
70
|
+
callableStack.push(callableId);
|
|
71
|
+
if (callableStack.at(-1) !== targetCallableId)
|
|
72
|
+
return;
|
|
73
|
+
normalizedTokens.push(normalizedNodeToken(node, parent));
|
|
74
|
+
const control = controlFlowToken(node);
|
|
75
|
+
if (control !== null)
|
|
76
|
+
controlFlowTokens.push(control);
|
|
77
|
+
const literal = semanticLiteral(node);
|
|
78
|
+
if (literal !== null)
|
|
79
|
+
literals.push(literal);
|
|
80
|
+
},
|
|
81
|
+
exit: (node) => {
|
|
82
|
+
const callableId = semanticCallableIdForNode(node);
|
|
83
|
+
if (callableId !== null && callableStack.at(-1) === callableId)
|
|
84
|
+
callableStack.pop();
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
return { normalizedTokens, controlFlowTokens, literals };
|
|
88
|
+
};
|
|
89
|
+
const normalizedNodeToken = (node, parent) => {
|
|
90
|
+
if (t.isIdentifier(node))
|
|
91
|
+
return propertyIdentity(node, parent)
|
|
92
|
+
? `property:${node.name}`
|
|
93
|
+
: "identifier";
|
|
94
|
+
if (t.isStringLiteral(node))
|
|
95
|
+
return propertyIdentity(node, parent)
|
|
96
|
+
? `property:${node.value}`
|
|
97
|
+
: "string-literal";
|
|
98
|
+
const attributes = [
|
|
99
|
+
primitiveAttribute(node, "operator"),
|
|
100
|
+
primitiveAttribute(node, "kind"),
|
|
101
|
+
primitiveAttribute(node, "computed"),
|
|
102
|
+
primitiveAttribute(node, "optional"),
|
|
103
|
+
primitiveAttribute(node, "async"),
|
|
104
|
+
primitiveAttribute(node, "generator"),
|
|
105
|
+
].filter((value) => value !== null);
|
|
106
|
+
return [node.type, ...attributes].join(":");
|
|
107
|
+
};
|
|
108
|
+
const primitiveAttribute = (node, key) => {
|
|
109
|
+
const value = Reflect.get(node, key);
|
|
110
|
+
return typeof value === "string" ||
|
|
111
|
+
typeof value === "number" ||
|
|
112
|
+
typeof value === "boolean"
|
|
113
|
+
? `${key}=${String(value)}`
|
|
114
|
+
: null;
|
|
115
|
+
};
|
|
116
|
+
const propertyIdentity = (node, parent) => {
|
|
117
|
+
if (parent === null)
|
|
118
|
+
return false;
|
|
119
|
+
if ((t.isMemberExpression(parent) || t.isOptionalMemberExpression(parent)) &&
|
|
120
|
+
parent.property === node)
|
|
121
|
+
return !parent.computed || t.isStringLiteral(node);
|
|
122
|
+
if ((t.isObjectProperty(parent) ||
|
|
123
|
+
t.isObjectMethod(parent) ||
|
|
124
|
+
t.isClassMethod(parent) ||
|
|
125
|
+
t.isClassProperty(parent)) &&
|
|
126
|
+
parent.key === node)
|
|
127
|
+
return !parent.computed || t.isStringLiteral(node);
|
|
128
|
+
return false;
|
|
129
|
+
};
|
|
130
|
+
const controlFlowToken = (node) => {
|
|
131
|
+
if (!t.isIfStatement(node) &&
|
|
132
|
+
!t.isConditionalExpression(node) &&
|
|
133
|
+
!t.isSwitchStatement(node) &&
|
|
134
|
+
!t.isSwitchCase(node) &&
|
|
135
|
+
!t.isLoop(node) &&
|
|
136
|
+
!t.isTryStatement(node) &&
|
|
137
|
+
!t.isCatchClause(node) &&
|
|
138
|
+
!t.isReturnStatement(node) &&
|
|
139
|
+
!t.isThrowStatement(node) &&
|
|
140
|
+
!t.isAwaitExpression(node) &&
|
|
141
|
+
!t.isYieldExpression(node) &&
|
|
142
|
+
!t.isLogicalExpression(node))
|
|
143
|
+
return null;
|
|
144
|
+
const operator = primitiveAttribute(node, "operator");
|
|
145
|
+
return operator === null ? node.type : `${node.type}:${operator}`;
|
|
146
|
+
};
|
|
147
|
+
const semanticLiteral = (node) => {
|
|
148
|
+
if (t.isStringLiteral(node))
|
|
149
|
+
return `string:${node.value}`;
|
|
150
|
+
if (t.isNumericLiteral(node))
|
|
151
|
+
return `number:${String(node.value)}`;
|
|
152
|
+
if (t.isBooleanLiteral(node))
|
|
153
|
+
return `boolean:${String(node.value)}`;
|
|
154
|
+
if (t.isNullLiteral(node))
|
|
155
|
+
return "null";
|
|
156
|
+
if (t.isBigIntLiteral(node))
|
|
157
|
+
return `bigint:${node.value}`;
|
|
158
|
+
if (t.isRegExpLiteral(node))
|
|
159
|
+
return `regexp:${node.pattern}/${node.flags}`;
|
|
160
|
+
if (t.isTemplateElement(node))
|
|
161
|
+
return `template:${node.value.cooked ?? node.value.raw}`;
|
|
162
|
+
return null;
|
|
163
|
+
};
|
|
164
|
+
const relationShape = (callable, calls, promises) => [
|
|
165
|
+
`returns:${String(callable.returnSites.length)}:${callable.returnCoverage.status}`,
|
|
166
|
+
...calls.callSites
|
|
167
|
+
.filter(({ callerCallableId }) => callerCallableId === callable.callableId)
|
|
168
|
+
.map(({ kind, resolution, arguments: args, calleeCallableIds }) => `call:${kind}:${resolution}:${String(args.length)}:${String(calleeCallableIds.length)}`),
|
|
169
|
+
`captures:${String(calls.closureCaptures.filter(({ callableId }) => callableId === callable.callableId).length)}`,
|
|
170
|
+
...promises.map(({ kind, method, ownership, sourceResolution }) => `promise:${kind}:${method}:${ownership}:${sourceResolution}`),
|
|
171
|
+
];
|
|
172
|
+
const callableParameterArity = (node) => t.isFunction(node) ? node.params.length : 0;
|
|
173
|
+
const isAsyncCallable = (node) => t.isFunction(node) && node.async;
|
|
174
|
+
const digest = (value) => createHash("sha256").update(JSON.stringify(value)).digest("hex");
|
|
@@ -11,6 +11,15 @@ export const DEFAULT_JAVASCRIPT_SEMANTIC_LIMITS = {
|
|
|
11
11
|
maxArgumentFlows: 100_000,
|
|
12
12
|
maxCallReturnFlows: 100_000,
|
|
13
13
|
maxClosureCaptures: 100_000,
|
|
14
|
+
maxPromiseOperations: 100_000,
|
|
15
|
+
maxEventOperations: 100_000,
|
|
16
|
+
maxTimerOperations: 100_000,
|
|
17
|
+
maxChildProcessOperations: 100_000,
|
|
18
|
+
maxConfigurationOperations: 100_000,
|
|
19
|
+
maxRequestOperations: 100_000,
|
|
20
|
+
maxBoundaryOperations: 100_000,
|
|
21
|
+
maxResourceOperations: 100_000,
|
|
22
|
+
maxObjectOperations: 100_000,
|
|
14
23
|
maxFrontiers: 20_000,
|
|
15
24
|
maxValueDepth: 16,
|
|
16
25
|
maxUnionValues: 32,
|
|
@@ -23,7 +32,7 @@ export const semanticBinding = (ir, bindingId) => ir.bindings.find(({ bindingId:
|
|
|
23
32
|
/** Fail-closed result when Babel cannot produce an inert syntax tree. */
|
|
24
33
|
export const failedJavaScriptSemanticIr = () => ({
|
|
25
34
|
schema: "JavaScriptSemanticIR",
|
|
26
|
-
schemaVersion:
|
|
35
|
+
schemaVersion: 4,
|
|
27
36
|
scopes: [],
|
|
28
37
|
bindings: [],
|
|
29
38
|
callables: [],
|
|
@@ -34,6 +43,17 @@ export const failedJavaScriptSemanticIr = () => ({
|
|
|
34
43
|
callReturnFlows: [],
|
|
35
44
|
callResultFlows: [],
|
|
36
45
|
closureCaptures: [],
|
|
46
|
+
promiseOperations: [],
|
|
47
|
+
eventOperations: [],
|
|
48
|
+
timerOperations: [],
|
|
49
|
+
childProcessSpawns: [],
|
|
50
|
+
childProcessInteractions: [],
|
|
51
|
+
configurationOperations: [],
|
|
52
|
+
requestOperations: [],
|
|
53
|
+
boundaryOperations: [],
|
|
54
|
+
resourceOperations: [],
|
|
55
|
+
objectOperations: [],
|
|
56
|
+
functionFingerprints: [],
|
|
37
57
|
frontiers: [],
|
|
38
58
|
coverage: { status: "failed", omittedCount: null, limitsReached: [] },
|
|
39
59
|
limitations: [
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { reachSemanticLimit, semanticCallableIdForNode, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
|
|
3
|
+
import { resolveSemanticBindingState, } from "./javascriptSemanticState.js";
|
|
4
|
+
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
|
5
|
+
import { propertyName, range } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
/** Recover bounded static property reads, writes, spreads, and destructuring. */
|
|
7
|
+
export const collectJavaScriptSemanticObjects = (program, state, callables) => {
|
|
8
|
+
const output = [];
|
|
9
|
+
const callableIds = new Set(callables.map(({ callableId }) => callableId));
|
|
10
|
+
const callableStack = [];
|
|
11
|
+
const context = { state, output };
|
|
12
|
+
traverseJavaScriptAst(program, {
|
|
13
|
+
enter: (node, parent) => {
|
|
14
|
+
const callableId = semanticCallableIdForNode(node);
|
|
15
|
+
if (callableId !== null && callableIds.has(callableId))
|
|
16
|
+
callableStack.push(callableId);
|
|
17
|
+
const ownerCallableId = callableStack.at(-1) ?? null;
|
|
18
|
+
if (t.isMemberExpression(node) || t.isOptionalMemberExpression(node))
|
|
19
|
+
collectMember(node, parent, ownerCallableId, context);
|
|
20
|
+
else if (t.isSpreadElement(node) &&
|
|
21
|
+
parent !== null &&
|
|
22
|
+
t.isObjectExpression(parent))
|
|
23
|
+
addObjectOperation({
|
|
24
|
+
node,
|
|
25
|
+
kind: "spread",
|
|
26
|
+
ownerCallableId,
|
|
27
|
+
objectBindingId: expressionBindingId(node.argument, state),
|
|
28
|
+
targetBindingId: null,
|
|
29
|
+
propertyName: null,
|
|
30
|
+
}, state, output);
|
|
31
|
+
else if (t.isVariableDeclarator(node) && t.isObjectPattern(node.id))
|
|
32
|
+
collectDestructuring(node, ownerCallableId, state, output);
|
|
33
|
+
},
|
|
34
|
+
exit: (node) => {
|
|
35
|
+
const callableId = semanticCallableIdForNode(node);
|
|
36
|
+
if (callableId !== null && callableStack.at(-1) === callableId)
|
|
37
|
+
callableStack.pop();
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
return output;
|
|
41
|
+
};
|
|
42
|
+
const collectMember = (node, parent, ownerCallableId, context) => {
|
|
43
|
+
const { state, output } = context;
|
|
44
|
+
const name = semanticStaticPropertyName(node.property, node.computed);
|
|
45
|
+
if (name.length === 0)
|
|
46
|
+
return;
|
|
47
|
+
const write = (t.isAssignmentExpression(parent) && parent.left === node) ||
|
|
48
|
+
(t.isUpdateExpression(parent) && parent.argument === node);
|
|
49
|
+
addObjectOperation({
|
|
50
|
+
node,
|
|
51
|
+
kind: write ? "write" : "read",
|
|
52
|
+
ownerCallableId,
|
|
53
|
+
objectBindingId: expressionBindingId(node.object, state),
|
|
54
|
+
targetBindingId: null,
|
|
55
|
+
propertyName: name,
|
|
56
|
+
}, state, output);
|
|
57
|
+
};
|
|
58
|
+
const collectDestructuring = (node, ownerCallableId, state, output) => {
|
|
59
|
+
if (!t.isObjectPattern(node.id))
|
|
60
|
+
return;
|
|
61
|
+
const objectBindingId = expressionBindingId(node.init, state);
|
|
62
|
+
for (const property of node.id.properties) {
|
|
63
|
+
if (!t.isObjectProperty(property))
|
|
64
|
+
continue;
|
|
65
|
+
const name = propertyName(property.key);
|
|
66
|
+
if (name.length === 0)
|
|
67
|
+
continue;
|
|
68
|
+
const target = bindingIdentifier(property.value);
|
|
69
|
+
addObjectOperation({
|
|
70
|
+
node: property,
|
|
71
|
+
kind: "destructure",
|
|
72
|
+
ownerCallableId,
|
|
73
|
+
objectBindingId,
|
|
74
|
+
targetBindingId: target === null
|
|
75
|
+
? null
|
|
76
|
+
: (resolveSemanticBindingState(state, target, target.name)
|
|
77
|
+
?.bindingId ?? null),
|
|
78
|
+
propertyName: name,
|
|
79
|
+
}, state, output);
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
const addObjectOperation = (input, state, output) => {
|
|
83
|
+
if (output.length >= state.limits.maxObjectOperations) {
|
|
84
|
+
reachSemanticLimit(state, "maxObjectOperations");
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
output.push({
|
|
88
|
+
objectOperationId: `object:${input.kind}:${String(input.node.start ?? -1)}:${String(input.node.end ?? -1)}`,
|
|
89
|
+
kind: input.kind,
|
|
90
|
+
location: range(input.node),
|
|
91
|
+
ownerCallableId: input.ownerCallableId,
|
|
92
|
+
objectBindingId: input.objectBindingId,
|
|
93
|
+
targetBindingId: input.targetBindingId,
|
|
94
|
+
propertyName: input.propertyName,
|
|
95
|
+
resolution: input.kind === "spread"
|
|
96
|
+
? input.objectBindingId === null
|
|
97
|
+
? "partial"
|
|
98
|
+
: "complete"
|
|
99
|
+
: input.objectBindingId === null ||
|
|
100
|
+
input.propertyName === null ||
|
|
101
|
+
(input.kind === "destructure" && input.targetBindingId === null)
|
|
102
|
+
? "partial"
|
|
103
|
+
: "complete",
|
|
104
|
+
});
|
|
105
|
+
};
|
|
106
|
+
const expressionBindingId = (node, state) => t.isIdentifier(node)
|
|
107
|
+
? (resolveSemanticBindingState(state, node, node.name)?.bindingId ?? null)
|
|
108
|
+
: null;
|
|
109
|
+
const bindingIdentifier = (node) => {
|
|
110
|
+
if (t.isIdentifier(node))
|
|
111
|
+
return node;
|
|
112
|
+
if (t.isAssignmentPattern(node) && t.isIdentifier(node.left))
|
|
113
|
+
return node.left;
|
|
114
|
+
return null;
|
|
115
|
+
};
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
import * as t from "@babel/types";
|
|
2
|
+
import { reachSemanticLimit, semanticCallableIdForNode, semanticStaticPropertyName, } from "./javascriptSemanticProjection.js";
|
|
3
|
+
import { resolveSemanticBindingState, semanticResolutionBlocked, } from "./javascriptSemanticState.js";
|
|
4
|
+
import { traverseJavaScriptAst } from "./javascriptSemanticTraversal.js";
|
|
5
|
+
import { compareCodePoints, range, sourceRangesEqual, } from "./javascriptStaticAnalysisHelpers.js";
|
|
6
|
+
/** Recover explicit Promise construction, chaining, aggregation, and ownership. */
|
|
7
|
+
export const collectJavaScriptSemanticPromises = (program, state, callables) => {
|
|
8
|
+
const candidates = collectCandidates(program, state, callables);
|
|
9
|
+
const candidateByNode = new WeakMap();
|
|
10
|
+
for (const candidate of candidates)
|
|
11
|
+
candidateByNode.set(candidate.node, candidate);
|
|
12
|
+
return candidates.map((candidate) => {
|
|
13
|
+
const ownership = promiseOwnership(candidate, candidateByNode, state, callables);
|
|
14
|
+
const sources = promiseSources(candidate, candidateByNode, state);
|
|
15
|
+
return {
|
|
16
|
+
promiseId: candidate.promiseId,
|
|
17
|
+
kind: candidate.kind,
|
|
18
|
+
method: candidate.method,
|
|
19
|
+
location: range(candidate.node),
|
|
20
|
+
ownerCallableId: candidate.ownerCallableId,
|
|
21
|
+
...ownership,
|
|
22
|
+
sourcePromiseIds: [...new Set(sources.promiseIds)].sort(compareCodePoints),
|
|
23
|
+
sourceResolution: sources.status,
|
|
24
|
+
};
|
|
25
|
+
});
|
|
26
|
+
};
|
|
27
|
+
const collectCandidates = (program, state, callables) => {
|
|
28
|
+
const output = [];
|
|
29
|
+
const admittedCallables = new Set(callables.map(({ callableId }) => callableId));
|
|
30
|
+
const callableStack = [];
|
|
31
|
+
const ancestors = [];
|
|
32
|
+
traverseJavaScriptAst(program, {
|
|
33
|
+
enter: (node) => {
|
|
34
|
+
const callableId = semanticCallableIdForNode(node);
|
|
35
|
+
if (callableId !== null && admittedCallables.has(callableId))
|
|
36
|
+
callableStack.push(callableId);
|
|
37
|
+
const details = promiseCandidateDetails(node, state);
|
|
38
|
+
if (details !== null) {
|
|
39
|
+
if (output.length >= state.limits.maxPromiseOperations)
|
|
40
|
+
reachSemanticLimit(state, "maxPromiseOperations");
|
|
41
|
+
else
|
|
42
|
+
output.push({
|
|
43
|
+
node,
|
|
44
|
+
...details,
|
|
45
|
+
promiseId: semanticPromiseId(details.kind, node),
|
|
46
|
+
ownerCallableId: callableStack.at(-1) ?? null,
|
|
47
|
+
ancestors: [...ancestors],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
ancestors.push(node);
|
|
51
|
+
},
|
|
52
|
+
exit: (node) => {
|
|
53
|
+
ancestors.pop();
|
|
54
|
+
const callableId = semanticCallableIdForNode(node);
|
|
55
|
+
if (callableId !== null && callableStack.at(-1) === callableId)
|
|
56
|
+
callableStack.pop();
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
return output;
|
|
60
|
+
};
|
|
61
|
+
const promiseCandidateDetails = (node, state) => {
|
|
62
|
+
if (t.isNewExpression(node) &&
|
|
63
|
+
t.isIdentifier(node.callee, { name: "Promise" }) &&
|
|
64
|
+
isUnshadowedGlobalPromise(node.callee, state))
|
|
65
|
+
return { kind: "constructor", method: "new" };
|
|
66
|
+
if (t.isCallExpression(node) || t.isOptionalCallExpression(node)) {
|
|
67
|
+
const member = memberCallee(node);
|
|
68
|
+
if (member === null)
|
|
69
|
+
return null;
|
|
70
|
+
const method = semanticStaticPropertyName(member.property, member.computed);
|
|
71
|
+
if (t.isIdentifier(member.object, { name: "Promise" }) &&
|
|
72
|
+
isUnshadowedGlobalPromise(member.object, state)) {
|
|
73
|
+
if (method === "all" || method === "allSettled")
|
|
74
|
+
return { kind: "aggregate", method };
|
|
75
|
+
if (method === "resolve" || method === "reject")
|
|
76
|
+
return { kind: "static", method };
|
|
77
|
+
}
|
|
78
|
+
if (method === "then" || method === "catch" || method === "finally")
|
|
79
|
+
return { kind: "chain", method };
|
|
80
|
+
}
|
|
81
|
+
if (t.isAwaitExpression(node) && !isPromiseProducer(node.argument, state))
|
|
82
|
+
return { kind: "awaited-expression", method: "await" };
|
|
83
|
+
return null;
|
|
84
|
+
};
|
|
85
|
+
const isUnshadowedGlobalPromise = (node, state) => resolveSemanticBindingState(state, node, "Promise") === undefined &&
|
|
86
|
+
!semanticResolutionBlocked(state, node, "Promise");
|
|
87
|
+
const isPromiseProducer = (node, state) => promiseCandidateDetails(unwrapExpression(node), state) !== null;
|
|
88
|
+
const semanticPromiseId = (kind, node) => `promise:${kind}:${String(node.start ?? -1)}:${String(node.end ?? -1)}`;
|
|
89
|
+
const memberCallee = (node) => t.isMemberExpression(node.callee) || t.isOptionalMemberExpression(node.callee)
|
|
90
|
+
? node.callee
|
|
91
|
+
: null;
|
|
92
|
+
const promiseOwnership = (candidate, candidateByNode, state, callables) => {
|
|
93
|
+
if (candidate.kind === "awaited-expression")
|
|
94
|
+
return emptyOwnership("awaited");
|
|
95
|
+
const context = {
|
|
96
|
+
candidate,
|
|
97
|
+
candidateByNode,
|
|
98
|
+
state,
|
|
99
|
+
callables,
|
|
100
|
+
};
|
|
101
|
+
for (let index = candidate.ancestors.length - 1; index >= 0; index -= 1) {
|
|
102
|
+
const ancestor = candidate.ancestors[index];
|
|
103
|
+
if (ancestor === undefined)
|
|
104
|
+
continue;
|
|
105
|
+
const ownership = ownershipAtAncestor(ancestor, context);
|
|
106
|
+
if (ownership === "boundary")
|
|
107
|
+
break;
|
|
108
|
+
if (ownership !== null)
|
|
109
|
+
return ownership;
|
|
110
|
+
}
|
|
111
|
+
return emptyOwnership("unknown");
|
|
112
|
+
};
|
|
113
|
+
const ownershipAtAncestor = (ancestor, context) => {
|
|
114
|
+
const { candidate, candidateByNode, state, callables } = context;
|
|
115
|
+
if (t.isAwaitExpression(ancestor))
|
|
116
|
+
return emptyOwnership("awaited");
|
|
117
|
+
const outer = candidateByNode.get(ancestor);
|
|
118
|
+
if (outer !== undefined &&
|
|
119
|
+
outer.ownerCallableId === candidate.ownerCallableId &&
|
|
120
|
+
outerConsumesCandidate(outer, candidate))
|
|
121
|
+
return outer.kind === "chain"
|
|
122
|
+
? emptyOwnership("chained")
|
|
123
|
+
: emptyOwnership("aggregated");
|
|
124
|
+
const assigned = assignedPromiseBinding(ancestor, state);
|
|
125
|
+
if (assigned !== undefined)
|
|
126
|
+
return {
|
|
127
|
+
ownership: "assigned",
|
|
128
|
+
ownerBindingId: assigned,
|
|
129
|
+
returnSiteId: null,
|
|
130
|
+
};
|
|
131
|
+
if (t.isReturnStatement(ancestor))
|
|
132
|
+
return returnedPromiseOwnership(candidate.ownerCallableId, range(ancestor), callables);
|
|
133
|
+
if (t.isExpressionStatement(ancestor))
|
|
134
|
+
return emptyOwnership("detached");
|
|
135
|
+
const callableId = semanticCallableIdForNode(ancestor);
|
|
136
|
+
if (callableId === null || callableId !== candidate.ownerCallableId)
|
|
137
|
+
return null;
|
|
138
|
+
return t.isArrowFunctionExpression(ancestor) &&
|
|
139
|
+
ancestor.body === candidate.node
|
|
140
|
+
? returnedPromiseOwnership(candidate.ownerCallableId, range(candidate.node), callables)
|
|
141
|
+
: "boundary";
|
|
142
|
+
};
|
|
143
|
+
const assignedPromiseBinding = (ancestor, state) => {
|
|
144
|
+
if (t.isVariableDeclarator(ancestor) &&
|
|
145
|
+
ancestor.init !== null &&
|
|
146
|
+
t.isIdentifier(ancestor.id))
|
|
147
|
+
return (resolveSemanticBindingState(state, ancestor.id, ancestor.id.name)
|
|
148
|
+
?.bindingId ?? null);
|
|
149
|
+
if (t.isAssignmentExpression(ancestor) && t.isIdentifier(ancestor.left))
|
|
150
|
+
return (resolveSemanticBindingState(state, ancestor.left, ancestor.left.name)
|
|
151
|
+
?.bindingId ?? null);
|
|
152
|
+
return undefined;
|
|
153
|
+
};
|
|
154
|
+
const returnedPromiseOwnership = (callableId, location, callables) => ({
|
|
155
|
+
ownership: "returned",
|
|
156
|
+
ownerBindingId: null,
|
|
157
|
+
returnSiteId: matchingReturnSite(callableId, location, callables),
|
|
158
|
+
});
|
|
159
|
+
const outerConsumesCandidate = (outer, candidate) => {
|
|
160
|
+
if (outer.kind === "chain" &&
|
|
161
|
+
(t.isCallExpression(outer.node) || t.isOptionalCallExpression(outer.node))) {
|
|
162
|
+
const member = memberCallee(outer.node);
|
|
163
|
+
return member !== null && containsNode(member.object, candidate.node);
|
|
164
|
+
}
|
|
165
|
+
if (outer.kind === "aggregate" &&
|
|
166
|
+
(t.isCallExpression(outer.node) || t.isOptionalCallExpression(outer.node))) {
|
|
167
|
+
const input = outer.node.arguments[0];
|
|
168
|
+
return t.isNode(input) && containsNode(input, candidate.node);
|
|
169
|
+
}
|
|
170
|
+
return false;
|
|
171
|
+
};
|
|
172
|
+
const containsNode = (outer, inner) => outer.start !== null &&
|
|
173
|
+
outer.start !== undefined &&
|
|
174
|
+
outer.end !== null &&
|
|
175
|
+
outer.end !== undefined &&
|
|
176
|
+
inner.start !== null &&
|
|
177
|
+
inner.start !== undefined &&
|
|
178
|
+
inner.end !== null &&
|
|
179
|
+
inner.end !== undefined &&
|
|
180
|
+
outer.start <= inner.start &&
|
|
181
|
+
outer.end >= inner.end;
|
|
182
|
+
const emptyOwnership = (ownership) => ({
|
|
183
|
+
ownership,
|
|
184
|
+
ownerBindingId: null,
|
|
185
|
+
returnSiteId: null,
|
|
186
|
+
});
|
|
187
|
+
const matchingReturnSite = (callableId, location, callables) => callables
|
|
188
|
+
.find(({ callableId: candidate }) => candidate === callableId)
|
|
189
|
+
?.returnSites.find(({ location: candidate }) => sourceRangesEqual(candidate, location))?.returnSiteId ?? null;
|
|
190
|
+
const promiseSources = (candidate, candidateByNode, state) => {
|
|
191
|
+
if (candidate.kind === "chain") {
|
|
192
|
+
const member = t.isCallExpression(candidate.node) ||
|
|
193
|
+
t.isOptionalCallExpression(candidate.node)
|
|
194
|
+
? memberCallee(candidate.node)
|
|
195
|
+
: null;
|
|
196
|
+
return member === null
|
|
197
|
+
? unresolvedPromise()
|
|
198
|
+
: resolvePromiseExpression(member.object, promiseResolutionContext(candidate, candidateByNode, state));
|
|
199
|
+
}
|
|
200
|
+
if (candidate.kind === "aggregate")
|
|
201
|
+
return aggregateSources(candidate, candidateByNode, state);
|
|
202
|
+
if (candidate.kind === "awaited-expression")
|
|
203
|
+
return t.isAwaitExpression(candidate.node)
|
|
204
|
+
? resolvePromiseExpression(candidate.node.argument, promiseResolutionContext(candidate, candidateByNode, state))
|
|
205
|
+
: unresolvedPromise();
|
|
206
|
+
return { promiseIds: [], status: "complete" };
|
|
207
|
+
};
|
|
208
|
+
const aggregateSources = (candidate, candidateByNode, state) => {
|
|
209
|
+
if (!t.isCallExpression(candidate.node) &&
|
|
210
|
+
!t.isOptionalCallExpression(candidate.node))
|
|
211
|
+
return unresolvedPromise();
|
|
212
|
+
const input = candidate.node.arguments[0];
|
|
213
|
+
if (!t.isArrayExpression(input))
|
|
214
|
+
return unresolvedPromise();
|
|
215
|
+
const resolutions = input.elements.map((element) => {
|
|
216
|
+
if (element === null)
|
|
217
|
+
return completePromise();
|
|
218
|
+
if (t.isSpreadElement(element))
|
|
219
|
+
return unresolvedPromise();
|
|
220
|
+
if (isObviouslyNonPromise(element))
|
|
221
|
+
return completePromise();
|
|
222
|
+
return resolvePromiseExpression(element, promiseResolutionContext(candidate, candidateByNode, state));
|
|
223
|
+
});
|
|
224
|
+
return combinePromiseResolutions(resolutions);
|
|
225
|
+
};
|
|
226
|
+
const resolvePromiseExpression = (rawNode, context) => {
|
|
227
|
+
const { current, candidateByNode, state, seenBindings, depth } = context;
|
|
228
|
+
if (depth >= state.limits.maxValueDepth)
|
|
229
|
+
return unresolvedPromise();
|
|
230
|
+
const node = unwrapExpression(rawNode);
|
|
231
|
+
const direct = candidateByNode.get(node);
|
|
232
|
+
if (direct !== undefined && direct.promiseId !== current.promiseId)
|
|
233
|
+
return { promiseIds: [direct.promiseId], status: "complete" };
|
|
234
|
+
if (!t.isIdentifier(node))
|
|
235
|
+
return unresolvedPromise();
|
|
236
|
+
const binding = resolveSemanticBindingState(state, node, node.name);
|
|
237
|
+
if (binding === undefined ||
|
|
238
|
+
binding.mutable ||
|
|
239
|
+
binding.initializers.length !== 1 ||
|
|
240
|
+
seenBindings.has(binding.bindingId))
|
|
241
|
+
return unresolvedPromise();
|
|
242
|
+
const initializer = binding.initializers[0];
|
|
243
|
+
if (initializer === undefined || initializer.projection.length > 0)
|
|
244
|
+
return unresolvedPromise();
|
|
245
|
+
const nestedSeen = new Set(seenBindings);
|
|
246
|
+
nestedSeen.add(binding.bindingId);
|
|
247
|
+
return resolvePromiseExpression(initializer.node, {
|
|
248
|
+
...context,
|
|
249
|
+
seenBindings: nestedSeen,
|
|
250
|
+
depth: depth + 1,
|
|
251
|
+
});
|
|
252
|
+
};
|
|
253
|
+
const promiseResolutionContext = (current, candidateByNode, state) => ({
|
|
254
|
+
current,
|
|
255
|
+
candidateByNode,
|
|
256
|
+
state,
|
|
257
|
+
seenBindings: new Set(),
|
|
258
|
+
depth: 0,
|
|
259
|
+
});
|
|
260
|
+
const unwrapExpression = (node) => {
|
|
261
|
+
if (t.isParenthesizedExpression(node) ||
|
|
262
|
+
t.isTSAsExpression(node) ||
|
|
263
|
+
t.isTSSatisfiesExpression(node) ||
|
|
264
|
+
t.isTSNonNullExpression(node) ||
|
|
265
|
+
t.isTypeCastExpression(node))
|
|
266
|
+
return unwrapExpression(node.expression);
|
|
267
|
+
return node;
|
|
268
|
+
};
|
|
269
|
+
const combinePromiseResolutions = (resolutions) => ({
|
|
270
|
+
promiseIds: resolutions.flatMap(({ promiseIds }) => promiseIds),
|
|
271
|
+
status: resolutions.some(({ status }) => status === "unresolved")
|
|
272
|
+
? resolutions.some(({ status }) => status === "complete")
|
|
273
|
+
? "partial"
|
|
274
|
+
: "unresolved"
|
|
275
|
+
: resolutions.some(({ status }) => status === "partial")
|
|
276
|
+
? "partial"
|
|
277
|
+
: "complete",
|
|
278
|
+
});
|
|
279
|
+
const completePromise = () => ({
|
|
280
|
+
promiseIds: [],
|
|
281
|
+
status: "complete",
|
|
282
|
+
});
|
|
283
|
+
const unresolvedPromise = () => ({
|
|
284
|
+
promiseIds: [],
|
|
285
|
+
status: "unresolved",
|
|
286
|
+
});
|
|
287
|
+
const isObviouslyNonPromise = (node) => t.isLiteral(node) ||
|
|
288
|
+
t.isObjectExpression(node) ||
|
|
289
|
+
t.isFunction(node) ||
|
|
290
|
+
t.isClassExpression(node);
|