@distrohelena/canton-explorer 0.1.5 → 0.1.6
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 +32 -0
- package/dist/public/assets/DebuggerView-Mk6TEUJB.js +5 -0
- package/dist/public/assets/abap-08VXUWAP.js +1 -0
- package/dist/public/assets/apex-BWPQTe0t.js +1 -0
- package/dist/public/assets/azcli-Bc_sGQ0U.js +1 -0
- package/dist/public/assets/bat-i0X4ZdIN.js +1 -0
- package/dist/public/assets/bicep-B5-_aFwp.js +2 -0
- package/dist/public/assets/cameligo-DMUM7wLl.js +1 -0
- package/dist/public/assets/clojure-Cm7r79vr.js +1 -0
- package/dist/public/assets/codicon-ngg6Pgfi.ttf +0 -0
- package/dist/public/assets/coffee-Ba7i2nA0.js +1 -0
- package/dist/public/assets/cpp-C7h46wYY.js +1 -0
- package/dist/public/assets/csharp-BKxtCVv1.js +1 -0
- package/dist/public/assets/csp-bTuwJoIa.js +1 -0
- package/dist/public/assets/css-DIMkf-bt.js +3 -0
- package/dist/public/assets/cssMode-Ce-rlOrP.js +1 -0
- package/dist/public/assets/cypher-CVaqCwHa.js +1 -0
- package/dist/public/assets/dart-onAF5SnQ.js +1 -0
- package/dist/public/assets/dockerfile-DZFCIeNp.js +1 -0
- package/dist/public/assets/ecl-D05T4iGw.js +1 -0
- package/dist/public/assets/editor-Br_kD0ds.css +1 -0
- package/dist/public/assets/editor.api2-7H_iQTr0.js +872 -0
- package/dist/public/assets/elixir-6RTg0lbw.js +1 -0
- package/dist/public/assets/flow9-C5_-GSwl.js +1 -0
- package/dist/public/assets/freemarker2-B-DvnnHs.js +3 -0
- package/dist/public/assets/fsharp-C8Ef5oNN.js +1 -0
- package/dist/public/assets/go-C-y9NEjX.js +1 -0
- package/dist/public/assets/graphql-fmXr3nnJ.js +1 -0
- package/dist/public/assets/handlebars-CME0Fldi.js +1 -0
- package/dist/public/assets/hcl-CpzslTdj.js +1 -0
- package/dist/public/assets/html-Beb3lxWO.js +1 -0
- package/dist/public/assets/htmlMode-CAgp27Dj.js +1 -0
- package/dist/public/assets/index-CucRixYI.css +1 -0
- package/dist/public/assets/{index-BmT5Hrud.js → index-Dn15ZYDU.js} +1 -1
- package/dist/public/assets/ini-sBoK_t0W.js +1 -0
- package/dist/public/assets/java-BEtHBSE6.js +1 -0
- package/dist/public/assets/javascript-DdcWrUzt.js +1 -0
- package/dist/public/assets/jsonMode-BCddWWWg.js +7 -0
- package/dist/public/assets/julia-Bri6UV-V.js +1 -0
- package/dist/public/assets/kotlin-BOotOW0E.js +1 -0
- package/dist/public/assets/less-B9JPFI3C.js +2 -0
- package/dist/public/assets/lexon-CfSJPG6W.js +1 -0
- package/dist/public/assets/liquid-DmF9SgC3.js +1 -0
- package/dist/public/assets/lspLanguageFeatures-D7zEWtOG.js +4 -0
- package/dist/public/assets/lua-CsQS60Ue.js +1 -0
- package/dist/public/assets/m3-D-oSqn_W.js +1 -0
- package/dist/public/assets/markdown-Cimd5fb3.js +1 -0
- package/dist/public/assets/mdx-PwCfoB0S.js +1 -0
- package/dist/public/assets/mips-CIPQ_RoX.js +1 -0
- package/dist/public/assets/monaco-runtime-Bkgn_wG8.js +6 -0
- package/dist/public/assets/monaco.contribution-B2Ohh6Li.js +2 -0
- package/dist/public/assets/msdax-DauUninz.js +1 -0
- package/dist/public/assets/mysql-SOo6toE5.js +1 -0
- package/dist/public/assets/objective-c-FvmIjYaQ.js +1 -0
- package/dist/public/assets/pascal-DrH0SRf2.js +1 -0
- package/dist/public/assets/pascaligo-D-ptJ9y-.js +1 -0
- package/dist/public/assets/perl-oz_6vUea.js +1 -0
- package/dist/public/assets/pgsql-DTj74zXo.js +1 -0
- package/dist/public/assets/php-nr791fC2.js +1 -0
- package/dist/public/assets/pla-CopQ2nXW.js +1 -0
- package/dist/public/assets/postiats-43DmfD33.js +1 -0
- package/dist/public/assets/powerquery-D3hlyOfw.js +1 -0
- package/dist/public/assets/powershell-DmHpPYUd.js +1 -0
- package/dist/public/assets/protobuf-C531GsRP.js +2 -0
- package/dist/public/assets/pug-Z5eAx3Zn.js +1 -0
- package/dist/public/assets/python-pS1EqvyQ.js +1 -0
- package/dist/public/assets/qsharp-DkqhCAOL.js +1 -0
- package/dist/public/assets/r-BwWrilGY.js +1 -0
- package/dist/public/assets/razor-CZ2fK_eC.js +1 -0
- package/dist/public/assets/redis-ClamHrr6.js +1 -0
- package/dist/public/assets/redshift-DT7zqm-g.js +1 -0
- package/dist/public/assets/restructuredtext-BYgofb2h.js +1 -0
- package/dist/public/assets/ruby-DezsRK8O.js +1 -0
- package/dist/public/assets/rust-DdL9SqIa.js +1 -0
- package/dist/public/assets/sb-CcwsVR0C.js +1 -0
- package/dist/public/assets/scala-DHpiXF5c.js +1 -0
- package/dist/public/assets/scheme-BeGwcela.js +1 -0
- package/dist/public/assets/scss-gp-XZpBa.js +3 -0
- package/dist/public/assets/shell-CC2rA5mh.js +1 -0
- package/dist/public/assets/solidity-BEEn4gHE.js +1 -0
- package/dist/public/assets/sophia-CRfGWb83.js +1 -0
- package/dist/public/assets/sparql-D_Lu-MrJ.js +1 -0
- package/dist/public/assets/sql-NEE52Syq.js +1 -0
- package/dist/public/assets/st-DbInun42.js +1 -0
- package/dist/public/assets/swift-Bxkupp3x.js +1 -0
- package/dist/public/assets/systemverilog-Bz4Y3fRF.js +1 -0
- package/dist/public/assets/tcl-DISqw1ZD.js +1 -0
- package/dist/public/assets/tsMode-06H9OqyC.js +11 -0
- package/dist/public/assets/twig-De2hgUGE.js +1 -0
- package/dist/public/assets/typescript-DS7X1pib.js +1 -0
- package/dist/public/assets/typespec-B8J7ngcE.js +1 -0
- package/dist/public/assets/vb-DV3o63ZY.js +1 -0
- package/dist/public/assets/wgsl-DpFanUEy.js +298 -0
- package/dist/public/assets/workers-D4g7l69J.js +1 -0
- package/dist/public/assets/xml-Br0rHxWi.js +1 -0
- package/dist/public/assets/yaml-qASkMRyN.js +1 -0
- package/dist/public/index.html +2 -2
- package/dist/src/debugger/debugger.service.d.ts +2 -0
- package/dist/src/debugger/debugger.service.js +2 -0
- package/dist/src/debugger/debugger.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -6
- package/scripts/build-debug-dar.mjs +277 -0
- package/scripts/generate-daml-source-map.mjs +1512 -0
- package/scripts/prepare-debug-dars-from-workspace.mjs +181 -0
- package/dist/public/assets/DebuggerView-VieEr0b3.js +0 -2
- package/dist/public/assets/index-DnEheBxd.css +0 -1
- package/dist/public/assets/monaco-runtime-C5uazxST.css +0 -1
- package/dist/public/assets/monaco-runtime-DiIHw4v2.js +0 -820
|
@@ -0,0 +1,1512 @@
|
|
|
1
|
+
import { readFileSync, readdirSync, statSync, writeFileSync } from 'node:fs'
|
|
2
|
+
import path from 'node:path'
|
|
3
|
+
|
|
4
|
+
async function main() {
|
|
5
|
+
const options = parseArgs(process.argv.slice(2))
|
|
6
|
+
|
|
7
|
+
if (options.help) {
|
|
8
|
+
printHelp()
|
|
9
|
+
return
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
validateOptions(options)
|
|
13
|
+
|
|
14
|
+
const sdk = await import('@distrohelena/canton-typescript-sdk/daml-lf')
|
|
15
|
+
const archiveLoader = new sdk.DarArchiveLoader()
|
|
16
|
+
const packageLoader = new sdk.DamlLfPackageLoader()
|
|
17
|
+
const archive = await archiveLoader.loadDarOrThrowAsync(readFileSync(options.inputDar))
|
|
18
|
+
const rawPackage = packageLoader.loadRawPackageOrThrow(archive.mainPackageEntry.bytes)
|
|
19
|
+
const compiledPackage = packageLoader.loadPackageOrThrow(archive.mainPackageEntry.bytes)
|
|
20
|
+
const debugDalfBytes = resolveDebugDalfBytes(options, archive)
|
|
21
|
+
const debugPackage = debugDalfBytes
|
|
22
|
+
? packageLoader.loadPackageOrThrow(debugDalfBytes)
|
|
23
|
+
: undefined
|
|
24
|
+
|
|
25
|
+
const packageDirectory = resolvePackageDirectory(
|
|
26
|
+
options.workspaceRoot,
|
|
27
|
+
options.packageDir,
|
|
28
|
+
compiledPackage.packageName,
|
|
29
|
+
)
|
|
30
|
+
const damlYaml = parseDamlYaml(path.join(packageDirectory, 'daml.yaml'))
|
|
31
|
+
const sourceDirectory = path.resolve(packageDirectory, damlYaml.source)
|
|
32
|
+
const archiveSourcePrefix = normalizeArchivePath(options.sourcePrefix || damlYaml.source)
|
|
33
|
+
const sourceModules = collectSourceModules(
|
|
34
|
+
sourceDirectory,
|
|
35
|
+
archiveSourcePrefix,
|
|
36
|
+
options.workspaceRoot,
|
|
37
|
+
)
|
|
38
|
+
const compiledModules = compiledPackage.modules.map((module) => {
|
|
39
|
+
const sourceModule = sourceModules.get(module.name)
|
|
40
|
+
const definitions = module.definitions.filter((definition) => definition.nodeKind === 'valueDefinition')
|
|
41
|
+
return {
|
|
42
|
+
moduleName: module.name,
|
|
43
|
+
sourceModule,
|
|
44
|
+
definitions,
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
const executables = []
|
|
49
|
+
let exactMatches = 0
|
|
50
|
+
let fallbackMatches = 0
|
|
51
|
+
let missingModules = 0
|
|
52
|
+
|
|
53
|
+
for (const compiledModule of compiledModules) {
|
|
54
|
+
if (compiledModule.sourceModule === undefined) {
|
|
55
|
+
missingModules += 1
|
|
56
|
+
continue
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (const definition of compiledModule.definitions) {
|
|
60
|
+
const mapping = resolveDefinitionMapping(definition.name, compiledModule.sourceModule)
|
|
61
|
+
|
|
62
|
+
if (mapping.kind === 'exact') {
|
|
63
|
+
exactMatches += 1
|
|
64
|
+
} else {
|
|
65
|
+
fallbackMatches += 1
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
executables.push({
|
|
69
|
+
packageId: compiledPackage.packageId,
|
|
70
|
+
moduleName: compiledModule.moduleName,
|
|
71
|
+
definitionName: definition.name,
|
|
72
|
+
path: compiledModule.sourceModule.archivePath,
|
|
73
|
+
startLine: mapping.span.startLine,
|
|
74
|
+
startColumn: mapping.span.startColumn,
|
|
75
|
+
endLine: mapping.span.endLine,
|
|
76
|
+
endColumn: mapping.span.endColumn,
|
|
77
|
+
precision: mapping.kind,
|
|
78
|
+
...(mapping.entrypointKind ? { entrypointKind: mapping.entrypointKind } : {}),
|
|
79
|
+
...(mapping.templateName ? { templateName: mapping.templateName } : {}),
|
|
80
|
+
...(mapping.choiceName ? { choiceName: mapping.choiceName } : {}),
|
|
81
|
+
...(mapping.choiceArgumentFieldName
|
|
82
|
+
? { choiceArgumentFieldName: mapping.choiceArgumentFieldName }
|
|
83
|
+
: {}),
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const importedPackages = await collectImportedPackageIds(
|
|
89
|
+
rawPackage.rawPackage,
|
|
90
|
+
compiledPackage,
|
|
91
|
+
options.workspaceImportsOnly
|
|
92
|
+
? await collectWorkspacePackageIds(options.workspaceRoot, options.packageDir)
|
|
93
|
+
: undefined,
|
|
94
|
+
)
|
|
95
|
+
const expressionLocationResult = debugPackage
|
|
96
|
+
? collectExpressionLocations(compiledPackage, debugPackage, sourceModules)
|
|
97
|
+
: { locations: [], skippedDefinitions: [] }
|
|
98
|
+
const expressionLocations = expressionLocationResult.locations
|
|
99
|
+
const interfaceChoiceExecutables = collectInterfaceChoiceExecutables(
|
|
100
|
+
compiledPackage.packageId,
|
|
101
|
+
compiledModules,
|
|
102
|
+
executables,
|
|
103
|
+
expressionLocations,
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
const metadata = {
|
|
107
|
+
packageId: compiledPackage.packageId,
|
|
108
|
+
importedPackages,
|
|
109
|
+
executables: [...executables, ...interfaceChoiceExecutables],
|
|
110
|
+
expressionLocations,
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
writeFileSync(options.outputPath, JSON.stringify(metadata, null, 2))
|
|
114
|
+
|
|
115
|
+
console.log(`Wrote source map: ${options.outputPath}`)
|
|
116
|
+
console.log(`Package: ${compiledPackage.packageName}@${compiledPackage.packageVersion}`)
|
|
117
|
+
console.log(`Definitions mapped: ${executables.length}`)
|
|
118
|
+
console.log(`Exact matches: ${exactMatches}`)
|
|
119
|
+
console.log(`Module fallbacks: ${fallbackMatches}`)
|
|
120
|
+
if (interfaceChoiceExecutables.length > 0) {
|
|
121
|
+
console.log(`Interface choice executables: ${interfaceChoiceExecutables.length}`)
|
|
122
|
+
}
|
|
123
|
+
console.log(`Expression locations: ${expressionLocations.length}`)
|
|
124
|
+
if (expressionLocationResult.skippedDefinitions.length > 0) {
|
|
125
|
+
console.log(
|
|
126
|
+
`Skipped non-isomorphic debug definitions: ${expressionLocationResult.skippedDefinitions.length}`,
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
if (missingModules > 0) {
|
|
130
|
+
console.log(`Source modules not found: ${missingModules}`)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function parseArgs(argv) {
|
|
135
|
+
const options = {
|
|
136
|
+
help: false,
|
|
137
|
+
inputDar: '',
|
|
138
|
+
outputPath: '',
|
|
139
|
+
workspaceRoot: '',
|
|
140
|
+
packageDir: '',
|
|
141
|
+
sourcePrefix: '',
|
|
142
|
+
debugDalfPath: '',
|
|
143
|
+
workspaceImportsOnly: false,
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
147
|
+
const argument = argv[index]
|
|
148
|
+
|
|
149
|
+
switch (argument) {
|
|
150
|
+
case '--help':
|
|
151
|
+
case '-h':
|
|
152
|
+
options.help = true
|
|
153
|
+
break
|
|
154
|
+
case '--input':
|
|
155
|
+
options.inputDar = requireValue(argv, ++index, '--input')
|
|
156
|
+
break
|
|
157
|
+
case '--output':
|
|
158
|
+
options.outputPath = requireValue(argv, ++index, '--output')
|
|
159
|
+
break
|
|
160
|
+
case '--workspace-root':
|
|
161
|
+
options.workspaceRoot = requireValue(argv, ++index, '--workspace-root')
|
|
162
|
+
break
|
|
163
|
+
case '--package-dir':
|
|
164
|
+
options.packageDir = requireValue(argv, ++index, '--package-dir')
|
|
165
|
+
break
|
|
166
|
+
case '--source-prefix':
|
|
167
|
+
options.sourcePrefix = requireValue(argv, ++index, '--source-prefix')
|
|
168
|
+
break
|
|
169
|
+
case '--debug-dalf':
|
|
170
|
+
options.debugDalfPath = requireValue(argv, ++index, '--debug-dalf')
|
|
171
|
+
break
|
|
172
|
+
case '--workspace-imports-only':
|
|
173
|
+
options.workspaceImportsOnly = true
|
|
174
|
+
break
|
|
175
|
+
default:
|
|
176
|
+
throw new Error(`Unknown argument: ${argument}`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
if (!options.outputPath && options.inputDar) {
|
|
181
|
+
options.outputPath = defaultOutputPath(options.inputDar)
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return options
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function validateOptions(options) {
|
|
188
|
+
if (!options.inputDar) {
|
|
189
|
+
throw new Error('--input is required')
|
|
190
|
+
}
|
|
191
|
+
if (!options.workspaceRoot && !options.packageDir) {
|
|
192
|
+
throw new Error('--workspace-root is required unless --package-dir is provided')
|
|
193
|
+
}
|
|
194
|
+
if (!statExists(options.inputDar, 'file')) {
|
|
195
|
+
throw new Error(`Input DAR not found: ${options.inputDar}`)
|
|
196
|
+
}
|
|
197
|
+
if (options.workspaceRoot && !statExists(options.workspaceRoot, 'directory')) {
|
|
198
|
+
throw new Error(`Workspace root not found: ${options.workspaceRoot}`)
|
|
199
|
+
}
|
|
200
|
+
if (options.packageDir && !statExists(options.packageDir, 'directory')) {
|
|
201
|
+
throw new Error(`Package directory not found: ${options.packageDir}`)
|
|
202
|
+
}
|
|
203
|
+
if (options.debugDalfPath && !statExists(options.debugDalfPath, 'file')) {
|
|
204
|
+
throw new Error(`Debug DALF not found: ${options.debugDalfPath}`)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function resolveDebugDalfBytes(options, archive) {
|
|
209
|
+
if (options.debugDalfPath) {
|
|
210
|
+
return readFileSync(options.debugDalfPath)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const debugEntry = archive.entries.find(
|
|
214
|
+
(entry) =>
|
|
215
|
+
(
|
|
216
|
+
entry.path.startsWith('debug/') ||
|
|
217
|
+
entry.path.endsWith('/data/debug-locations.dalf')
|
|
218
|
+
) &&
|
|
219
|
+
entry.path.endsWith('.dalf'),
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
return debugEntry?.bytes
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function collectExpressionLocations(productionPackage, debugPackage, sourceModules) {
|
|
226
|
+
const locations = []
|
|
227
|
+
const skippedDefinitions = []
|
|
228
|
+
const debugModules = new Map(debugPackage.modules.map((module) => [module.name, module]))
|
|
229
|
+
const packageIdEquivalence = new Map([
|
|
230
|
+
[productionPackage.packageId, debugPackage.packageId],
|
|
231
|
+
[debugPackage.packageId, productionPackage.packageId],
|
|
232
|
+
])
|
|
233
|
+
|
|
234
|
+
for (const productionModule of productionPackage.modules) {
|
|
235
|
+
const debugModule = debugModules.get(productionModule.name)
|
|
236
|
+
|
|
237
|
+
if (debugModule === undefined) {
|
|
238
|
+
throw new Error(`debug DALF is missing module ${productionModule.name}`)
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const productionDefinitions = productionModule.definitions.filter(
|
|
242
|
+
(definition) => definition.nodeKind === 'valueDefinition',
|
|
243
|
+
)
|
|
244
|
+
const debugDefinitions = new Map(
|
|
245
|
+
debugModule.definitions
|
|
246
|
+
.filter((definition) => definition.nodeKind === 'valueDefinition')
|
|
247
|
+
.map((definition) => [definition.name, definition]),
|
|
248
|
+
)
|
|
249
|
+
for (const productionDefinition of productionDefinitions) {
|
|
250
|
+
const debugDefinition = debugDefinitions.get(productionDefinition.name)
|
|
251
|
+
|
|
252
|
+
if (debugDefinition === undefined) {
|
|
253
|
+
throw new Error(
|
|
254
|
+
`debug DALF is missing definition ${productionModule.name}:${productionDefinition.name}`,
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (
|
|
259
|
+
!structurallyEqualIgnoringSourceLocation(
|
|
260
|
+
productionDefinition.expression,
|
|
261
|
+
debugDefinition.expression,
|
|
262
|
+
packageIdEquivalence,
|
|
263
|
+
)
|
|
264
|
+
) {
|
|
265
|
+
skippedDefinitions.push(`${productionModule.name}:${productionDefinition.name}`)
|
|
266
|
+
continue
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
collectExpressionLocationsFromExpression({
|
|
270
|
+
packageId: productionPackage.packageId,
|
|
271
|
+
moduleName: productionModule.name,
|
|
272
|
+
definitionName: productionDefinition.name,
|
|
273
|
+
productionExpression: productionDefinition.expression,
|
|
274
|
+
debugExpression: debugDefinition.expression,
|
|
275
|
+
expressionPath: [],
|
|
276
|
+
sourceModules,
|
|
277
|
+
packageIdEquivalence,
|
|
278
|
+
locations,
|
|
279
|
+
})
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return { locations, skippedDefinitions }
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
function collectInterfaceChoiceExecutables(
|
|
287
|
+
packageId,
|
|
288
|
+
compiledModules,
|
|
289
|
+
executables,
|
|
290
|
+
expressionLocations,
|
|
291
|
+
) {
|
|
292
|
+
const existingExerciseKeys = new Set(
|
|
293
|
+
executables
|
|
294
|
+
.filter((candidate) => candidate.entrypointKind === 'exercise')
|
|
295
|
+
.map((candidate) =>
|
|
296
|
+
`${candidate.packageId}::${candidate.moduleName}::${candidate.templateName}::${candidate.choiceName}`,
|
|
297
|
+
),
|
|
298
|
+
)
|
|
299
|
+
const expressionLocationsByDefinition = new Map()
|
|
300
|
+
|
|
301
|
+
for (const location of expressionLocations) {
|
|
302
|
+
const key = `${location.moduleName}::${location.definitionName}`
|
|
303
|
+
const locations = expressionLocationsByDefinition.get(key) ?? []
|
|
304
|
+
locations.push(location)
|
|
305
|
+
expressionLocationsByDefinition.set(key, locations)
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
const resolvedExecutables = []
|
|
309
|
+
|
|
310
|
+
for (const compiledModule of compiledModules) {
|
|
311
|
+
const sourceModule = compiledModule.sourceModule
|
|
312
|
+
if (sourceModule === undefined) {
|
|
313
|
+
continue
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
for (const implementation of sourceModule.interfaceChoiceImplementations) {
|
|
317
|
+
const executableKey =
|
|
318
|
+
`${packageId}::${compiledModule.moduleName}::${implementation.templateName}::${implementation.choiceName}`
|
|
319
|
+
if (existingExerciseKeys.has(executableKey)) {
|
|
320
|
+
continue
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
const definitionName = resolveInterfaceChoiceDefinitionName(
|
|
324
|
+
compiledModule.moduleName,
|
|
325
|
+
compiledModule.definitions,
|
|
326
|
+
implementation.span,
|
|
327
|
+
implementation.templateName,
|
|
328
|
+
expressionLocationsByDefinition,
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
if (definitionName === undefined) {
|
|
332
|
+
continue
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
resolvedExecutables.push({
|
|
336
|
+
packageId,
|
|
337
|
+
moduleName: compiledModule.moduleName,
|
|
338
|
+
definitionName,
|
|
339
|
+
path: sourceModule.archivePath,
|
|
340
|
+
startLine: implementation.span.startLine,
|
|
341
|
+
startColumn: implementation.span.startColumn,
|
|
342
|
+
endLine: implementation.span.endLine,
|
|
343
|
+
endColumn: implementation.span.endColumn,
|
|
344
|
+
precision: 'exact',
|
|
345
|
+
entrypointKind: 'exercise',
|
|
346
|
+
templateName: implementation.templateName,
|
|
347
|
+
choiceName: implementation.choiceName,
|
|
348
|
+
choiceArgumentFieldName: implementation.choiceArgumentFieldName,
|
|
349
|
+
})
|
|
350
|
+
existingExerciseKeys.add(executableKey)
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return resolvedExecutables
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
function resolveInterfaceChoiceDefinitionName(
|
|
358
|
+
moduleName,
|
|
359
|
+
definitions,
|
|
360
|
+
span,
|
|
361
|
+
templateName,
|
|
362
|
+
expressionLocationsByDefinition,
|
|
363
|
+
) {
|
|
364
|
+
let bestCandidate
|
|
365
|
+
|
|
366
|
+
for (const definition of definitions) {
|
|
367
|
+
const key = `${moduleName}::${definition.name}`
|
|
368
|
+
const locations = expressionLocationsByDefinition.get(key)
|
|
369
|
+
if (locations === undefined || locations.length === 0) {
|
|
370
|
+
continue
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const overlappingLines = new Set()
|
|
374
|
+
for (const location of locations) {
|
|
375
|
+
if (
|
|
376
|
+
location.startLine >= span.startLine - 1
|
|
377
|
+
&& location.startLine <= span.endLine
|
|
378
|
+
) {
|
|
379
|
+
overlappingLines.add(location.startLine)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
if (overlappingLines.size === 0) {
|
|
384
|
+
continue
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
const candidate = {
|
|
388
|
+
definitionName: definition.name,
|
|
389
|
+
overlapScore: overlappingLines.size,
|
|
390
|
+
preferredTemplateScope:
|
|
391
|
+
definition.name.startsWith(`$$$$sc_${templateName}_`) ? 1 : 0,
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (
|
|
395
|
+
bestCandidate === undefined
|
|
396
|
+
|| candidate.overlapScore > bestCandidate.overlapScore
|
|
397
|
+
|| (
|
|
398
|
+
candidate.overlapScore === bestCandidate.overlapScore
|
|
399
|
+
&& candidate.preferredTemplateScope > bestCandidate.preferredTemplateScope
|
|
400
|
+
)
|
|
401
|
+
) {
|
|
402
|
+
bestCandidate = candidate
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
return bestCandidate?.definitionName
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
function collectExpressionLocationsFromExpression({
|
|
410
|
+
packageId,
|
|
411
|
+
moduleName,
|
|
412
|
+
definitionName,
|
|
413
|
+
productionExpression,
|
|
414
|
+
debugExpression,
|
|
415
|
+
expressionPath,
|
|
416
|
+
sourceModules,
|
|
417
|
+
packageIdEquivalence,
|
|
418
|
+
locations,
|
|
419
|
+
}) {
|
|
420
|
+
if (
|
|
421
|
+
!structurallyEqualIgnoringSourceLocation(
|
|
422
|
+
productionExpression,
|
|
423
|
+
debugExpression,
|
|
424
|
+
packageIdEquivalence,
|
|
425
|
+
)
|
|
426
|
+
) {
|
|
427
|
+
throw new Error(
|
|
428
|
+
`debug DALF expression mismatch at ${moduleName}:${definitionName} path ${expressionPath.join('.')}`,
|
|
429
|
+
)
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const sourceLocation = debugExpression.sourceLocation
|
|
433
|
+
if (sourceLocation !== undefined) {
|
|
434
|
+
const sourceModule = sourceModules.get(sourceLocation.moduleName ?? moduleName)
|
|
435
|
+
|
|
436
|
+
if (sourceModule !== undefined) {
|
|
437
|
+
locations.push({
|
|
438
|
+
packageId,
|
|
439
|
+
moduleName,
|
|
440
|
+
definitionName,
|
|
441
|
+
expressionPath,
|
|
442
|
+
path: sourceModule.archivePath,
|
|
443
|
+
startLine: sourceLocation.startLine,
|
|
444
|
+
startColumn: sourceLocation.startColumn,
|
|
445
|
+
endLine: sourceLocation.endLine,
|
|
446
|
+
endColumn: sourceLocation.endColumn,
|
|
447
|
+
})
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const productionChildren = getExpressionChildren(productionExpression)
|
|
452
|
+
const debugChildren = getExpressionChildren(debugExpression)
|
|
453
|
+
|
|
454
|
+
if (productionChildren.length !== debugChildren.length) {
|
|
455
|
+
throw new Error(
|
|
456
|
+
`debug DALF child count mismatch at ${moduleName}:${definitionName} path ${expressionPath.join('.')}`,
|
|
457
|
+
)
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
for (let index = 0; index < productionChildren.length; index += 1) {
|
|
461
|
+
collectExpressionLocationsFromExpression({
|
|
462
|
+
packageId,
|
|
463
|
+
moduleName,
|
|
464
|
+
definitionName,
|
|
465
|
+
productionExpression: productionChildren[index],
|
|
466
|
+
debugExpression: debugChildren[index],
|
|
467
|
+
expressionPath: [...expressionPath, index],
|
|
468
|
+
sourceModules,
|
|
469
|
+
packageIdEquivalence,
|
|
470
|
+
locations,
|
|
471
|
+
})
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
function structurallyEqualIgnoringSourceLocation(left, right, packageIdEquivalence, parentKey = '') {
|
|
476
|
+
if (left === right) {
|
|
477
|
+
return true
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (
|
|
481
|
+
parentKey === 'packageId' &&
|
|
482
|
+
typeof left === 'string' &&
|
|
483
|
+
typeof right === 'string' &&
|
|
484
|
+
packageIdEquivalence.get(left) === right
|
|
485
|
+
) {
|
|
486
|
+
return true
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
if (Array.isArray(left) || Array.isArray(right)) {
|
|
490
|
+
if (!Array.isArray(left) || !Array.isArray(right) || left.length !== right.length) {
|
|
491
|
+
return false
|
|
492
|
+
}
|
|
493
|
+
return left.every((item, index) =>
|
|
494
|
+
structurallyEqualIgnoringSourceLocation(item, right[index], packageIdEquivalence, parentKey),
|
|
495
|
+
)
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
if (left && right && typeof left === 'object' && typeof right === 'object') {
|
|
499
|
+
const leftKeys = Object.keys(left).filter((key) => key !== 'sourceLocation')
|
|
500
|
+
const rightKeys = Object.keys(right).filter((key) => key !== 'sourceLocation')
|
|
501
|
+
|
|
502
|
+
if (leftKeys.length !== rightKeys.length) {
|
|
503
|
+
return false
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
for (let index = 0; index < leftKeys.length; index += 1) {
|
|
507
|
+
const key = leftKeys[index]
|
|
508
|
+
|
|
509
|
+
if (key !== rightKeys[index]) {
|
|
510
|
+
return false
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (
|
|
514
|
+
!structurallyEqualIgnoringSourceLocation(
|
|
515
|
+
left[key],
|
|
516
|
+
right[key],
|
|
517
|
+
packageIdEquivalence,
|
|
518
|
+
key,
|
|
519
|
+
)
|
|
520
|
+
) {
|
|
521
|
+
return false
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return true
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
return false
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
function getExpressionChildren(expression) {
|
|
532
|
+
if (expression.lambda !== undefined) {
|
|
533
|
+
return [expression.lambda.body]
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (expression.application !== undefined) {
|
|
537
|
+
return [
|
|
538
|
+
expression.application.function,
|
|
539
|
+
...expression.application.arguments,
|
|
540
|
+
]
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
if (expression.letExpression !== undefined) {
|
|
544
|
+
return [
|
|
545
|
+
...expression.letExpression.bindings.map((binding) => binding.value),
|
|
546
|
+
expression.letExpression.body,
|
|
547
|
+
]
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
if (expression.recordConstruction !== undefined) {
|
|
551
|
+
return expression.recordConstruction.fields.map((field) => field.value)
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
if (expression.recordProjection !== undefined) {
|
|
555
|
+
return [expression.recordProjection.record]
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
if (expression.recordUpdate !== undefined) {
|
|
559
|
+
return [
|
|
560
|
+
expression.recordUpdate.record,
|
|
561
|
+
expression.recordUpdate.value,
|
|
562
|
+
]
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
if (expression.caseExpression !== undefined) {
|
|
566
|
+
return [
|
|
567
|
+
expression.caseExpression.scrutinee,
|
|
568
|
+
...expression.caseExpression.alternatives.map((alternative) => alternative.body),
|
|
569
|
+
]
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (expression.variantConstruction !== undefined) {
|
|
573
|
+
return [expression.variantConstruction.argument]
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
if (expression.optionalConstruction?.value !== undefined) {
|
|
577
|
+
return [expression.optionalConstruction.value]
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (expression.listConstruction !== undefined) {
|
|
581
|
+
return [
|
|
582
|
+
...expression.listConstruction.front,
|
|
583
|
+
...(expression.listConstruction.tail === undefined
|
|
584
|
+
? []
|
|
585
|
+
: [expression.listConstruction.tail]),
|
|
586
|
+
]
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
if (expression.updateExpression !== undefined) {
|
|
590
|
+
return [
|
|
591
|
+
...(expression.updateExpression.expression === undefined
|
|
592
|
+
? []
|
|
593
|
+
: [expression.updateExpression.expression]),
|
|
594
|
+
...(expression.updateExpression.bindings?.map((binding) => binding.value) ?? []),
|
|
595
|
+
...(expression.updateExpression.body === undefined
|
|
596
|
+
? []
|
|
597
|
+
: [expression.updateExpression.body]),
|
|
598
|
+
...(expression.updateExpression.contractId === undefined
|
|
599
|
+
? []
|
|
600
|
+
: [expression.updateExpression.contractId]),
|
|
601
|
+
...(expression.updateExpression.argument === undefined
|
|
602
|
+
? []
|
|
603
|
+
: [expression.updateExpression.argument]),
|
|
604
|
+
]
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
return []
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
function resolveDefinitionMapping(definitionName, sourceModule) {
|
|
611
|
+
const exactValue = sourceModule.values.get(definitionName)
|
|
612
|
+
if (exactValue !== undefined) {
|
|
613
|
+
return {
|
|
614
|
+
kind: 'exact',
|
|
615
|
+
span: exactValue,
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
const recursiveStrip = stripScopeWrapper(definitionName)
|
|
620
|
+
if (recursiveStrip !== definitionName) {
|
|
621
|
+
const nested = resolveDefinitionMapping(recursiveStrip, sourceModule)
|
|
622
|
+
return nested.kind === 'exact'
|
|
623
|
+
? nested
|
|
624
|
+
: {
|
|
625
|
+
kind: 'fallback',
|
|
626
|
+
span: nested.span,
|
|
627
|
+
entrypointKind: nested.entrypointKind,
|
|
628
|
+
templateName: nested.templateName,
|
|
629
|
+
choiceName: nested.choiceName,
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
const decodedName = decodeGeneratedName(definitionName)
|
|
634
|
+
|
|
635
|
+
const fieldSpan = resolveFieldMapping(decodedName, sourceModule)
|
|
636
|
+
if (fieldSpan !== undefined) {
|
|
637
|
+
return {
|
|
638
|
+
kind: 'exact',
|
|
639
|
+
span: fieldSpan,
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const generatedEntity = resolveGeneratedEntityMapping(definitionName, decodedName, sourceModule)
|
|
644
|
+
if (generatedEntity !== undefined) {
|
|
645
|
+
return generatedEntity
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
const ordinalTemplateSpan = resolveOrdinalTemplateFamily(decodedName, sourceModule)
|
|
649
|
+
if (ordinalTemplateSpan !== undefined) {
|
|
650
|
+
return {
|
|
651
|
+
kind: 'fallback',
|
|
652
|
+
span: ordinalTemplateSpan,
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
const ordinalTypeSpan = resolveOrdinalTypeFamily(decodedName, sourceModule)
|
|
657
|
+
if (ordinalTypeSpan !== undefined) {
|
|
658
|
+
return {
|
|
659
|
+
kind: 'fallback',
|
|
660
|
+
span: ordinalTypeSpan,
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
return {
|
|
665
|
+
kind: 'fallback',
|
|
666
|
+
span: sourceModule.moduleSpan,
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
function resolveGeneratedEntityMapping(definitionName, decodedName, sourceModule) {
|
|
671
|
+
const createMatch = matchTemplateSuffix(definitionName, sourceModule.templates, ['$$fHasCreate'])
|
|
672
|
+
if (createMatch !== undefined) {
|
|
673
|
+
return {
|
|
674
|
+
kind: 'exact',
|
|
675
|
+
span: createMatch.span,
|
|
676
|
+
entrypointKind: 'create',
|
|
677
|
+
templateName: createMatch.name,
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
|
|
681
|
+
const templatePrefixes = [
|
|
682
|
+
'$$fHasFetch',
|
|
683
|
+
'$$fHasArchive',
|
|
684
|
+
'$$fHasObserver',
|
|
685
|
+
'$$fHasSignatory',
|
|
686
|
+
'$$fHasEnsure',
|
|
687
|
+
'$$fHasIsInterfaceType',
|
|
688
|
+
'$$fHasTemplateTypeRep',
|
|
689
|
+
'$$fHasToAnyTemplate',
|
|
690
|
+
'$$fHasFromAnyTemplate',
|
|
691
|
+
'$$fHasToInterface',
|
|
692
|
+
'$$fHasFromInterface',
|
|
693
|
+
'$$W',
|
|
694
|
+
'$$fShow',
|
|
695
|
+
'$$fEq',
|
|
696
|
+
]
|
|
697
|
+
|
|
698
|
+
const templateMatch = matchTemplateSuffix(definitionName, sourceModule.templates, templatePrefixes)
|
|
699
|
+
if (templateMatch !== undefined) {
|
|
700
|
+
return {
|
|
701
|
+
kind: 'exact',
|
|
702
|
+
span: templateMatch.span,
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
const typePrefixes = ['$$W', '$$fShow', '$$fEq']
|
|
707
|
+
const dataTypeMatch = matchTemplateSuffix(definitionName, sourceModule.dataTypes, typePrefixes)
|
|
708
|
+
if (dataTypeMatch !== undefined) {
|
|
709
|
+
return {
|
|
710
|
+
kind: 'exact',
|
|
711
|
+
span: dataTypeMatch.span,
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
const choiceMatch = matchChoiceSuffix(definitionName, sourceModule, [
|
|
716
|
+
'$$fHasExercise',
|
|
717
|
+
'$$fHasToAnyChoice',
|
|
718
|
+
'$$fHasFromAnyChoice',
|
|
719
|
+
])
|
|
720
|
+
if (choiceMatch !== undefined) {
|
|
721
|
+
return choiceMatch
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
const decodedChoiceMatch = matchChoiceSuffix(decodedName, sourceModule, [
|
|
725
|
+
'$$fHasExercise',
|
|
726
|
+
'$$fHasToAnyChoice',
|
|
727
|
+
'$$fHasFromAnyChoice',
|
|
728
|
+
])
|
|
729
|
+
if (decodedChoiceMatch !== undefined) {
|
|
730
|
+
return decodedChoiceMatch
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
if (definitionName.startsWith('$$fHasExercise') && definitionName.endsWith('Archive$u0028$u0029')) {
|
|
734
|
+
const template = matchTemplatePrefix(
|
|
735
|
+
definitionName.slice('$$fHasExercise'.length, -'Archive$u0028$u0029'.length),
|
|
736
|
+
sourceModule.templates,
|
|
737
|
+
)
|
|
738
|
+
if (template !== undefined) {
|
|
739
|
+
return {
|
|
740
|
+
kind: 'exact',
|
|
741
|
+
span: template.span,
|
|
742
|
+
entrypointKind: 'exercise',
|
|
743
|
+
templateName: template.name,
|
|
744
|
+
choiceName: 'Archive',
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
749
|
+
if (definitionName.startsWith('$$fHasExercise') && definitionName.endsWith('Archive()')) {
|
|
750
|
+
const template = matchTemplatePrefix(
|
|
751
|
+
definitionName.slice('$$fHasExercise'.length, -'Archive()'.length),
|
|
752
|
+
sourceModule.templates,
|
|
753
|
+
)
|
|
754
|
+
if (template !== undefined) {
|
|
755
|
+
return {
|
|
756
|
+
kind: 'exact',
|
|
757
|
+
span: template.span,
|
|
758
|
+
entrypointKind: 'exercise',
|
|
759
|
+
templateName: template.name,
|
|
760
|
+
choiceName: 'Archive',
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
return undefined
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
function resolveFieldMapping(decodedName, sourceModule) {
|
|
769
|
+
const selectorMatch = /^\$\$sel:([^:]+):(.+)$/.exec(decodedName)
|
|
770
|
+
if (selectorMatch) {
|
|
771
|
+
const fieldName = selectorMatch[1]
|
|
772
|
+
const typeName = selectorMatch[2]
|
|
773
|
+
const entity = sourceModule.templateOrDataEntities.find((candidate) => candidate.name === typeName)
|
|
774
|
+
return entity?.fields.get(fieldName)
|
|
775
|
+
}
|
|
776
|
+
|
|
777
|
+
const accessorMatch = /^\$\$f(?:Get|Set)Field"([^"]+)"(.+)$/.exec(decodedName)
|
|
778
|
+
if (accessorMatch) {
|
|
779
|
+
const fieldName = accessorMatch[1]
|
|
780
|
+
const typeMatch = matchTemplatePrefix(accessorMatch[2], sourceModule.templateOrDataEntityMap)
|
|
781
|
+
return typeMatch?.fields.get(fieldName)
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
return undefined
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
function resolveOrdinalTemplateFamily(decodedName, sourceModule) {
|
|
788
|
+
const match = /^\$\$c(?:archive|observer|signatory|ensure)(\d*)$/.exec(decodedName)
|
|
789
|
+
if (!match) {
|
|
790
|
+
return undefined
|
|
791
|
+
}
|
|
792
|
+
const index = match[1] === '' ? 0 : Number.parseInt(match[1], 10)
|
|
793
|
+
return sourceModule.templateOrder[index]?.span
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
function resolveOrdinalTypeFamily(decodedName, sourceModule) {
|
|
797
|
+
const match = /^\$\$c(?:show|showList|showsPrec|==|\/=)(\d*)$/.exec(decodedName)
|
|
798
|
+
if (!match) {
|
|
799
|
+
return undefined
|
|
800
|
+
}
|
|
801
|
+
const index = match[1] === '' ? 0 : Number.parseInt(match[1], 10)
|
|
802
|
+
return sourceModule.typeOrder[index]?.span
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
function matchTemplateSuffix(definitionName, entities, prefixes) {
|
|
806
|
+
for (const prefix of prefixes) {
|
|
807
|
+
if (!definitionName.startsWith(prefix)) {
|
|
808
|
+
continue
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
const suffix = definitionName.slice(prefix.length)
|
|
812
|
+
const entity = matchTemplatePrefix(suffix, entities)
|
|
813
|
+
if (entity !== undefined) {
|
|
814
|
+
return entity
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
return undefined
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
function matchChoiceSuffix(definitionName, sourceModule, prefixes) {
|
|
822
|
+
for (const prefix of prefixes) {
|
|
823
|
+
if (!definitionName.startsWith(prefix)) {
|
|
824
|
+
continue
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
const suffix = definitionName.slice(prefix.length)
|
|
828
|
+
|
|
829
|
+
for (const template of sourceModule.templateOrder) {
|
|
830
|
+
if (!suffix.startsWith(template.name)) {
|
|
831
|
+
continue
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
const afterTemplate = suffix.slice(template.name.length)
|
|
835
|
+
const choice = matchTemplatePrefix(afterTemplate, template.choices)
|
|
836
|
+
|
|
837
|
+
if (choice !== undefined) {
|
|
838
|
+
return {
|
|
839
|
+
kind: 'exact',
|
|
840
|
+
span: choice.span,
|
|
841
|
+
entrypointKind: prefix === '$$fHasExercise' ? 'exercise' : undefined,
|
|
842
|
+
templateName: prefix === '$$fHasExercise' ? template.name : undefined,
|
|
843
|
+
choiceName: prefix === '$$fHasExercise' ? choice.name : undefined,
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
return undefined
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
function matchTemplatePrefix(value, entities) {
|
|
853
|
+
let match
|
|
854
|
+
|
|
855
|
+
for (const entity of valuesOfEntityCollection(entities)) {
|
|
856
|
+
if (!value.startsWith(entity.name)) {
|
|
857
|
+
continue
|
|
858
|
+
}
|
|
859
|
+
if (match === undefined || entity.name.length > match.name.length) {
|
|
860
|
+
match = entity
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
return match
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
function valuesOfEntityCollection(entities) {
|
|
868
|
+
if (entities instanceof Map) {
|
|
869
|
+
return [...entities.values()]
|
|
870
|
+
}
|
|
871
|
+
return entities
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
function stripScopeWrapper(definitionName) {
|
|
875
|
+
const match = /^\$\$\$\$sc_(.+)_\d+$/.exec(definitionName)
|
|
876
|
+
return match?.[1] ?? definitionName
|
|
877
|
+
}
|
|
878
|
+
|
|
879
|
+
function decodeGeneratedName(value) {
|
|
880
|
+
return value.replace(/\$u([0-9a-fA-F]{4})/g, (_match, hex) =>
|
|
881
|
+
String.fromCharCode(Number.parseInt(hex, 16)),
|
|
882
|
+
)
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function resolvePackageDirectory(workspaceRoot, packageDir, packageName) {
|
|
886
|
+
if (packageDir) {
|
|
887
|
+
return path.resolve(packageDir)
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
const matches = []
|
|
891
|
+
walkDirectories(path.resolve(workspaceRoot), (directoryPath) => {
|
|
892
|
+
const damlYamlPath = path.join(directoryPath, 'daml.yaml')
|
|
893
|
+
if (!statExists(damlYamlPath, 'file')) {
|
|
894
|
+
return
|
|
895
|
+
}
|
|
896
|
+
const parsed = parseDamlYaml(damlYamlPath)
|
|
897
|
+
if (parsed.name === packageName) {
|
|
898
|
+
matches.push(directoryPath)
|
|
899
|
+
}
|
|
900
|
+
})
|
|
901
|
+
|
|
902
|
+
if (matches.length === 0) {
|
|
903
|
+
throw new Error(`Could not find package '${packageName}' under ${workspaceRoot}`)
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
if (matches.length > 1) {
|
|
907
|
+
throw new Error(`Found multiple package directories for '${packageName}': ${matches.join(', ')}`)
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
return matches[0]
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
function parseDamlYaml(filePath) {
|
|
914
|
+
const text = readFileSync(filePath, 'utf8')
|
|
915
|
+
const result = {
|
|
916
|
+
name: '',
|
|
917
|
+
source: 'daml',
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
for (const line of text.split(/\r?\n/)) {
|
|
921
|
+
const match = /^([A-Za-z0-9_-]+)\s*:\s*(.+)\s*$/.exec(line.trim())
|
|
922
|
+
if (!match) {
|
|
923
|
+
continue
|
|
924
|
+
}
|
|
925
|
+
const key = match[1]
|
|
926
|
+
const value = stripYamlQuotes(match[2])
|
|
927
|
+
|
|
928
|
+
if (key === 'name') {
|
|
929
|
+
result.name = value
|
|
930
|
+
} else if (key === 'source') {
|
|
931
|
+
result.source = value
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (!result.name) {
|
|
936
|
+
throw new Error(`Could not read package name from ${filePath}`)
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
return result
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
async function collectImportedPackageIds(rawPackage, compiledPackage, allowedPackageIds) {
|
|
943
|
+
const directImports =
|
|
944
|
+
rawPackage.importsSum?.oneofKind === 'packageImports'
|
|
945
|
+
? [...rawPackage.importsSum.packageImports.importedPackages]
|
|
946
|
+
: []
|
|
947
|
+
|
|
948
|
+
if (directImports.length > 0) {
|
|
949
|
+
return filterImportedPackages(directImports, allowedPackageIds)
|
|
950
|
+
}
|
|
951
|
+
|
|
952
|
+
const discoveredImports = new Set()
|
|
953
|
+
const serializedPackage = JSON.stringify(compiledPackage)
|
|
954
|
+
for (const match of serializedPackage.matchAll(/[0-9a-f]{64}/g)) {
|
|
955
|
+
discoveredImports.add(match[0])
|
|
956
|
+
}
|
|
957
|
+
discoveredImports.delete(compiledPackage.packageId)
|
|
958
|
+
return filterImportedPackages([...discoveredImports], allowedPackageIds)
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
async function collectWorkspacePackageIds(workspaceRoot, packageDir) {
|
|
962
|
+
const sdk = await import('@distrohelena/canton-typescript-sdk/daml-lf')
|
|
963
|
+
const archiveLoader = new sdk.DarArchiveLoader()
|
|
964
|
+
const packageLoader = new sdk.DamlLfPackageLoader()
|
|
965
|
+
const packageIds = new Set()
|
|
966
|
+
const roots = packageDir ? [path.resolve(packageDir)] : [path.resolve(workspaceRoot)]
|
|
967
|
+
|
|
968
|
+
for (const root of roots) {
|
|
969
|
+
for (const darPath of findDarFiles(root)) {
|
|
970
|
+
if (darPath.endsWith('-debug.dar')) {
|
|
971
|
+
continue
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
try {
|
|
975
|
+
const archive = await archiveLoader.loadDarOrThrowAsync(readFileSync(darPath))
|
|
976
|
+
const compiledPackage = packageLoader.loadPackageOrThrow(archive.mainPackageEntry.bytes)
|
|
977
|
+
packageIds.add(compiledPackage.packageId)
|
|
978
|
+
} catch {
|
|
979
|
+
// Ignore artifacts that are not package DARs we can index here.
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
return packageIds
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
function filterImportedPackages(importedPackages, allowedPackageIds) {
|
|
988
|
+
const uniquePackageIds = [...new Set(importedPackages)]
|
|
989
|
+
if (!allowedPackageIds || allowedPackageIds.size === 0) {
|
|
990
|
+
return uniquePackageIds.sort()
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
return uniquePackageIds
|
|
994
|
+
.filter((packageId) => allowedPackageIds.has(packageId))
|
|
995
|
+
.sort()
|
|
996
|
+
}
|
|
997
|
+
|
|
998
|
+
function stripYamlQuotes(value) {
|
|
999
|
+
if (
|
|
1000
|
+
(value.startsWith('"') && value.endsWith('"'))
|
|
1001
|
+
|| (value.startsWith("'") && value.endsWith("'"))
|
|
1002
|
+
) {
|
|
1003
|
+
return value.slice(1, -1)
|
|
1004
|
+
}
|
|
1005
|
+
return value
|
|
1006
|
+
}
|
|
1007
|
+
|
|
1008
|
+
function collectSourceModules(sourceDirectory, archiveSourcePrefix, workspaceRoot) {
|
|
1009
|
+
const modules = new Map()
|
|
1010
|
+
|
|
1011
|
+
walkFiles(sourceDirectory, (filePath) => {
|
|
1012
|
+
if (!filePath.endsWith('.daml')) {
|
|
1013
|
+
return
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
const relativePath = path.relative(sourceDirectory, filePath)
|
|
1017
|
+
const archivePath = joinArchivePath(archiveSourcePrefix, relativePath)
|
|
1018
|
+
const sourceModule = parseSourceModule(
|
|
1019
|
+
readFileSync(filePath, 'utf8'),
|
|
1020
|
+
archivePath,
|
|
1021
|
+
)
|
|
1022
|
+
|
|
1023
|
+
if (sourceModule !== undefined) {
|
|
1024
|
+
modules.set(sourceModule.moduleName, sourceModule)
|
|
1025
|
+
}
|
|
1026
|
+
})
|
|
1027
|
+
|
|
1028
|
+
resolveInterfaceChoiceImplementations(
|
|
1029
|
+
modules,
|
|
1030
|
+
workspaceRoot ? collectWorkspaceInterfaces(workspaceRoot) : undefined,
|
|
1031
|
+
)
|
|
1032
|
+
|
|
1033
|
+
return modules
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
function collectWorkspaceInterfaces(workspaceRoot) {
|
|
1037
|
+
const interfaces = new Map()
|
|
1038
|
+
|
|
1039
|
+
walkFiles(path.resolve(workspaceRoot), (filePath) => {
|
|
1040
|
+
if (!filePath.endsWith('.daml')) {
|
|
1041
|
+
return
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
const sourceModule = parseSourceModule(readFileSync(filePath, 'utf8'), '')
|
|
1045
|
+
if (sourceModule === undefined) {
|
|
1046
|
+
return
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
for (const interfaceDefinition of sourceModule.interfaces.values()) {
|
|
1050
|
+
interfaces.set(interfaceDefinition.name, interfaceDefinition)
|
|
1051
|
+
}
|
|
1052
|
+
})
|
|
1053
|
+
|
|
1054
|
+
return interfaces
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1057
|
+
function parseSourceModule(content, archivePath) {
|
|
1058
|
+
const lines = content.split(/\r?\n/)
|
|
1059
|
+
const topLevelLineIndexes = collectTopLevelLineIndexes(lines)
|
|
1060
|
+
const moduleLineIndex = lines.findIndex((line) => /^module\s+([A-Za-z0-9_.']+)\s+where\s*$/.test(line.trim()))
|
|
1061
|
+
|
|
1062
|
+
if (moduleLineIndex < 0) {
|
|
1063
|
+
return undefined
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
const moduleName = lines[moduleLineIndex].trim().match(/^module\s+([A-Za-z0-9_.']+)\s+where\s*$/)?.[1]
|
|
1067
|
+
if (!moduleName) {
|
|
1068
|
+
return undefined
|
|
1069
|
+
}
|
|
1070
|
+
|
|
1071
|
+
const moduleSpan = createSpan(lines, firstNonEmptyLine(lines), lastNonEmptyLine(lines))
|
|
1072
|
+
const values = new Map()
|
|
1073
|
+
const dataTypes = new Map()
|
|
1074
|
+
const interfaces = new Map()
|
|
1075
|
+
const interfaceInstances = []
|
|
1076
|
+
const templates = new Map()
|
|
1077
|
+
const templateOrder = []
|
|
1078
|
+
const typeOrder = []
|
|
1079
|
+
const pendingValueSignatures = new Map()
|
|
1080
|
+
|
|
1081
|
+
for (let index = 0; index < topLevelLineIndexes.length; index += 1) {
|
|
1082
|
+
const startLineIndex = topLevelLineIndexes[index]
|
|
1083
|
+
const endLineIndex = findBlockEnd(lines, topLevelLineIndexes, index)
|
|
1084
|
+
const header = lines[startLineIndex].trim()
|
|
1085
|
+
|
|
1086
|
+
if (header.startsWith('module ')) {
|
|
1087
|
+
continue
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
const dataMatch = /^data\s+([A-Z][A-Za-z0-9_']*)\b/.exec(header)
|
|
1091
|
+
if (dataMatch) {
|
|
1092
|
+
const fields = parseRecordFields(lines, startLineIndex, endLineIndex)
|
|
1093
|
+
const entity = {
|
|
1094
|
+
name: dataMatch[1],
|
|
1095
|
+
span: createSpan(lines, startLineIndex, endLineIndex),
|
|
1096
|
+
fields,
|
|
1097
|
+
}
|
|
1098
|
+
dataTypes.set(entity.name, entity)
|
|
1099
|
+
typeOrder.push(entity)
|
|
1100
|
+
continue
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
const interfaceMatch = /^interface\s+([A-Z][A-Za-z0-9_']*)\s+where\b/.exec(header)
|
|
1104
|
+
if (interfaceMatch) {
|
|
1105
|
+
interfaces.set(
|
|
1106
|
+
interfaceMatch[1],
|
|
1107
|
+
parseInterfaceDefinition(lines, startLineIndex, endLineIndex, interfaceMatch[1]),
|
|
1108
|
+
)
|
|
1109
|
+
continue
|
|
1110
|
+
}
|
|
1111
|
+
|
|
1112
|
+
const templateMatch = /^template\s+([A-Z][A-Za-z0-9_']*)\b/.exec(header)
|
|
1113
|
+
if (templateMatch) {
|
|
1114
|
+
const fields = parseRecordFields(lines, startLineIndex, endLineIndex)
|
|
1115
|
+
const choices = parseTemplateChoices(lines, startLineIndex, endLineIndex)
|
|
1116
|
+
const templateInterfaceInstances = parseTemplateInterfaceInstances(
|
|
1117
|
+
lines,
|
|
1118
|
+
startLineIndex,
|
|
1119
|
+
endLineIndex,
|
|
1120
|
+
templateMatch[1],
|
|
1121
|
+
)
|
|
1122
|
+
const entity = {
|
|
1123
|
+
name: templateMatch[1],
|
|
1124
|
+
span: createSpan(lines, startLineIndex, endLineIndex),
|
|
1125
|
+
fields,
|
|
1126
|
+
choices,
|
|
1127
|
+
}
|
|
1128
|
+
templates.set(entity.name, entity)
|
|
1129
|
+
templateOrder.push(entity)
|
|
1130
|
+
typeOrder.push(entity)
|
|
1131
|
+
interfaceInstances.push(...templateInterfaceInstances)
|
|
1132
|
+
continue
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
const signatureMatch = /^([a-z][A-Za-z0-9_']*)\s*:/.exec(header)
|
|
1136
|
+
if (signatureMatch) {
|
|
1137
|
+
pendingValueSignatures.set(signatureMatch[1], startLineIndex)
|
|
1138
|
+
continue
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
const valueMatch = /^([a-z][A-Za-z0-9_']*)\b.*=/.exec(header)
|
|
1142
|
+
if (valueMatch && !isReservedTopLevelKeyword(valueMatch[1])) {
|
|
1143
|
+
const valueName = valueMatch[1]
|
|
1144
|
+
const valueStartLine = pendingValueSignatures.get(valueName) ?? startLineIndex
|
|
1145
|
+
values.set(valueName, createSpan(lines, valueStartLine, endLineIndex))
|
|
1146
|
+
pendingValueSignatures.delete(valueName)
|
|
1147
|
+
}
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
const templateOrDataEntities = [...templates.values(), ...dataTypes.values()]
|
|
1151
|
+
const templateOrDataEntityMap = new Map(templateOrDataEntities.map((entity) => [entity.name, entity]))
|
|
1152
|
+
|
|
1153
|
+
return {
|
|
1154
|
+
moduleName,
|
|
1155
|
+
archivePath,
|
|
1156
|
+
moduleSpan,
|
|
1157
|
+
values,
|
|
1158
|
+
dataTypes,
|
|
1159
|
+
interfaces,
|
|
1160
|
+
interfaceInstances,
|
|
1161
|
+
templates,
|
|
1162
|
+
templateOrder,
|
|
1163
|
+
typeOrder,
|
|
1164
|
+
templateOrDataEntities,
|
|
1165
|
+
templateOrDataEntityMap,
|
|
1166
|
+
interfaceChoiceImplementations: [],
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1170
|
+
function parseInterfaceDefinition(lines, startLineIndex, endLineIndex, interfaceName) {
|
|
1171
|
+
const choiceMethods = new Map()
|
|
1172
|
+
const choiceArgumentFieldNames = new Map()
|
|
1173
|
+
|
|
1174
|
+
for (let lineIndex = startLineIndex + 1; lineIndex <= endLineIndex; lineIndex += 1) {
|
|
1175
|
+
const choiceMatch = /^\s*(?:nonconsuming\s+)?choice\s+([A-Z][A-Za-z0-9_']*)\s*:/.exec(lines[lineIndex])
|
|
1176
|
+
if (!choiceMatch) {
|
|
1177
|
+
continue
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
const choiceName = choiceMatch[1]
|
|
1181
|
+
const choiceEndLineIndex = findIndentedBlockEnd(lines, lineIndex, endLineIndex)
|
|
1182
|
+
|
|
1183
|
+
for (let bodyLineIndex = lineIndex + 1; bodyLineIndex <= choiceEndLineIndex; bodyLineIndex += 1) {
|
|
1184
|
+
const argumentFieldMatch = /^\s+with\s+([a-z][A-Za-z0-9_']*)\s*:/.exec(lines[bodyLineIndex])
|
|
1185
|
+
if (argumentFieldMatch) {
|
|
1186
|
+
choiceArgumentFieldNames.set(choiceName, argumentFieldMatch[1])
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
const methodMatch = /\bdo\s+([a-z][A-Za-z0-9_']*)\s+this\s+self\b/.exec(lines[bodyLineIndex])
|
|
1190
|
+
if (methodMatch) {
|
|
1191
|
+
choiceMethods.set(methodMatch[1], choiceName)
|
|
1192
|
+
break
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
return {
|
|
1198
|
+
name: interfaceName,
|
|
1199
|
+
choiceMethods,
|
|
1200
|
+
choiceArgumentFieldNames,
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
function parseRecordFields(lines, startLineIndex, endLineIndex) {
|
|
1205
|
+
const fields = new Map()
|
|
1206
|
+
let inWithBlock = false
|
|
1207
|
+
|
|
1208
|
+
for (let lineIndex = startLineIndex + 1; lineIndex <= endLineIndex; lineIndex += 1) {
|
|
1209
|
+
const trimmed = lines[lineIndex].trim()
|
|
1210
|
+
if (trimmed === 'with') {
|
|
1211
|
+
inWithBlock = true
|
|
1212
|
+
continue
|
|
1213
|
+
}
|
|
1214
|
+
if (!inWithBlock) {
|
|
1215
|
+
continue
|
|
1216
|
+
}
|
|
1217
|
+
if (trimmed === 'where' || trimmed.startsWith('deriving')) {
|
|
1218
|
+
break
|
|
1219
|
+
}
|
|
1220
|
+
const fieldMatch = /^\s{2,}([a-z][A-Za-z0-9_']*)\s*:/.exec(lines[lineIndex])
|
|
1221
|
+
if (fieldMatch) {
|
|
1222
|
+
fields.set(fieldMatch[1], createSpan(lines, lineIndex, lineIndex))
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1226
|
+
return fields
|
|
1227
|
+
}
|
|
1228
|
+
|
|
1229
|
+
function parseTemplateChoices(lines, startLineIndex, endLineIndex) {
|
|
1230
|
+
const choices = new Map()
|
|
1231
|
+
|
|
1232
|
+
for (let lineIndex = startLineIndex + 1; lineIndex <= endLineIndex; lineIndex += 1) {
|
|
1233
|
+
const choiceMatch = /^\s*(?:nonconsuming\s+)?choice\s+([A-Z][A-Za-z0-9_']*)\s*:/.exec(lines[lineIndex])
|
|
1234
|
+
if (choiceMatch) {
|
|
1235
|
+
choices.set(choiceMatch[1], {
|
|
1236
|
+
name: choiceMatch[1],
|
|
1237
|
+
span: createSpan(lines, lineIndex, findIndentedBlockEnd(lines, lineIndex, endLineIndex)),
|
|
1238
|
+
})
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
return choices
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
function parseTemplateInterfaceInstances(lines, startLineIndex, endLineIndex, templateName) {
|
|
1246
|
+
const interfaceInstances = []
|
|
1247
|
+
|
|
1248
|
+
for (let lineIndex = startLineIndex + 1; lineIndex <= endLineIndex; lineIndex += 1) {
|
|
1249
|
+
const instanceMatch =
|
|
1250
|
+
/^\s+interface instance\s+([A-Z][A-Za-z0-9_']*)\s+for\s+([A-Z][A-Za-z0-9_']*)\s+where\s*$/.exec(
|
|
1251
|
+
lines[lineIndex],
|
|
1252
|
+
)
|
|
1253
|
+
|
|
1254
|
+
if (!instanceMatch || instanceMatch[2] !== templateName) {
|
|
1255
|
+
continue
|
|
1256
|
+
}
|
|
1257
|
+
|
|
1258
|
+
const instanceEndLineIndex = findIndentedBlockEnd(lines, lineIndex, endLineIndex)
|
|
1259
|
+
const implementations = []
|
|
1260
|
+
|
|
1261
|
+
for (
|
|
1262
|
+
let methodLineIndex = lineIndex + 1;
|
|
1263
|
+
methodLineIndex <= instanceEndLineIndex;
|
|
1264
|
+
methodLineIndex += 1
|
|
1265
|
+
) {
|
|
1266
|
+
const methodMatch = /^\s+([a-z][A-Za-z0-9_']*)\b.*=/.exec(lines[methodLineIndex])
|
|
1267
|
+
if (!methodMatch || isReservedTopLevelKeyword(methodMatch[1])) {
|
|
1268
|
+
continue
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
implementations.push({
|
|
1272
|
+
methodName: methodMatch[1],
|
|
1273
|
+
span: createSpan(
|
|
1274
|
+
lines,
|
|
1275
|
+
methodLineIndex,
|
|
1276
|
+
findIndentedBlockEnd(lines, methodLineIndex, instanceEndLineIndex),
|
|
1277
|
+
),
|
|
1278
|
+
})
|
|
1279
|
+
}
|
|
1280
|
+
|
|
1281
|
+
interfaceInstances.push({
|
|
1282
|
+
interfaceName: instanceMatch[1],
|
|
1283
|
+
templateName,
|
|
1284
|
+
implementations,
|
|
1285
|
+
})
|
|
1286
|
+
}
|
|
1287
|
+
|
|
1288
|
+
return interfaceInstances
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
function findIndentedBlockEnd(lines, startLineIndex, endLineIndex) {
|
|
1292
|
+
const startIndent = indentationWidth(lines[startLineIndex])
|
|
1293
|
+
let lastContentLine = startLineIndex
|
|
1294
|
+
|
|
1295
|
+
for (let lineIndex = startLineIndex + 1; lineIndex <= endLineIndex; lineIndex += 1) {
|
|
1296
|
+
const line = lines[lineIndex]
|
|
1297
|
+
const trimmed = line.trim()
|
|
1298
|
+
if (!trimmed || trimmed.startsWith('--')) {
|
|
1299
|
+
continue
|
|
1300
|
+
}
|
|
1301
|
+
if (indentationWidth(line) <= startIndent) {
|
|
1302
|
+
break
|
|
1303
|
+
}
|
|
1304
|
+
lastContentLine = lineIndex
|
|
1305
|
+
}
|
|
1306
|
+
|
|
1307
|
+
return lastContentLine
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
function resolveInterfaceChoiceImplementations(sourceModules, workspaceInterfaces) {
|
|
1311
|
+
const interfacesByName = workspaceInterfaces ?? new Map()
|
|
1312
|
+
|
|
1313
|
+
if (workspaceInterfaces === undefined) {
|
|
1314
|
+
for (const sourceModule of sourceModules.values()) {
|
|
1315
|
+
for (const interfaceDefinition of sourceModule.interfaces.values()) {
|
|
1316
|
+
interfacesByName.set(interfaceDefinition.name, interfaceDefinition)
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
}
|
|
1320
|
+
|
|
1321
|
+
for (const sourceModule of sourceModules.values()) {
|
|
1322
|
+
sourceModule.interfaceChoiceImplementations = sourceModule.interfaceInstances.flatMap(
|
|
1323
|
+
(instance) => {
|
|
1324
|
+
const interfaceDefinition = interfacesByName.get(instance.interfaceName)
|
|
1325
|
+
if (interfaceDefinition === undefined) {
|
|
1326
|
+
return []
|
|
1327
|
+
}
|
|
1328
|
+
|
|
1329
|
+
return instance.implementations.flatMap((implementation) => {
|
|
1330
|
+
const choiceName = interfaceDefinition.choiceMethods.get(implementation.methodName)
|
|
1331
|
+
if (choiceName === undefined) {
|
|
1332
|
+
return []
|
|
1333
|
+
}
|
|
1334
|
+
|
|
1335
|
+
return [{
|
|
1336
|
+
templateName: instance.templateName,
|
|
1337
|
+
choiceName,
|
|
1338
|
+
choiceArgumentFieldName: interfaceDefinition.choiceArgumentFieldNames.get(choiceName),
|
|
1339
|
+
span: implementation.span,
|
|
1340
|
+
}]
|
|
1341
|
+
})
|
|
1342
|
+
},
|
|
1343
|
+
)
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
function collectTopLevelLineIndexes(lines) {
|
|
1348
|
+
const indexes = []
|
|
1349
|
+
for (let index = 0; index < lines.length; index += 1) {
|
|
1350
|
+
const line = lines[index]
|
|
1351
|
+
const trimmed = line.trim()
|
|
1352
|
+
if (!trimmed || trimmed.startsWith('--')) {
|
|
1353
|
+
continue
|
|
1354
|
+
}
|
|
1355
|
+
if (/^\S/.test(line)) {
|
|
1356
|
+
indexes.push(index)
|
|
1357
|
+
}
|
|
1358
|
+
}
|
|
1359
|
+
return indexes
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
function findBlockEnd(lines, topLevelLineIndexes, index) {
|
|
1363
|
+
const nextTopLevelLineIndex = topLevelLineIndexes[index + 1] ?? lines.length
|
|
1364
|
+
let endLineIndex = nextTopLevelLineIndex - 1
|
|
1365
|
+
|
|
1366
|
+
while (endLineIndex > topLevelLineIndexes[index] && !lines[endLineIndex].trim()) {
|
|
1367
|
+
endLineIndex -= 1
|
|
1368
|
+
}
|
|
1369
|
+
|
|
1370
|
+
return endLineIndex
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
function createSpan(lines, startLineIndex, endLineIndex) {
|
|
1374
|
+
return {
|
|
1375
|
+
startLine: startLineIndex + 1,
|
|
1376
|
+
startColumn: 1,
|
|
1377
|
+
endLine: endLineIndex + 1,
|
|
1378
|
+
endColumn: Math.max((lines[endLineIndex] ?? '').length + 1, 1),
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
function firstNonEmptyLine(lines) {
|
|
1383
|
+
const index = lines.findIndex((line) => line.trim().length > 0)
|
|
1384
|
+
return index >= 0 ? index : 0
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
function lastNonEmptyLine(lines) {
|
|
1388
|
+
for (let index = lines.length - 1; index >= 0; index -= 1) {
|
|
1389
|
+
if (lines[index].trim().length > 0) {
|
|
1390
|
+
return index
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
return 0
|
|
1394
|
+
}
|
|
1395
|
+
|
|
1396
|
+
function isReservedTopLevelKeyword(value) {
|
|
1397
|
+
return new Set([
|
|
1398
|
+
'choice',
|
|
1399
|
+
'controller',
|
|
1400
|
+
'do',
|
|
1401
|
+
'ensure',
|
|
1402
|
+
'let',
|
|
1403
|
+
'observer',
|
|
1404
|
+
'signatory',
|
|
1405
|
+
'then',
|
|
1406
|
+
'where',
|
|
1407
|
+
]).has(value)
|
|
1408
|
+
}
|
|
1409
|
+
|
|
1410
|
+
function indentationWidth(line) {
|
|
1411
|
+
const match = /^(\s*)/.exec(line)
|
|
1412
|
+
return match?.[1].length ?? 0
|
|
1413
|
+
}
|
|
1414
|
+
|
|
1415
|
+
function statExists(candidatePath, kind) {
|
|
1416
|
+
try {
|
|
1417
|
+
const stats = statSync(path.resolve(candidatePath))
|
|
1418
|
+
return kind === 'file' ? stats.isFile() : stats.isDirectory()
|
|
1419
|
+
} catch {
|
|
1420
|
+
return false
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
function walkDirectories(rootDir, visitor) {
|
|
1425
|
+
visitor(rootDir)
|
|
1426
|
+
for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
|
|
1427
|
+
if (!entry.isDirectory()) {
|
|
1428
|
+
continue
|
|
1429
|
+
}
|
|
1430
|
+
const absolutePath = path.join(rootDir, entry.name)
|
|
1431
|
+
walkDirectories(absolutePath, visitor)
|
|
1432
|
+
}
|
|
1433
|
+
}
|
|
1434
|
+
|
|
1435
|
+
function walkFiles(rootDir, visitor) {
|
|
1436
|
+
for (const entry of readdirSync(rootDir, { withFileTypes: true })) {
|
|
1437
|
+
const absolutePath = path.join(rootDir, entry.name)
|
|
1438
|
+
if (entry.isDirectory()) {
|
|
1439
|
+
walkFiles(absolutePath, visitor)
|
|
1440
|
+
continue
|
|
1441
|
+
}
|
|
1442
|
+
if (entry.isFile()) {
|
|
1443
|
+
visitor(absolutePath)
|
|
1444
|
+
}
|
|
1445
|
+
}
|
|
1446
|
+
}
|
|
1447
|
+
|
|
1448
|
+
function findDarFiles(rootDir) {
|
|
1449
|
+
const files = []
|
|
1450
|
+
walkFiles(rootDir, (filePath) => {
|
|
1451
|
+
if (filePath.endsWith('.dar')) {
|
|
1452
|
+
files.push(filePath)
|
|
1453
|
+
}
|
|
1454
|
+
})
|
|
1455
|
+
return files.sort()
|
|
1456
|
+
}
|
|
1457
|
+
|
|
1458
|
+
function joinArchivePath(prefix, relativePath) {
|
|
1459
|
+
const normalizedRelativePath = normalizeArchivePath(relativePath)
|
|
1460
|
+
if (!prefix) {
|
|
1461
|
+
return normalizedRelativePath
|
|
1462
|
+
}
|
|
1463
|
+
return `${normalizeArchivePath(prefix)}/${normalizedRelativePath}`
|
|
1464
|
+
}
|
|
1465
|
+
|
|
1466
|
+
function normalizeArchivePath(value) {
|
|
1467
|
+
return value
|
|
1468
|
+
.replace(/\\/g, '/')
|
|
1469
|
+
.replace(/^\.\/+/, '')
|
|
1470
|
+
.replace(/^\/+/, '')
|
|
1471
|
+
.replace(/\/+/g, '/')
|
|
1472
|
+
}
|
|
1473
|
+
|
|
1474
|
+
function defaultOutputPath(inputDar) {
|
|
1475
|
+
const extension = path.extname(inputDar)
|
|
1476
|
+
const baseName = extension ? inputDar.slice(0, -extension.length) : inputDar
|
|
1477
|
+
return `${baseName}-source-map.json`
|
|
1478
|
+
}
|
|
1479
|
+
|
|
1480
|
+
function requireValue(argv, index, flagName) {
|
|
1481
|
+
const value = argv[index]
|
|
1482
|
+
if (!value) {
|
|
1483
|
+
throw new Error(`${flagName} requires a value`)
|
|
1484
|
+
}
|
|
1485
|
+
return value
|
|
1486
|
+
}
|
|
1487
|
+
|
|
1488
|
+
function printHelp() {
|
|
1489
|
+
console.log(`Usage:
|
|
1490
|
+
node ./scripts/generate-daml-source-map.mjs \\
|
|
1491
|
+
--input /path/to/package.dar \\
|
|
1492
|
+
--workspace-root /path/to/daml/workspace \\
|
|
1493
|
+
[--package-dir /path/to/specific/package] \\
|
|
1494
|
+
[--source-prefix daml] \\
|
|
1495
|
+
[--debug-dalf /path/to/location-preserving.dalf] \\
|
|
1496
|
+
[--workspace-imports-only] \\
|
|
1497
|
+
[--output /path/to/source-map.json]
|
|
1498
|
+
|
|
1499
|
+
Notes:
|
|
1500
|
+
- The script reads the compiled main package from the DAR.
|
|
1501
|
+
- If --debug-dalf is provided, or the DAR already contains debug/*.dalf, the
|
|
1502
|
+
script emits deterministic expressionLocations from compiler-authored LF
|
|
1503
|
+
source spans.
|
|
1504
|
+
- It resolves the matching DAML package directory from daml.yaml.
|
|
1505
|
+
- It emits debug/source-map-compatible metadata for the main package.
|
|
1506
|
+
- --workspace-imports-only limits importedPackages to package ids built from
|
|
1507
|
+
the same workspace, so replay does not recurse into stdlib/vendor DARs.
|
|
1508
|
+
- Compiler-generated helper definitions fall back to the surrounding module span
|
|
1509
|
+
when a more specific source span cannot be inferred.`)
|
|
1510
|
+
}
|
|
1511
|
+
|
|
1512
|
+
await main()
|