semantifold 0.12.0 → 0.14.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 +2 -0
- package/build/src/backends/effects.js +5 -2
- package/build/src/backends/java.js +33 -14
- package/build/src/backends/javascript.js +83 -18
- package/build/src/backends/php.d.ts.map +1 -1
- package/build/src/backends/php.js +88 -16
- package/build/src/backends/ruby.js +37 -12
- package/build/src/backends/scalars.js +2 -2
- package/build/src/backends/shared.d.ts +2 -1
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +299 -42
- package/build/src/backends/typescript.js +83 -18
- package/build/src/backends/writer.d.ts +18 -4
- package/build/src/backends/writer.d.ts.map +1 -1
- package/build/src/backends/writer.js +40 -4
- package/build/src/frontends/java.d.ts +9 -1
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +90 -43
- package/build/src/frontends/javascript-typescript.d.ts +3 -1
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +133 -52
- package/build/src/frontends/php.d.ts +13 -1
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +226 -59
- package/build/src/frontends/ruby.d.ts +4 -1
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +113 -37
- package/build/src/frontends/types.d.ts +9 -2
- package/build/src/frontends/types.d.ts.map +1 -1
- package/build/src/frontends/types.js +26 -9
- package/build/src/language-registry.d.ts.map +1 -1
- package/build/src/language-registry.js +14 -6
- package/build/src/mapping.d.ts.map +1 -1
- package/build/src/mapping.js +4 -2
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +20 -6
- package/build/src/semantic/lifetime.js +28 -4
- package/build/src/semantic/provenance.d.ts.map +1 -1
- package/build/src/semantic/provenance.js +61 -9
- package/build/src/semantic/types.d.ts +214 -23
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +58 -11
- package/build/src/semantic/validate.d.ts +31 -2
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +450 -60
- package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
- package/docs/adr-bounded-subtyping.md +180 -0
- package/docs/adr-numeric-tower.md +156 -0
- package/docs/architecture.md +1 -1
- package/docs/language-support.md +8 -8
- package/docs/testing.md +5 -3
- package/package.json +1 -1
- package/src/backends/effects.js +3 -1
- package/src/backends/java.js +33 -14
- package/src/backends/javascript.js +83 -18
- package/src/backends/php.js +89 -16
- package/src/backends/ruby.js +36 -12
- package/src/backends/scalars.js +1 -1
- package/src/backends/shared.js +298 -47
- package/src/backends/typescript.js +83 -18
- package/src/backends/writer.js +41 -3
- package/src/frontends/java.js +95 -43
- package/src/frontends/javascript-typescript.js +145 -53
- package/src/frontends/php.js +243 -58
- package/src/frontends/ruby.js +117 -35
- package/src/frontends/types.js +26 -8
- package/src/language-registry.js +13 -5
- package/src/mapping.js +3 -1
- package/src/semantic/effects.js +18 -5
- package/src/semantic/lifetime.js +29 -3
- package/src/semantic/provenance.js +60 -14
- package/src/semantic/types.js +62 -10
- package/src/semantic/validate.js +481 -61
package/src/backends/shared.js
CHANGED
|
@@ -38,6 +38,7 @@ const task014Languages = new Set(["php", "ruby", "javascript", "typescript", "ja
|
|
|
38
38
|
const task032Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
39
39
|
const task033Languages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
40
40
|
const task048ConditionalLanguages = new Set(["php", "ruby", "javascript", "typescript", "java"])
|
|
41
|
+
const boundedSubtypingLanguages = new Set(["php"])
|
|
41
42
|
const javaReferenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass", "hashCode", "notify", "notifyAll", "toString", "wait"])
|
|
42
43
|
const javascriptReferenceMethodHooks = new Set([
|
|
43
44
|
"__defineGetter__", "__defineSetter__", "__lookupGetter__", "__lookupSetter__", "__proto__", "constructor",
|
|
@@ -56,7 +57,7 @@ const javaObjectInstanceMethodSignatures = new Set([
|
|
|
56
57
|
* Checks the intentionally narrow backend contract.
|
|
57
58
|
* @param {import("../semantic/types.js").SemanticModule} module - Semantic module.
|
|
58
59
|
* @param {import("../semantic/types.js").BackendLanguage} language - Backend language or binary target.
|
|
59
|
-
* @param {{externalDeclarationIds?: Set<string>, program?: boolean, visibleFunctions?: Map<string, import("../semantic/types.js").FunctionDeclaration>, visibleRecords?: Map<string, import("../semantic/types.js").RecordDeclaration>, visibleClasses?: Map<string, import("../semantic/types.js").ClassDeclaration>, visibleErrors?: Map<string, import("../semantic/types.js").ErrorDeclaration>, visibleCallEffects?: Map<string, Set<string>>}} [options] - Resolved program validation context.
|
|
60
|
+
* @param {{externalDeclarationIds?: Set<string>, program?: boolean, visibleFunctions?: Map<string, import("../semantic/types.js").FunctionDeclaration>, visibleRecords?: Map<string, import("../semantic/types.js").RecordDeclaration>, visibleClasses?: Map<string, import("../semantic/types.js").ClassDeclaration>, visibleInterfaces?: Map<string, import("../semantic/types.js").InterfaceDeclaration>, visibleErrors?: Map<string, import("../semantic/types.js").ErrorDeclaration>, visibleCallEffects?: Map<string, Set<string>>}} [options] - Resolved program validation context.
|
|
60
61
|
* @returns {void}
|
|
61
62
|
*/
|
|
62
63
|
export function validateBackendModule(module, language, options = {}) {
|
|
@@ -89,11 +90,19 @@ export function validateBackendModule(module, language, options = {}) {
|
|
|
89
90
|
if (classes.length > 0 && !task033Languages.has(language)) {
|
|
90
91
|
unsupportedCapability(language, "Task 033 reference classes, methods, and constructors", classes[0]?.location ?? module.location)
|
|
91
92
|
}
|
|
93
|
+
const declaredInterfaces = module.interfaces
|
|
94
|
+
const interfaces = declaredInterfaces === undefined ? [] : declaredInterfaces
|
|
95
|
+
|
|
96
|
+
if (!Array.isArray(interfaces)) unsupportedCapability(language, "missing or invalid interface declarations", module.location)
|
|
97
|
+
if (interfaces.length > 0 && !boundedSubtypingLanguages.has(language)) {
|
|
98
|
+
unsupportedCapability(language, "Task 048 bounded subtyping interfaces", interfaces[0]?.location ?? module.location)
|
|
99
|
+
}
|
|
92
100
|
if (!task011Languages.has(language)) rejectTypedErrors(module, language)
|
|
93
101
|
if (!task032Languages.has(language)) rejectConditionControlledLoops(module, language)
|
|
94
102
|
if (!task014Languages.has(language)) rejectOrderedMapCapability(module, language)
|
|
95
103
|
validateRecordTargets(records, module.functions, language, module.location)
|
|
96
|
-
|
|
104
|
+
validateInterfaceTargets(interfaces, records, errors, module.functions, language, module.location)
|
|
105
|
+
validateReferenceClassTargets(classes, records, errors, module.functions, interfaces, language, module.location)
|
|
97
106
|
if (!task008Languages.has(language)) rejectIterationStatements(module, language)
|
|
98
107
|
if (!task007Languages.has(language)) rejectOptionalTypes(module, language)
|
|
99
108
|
if (!task006Languages.has(language)) rejectCollectionTypes(module, language)
|
|
@@ -163,6 +172,7 @@ export function validateBackendModule(module, language, options = {}) {
|
|
|
163
172
|
classes: options.visibleClasses,
|
|
164
173
|
errors: options.visibleErrors,
|
|
165
174
|
functions: options.visibleFunctions,
|
|
175
|
+
interfaces: options.visibleInterfaces,
|
|
166
176
|
records: options.visibleRecords
|
|
167
177
|
})
|
|
168
178
|
}
|
|
@@ -382,7 +392,99 @@ function validateRecordTargets(records, functions, language, moduleLocation) {
|
|
|
382
392
|
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
383
393
|
* @returns {void}
|
|
384
394
|
*/
|
|
385
|
-
|
|
395
|
+
/**
|
|
396
|
+
* Validates the exact target-visible shape of every interface and its override contracts before output allocation.
|
|
397
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration[]} interfaces - Candidate interfaces.
|
|
398
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Existing records.
|
|
399
|
+
* @param {import("../semantic/types.js").ErrorDeclaration[]} errors - Existing typed errors.
|
|
400
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Existing functions.
|
|
401
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
402
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
403
|
+
* @returns {void}
|
|
404
|
+
*/
|
|
405
|
+
function validateInterfaceTargets(interfaces, records, errors, functions, language, moduleLocation) {
|
|
406
|
+
if (interfaces.length == 0) return
|
|
407
|
+
const byId = new Map(interfaces.map((declaration) => [declaration.id, declaration]))
|
|
408
|
+
const targetNames = new Set()
|
|
409
|
+
|
|
410
|
+
for (const declaration of [...records, ...errors, ...functions]) {
|
|
411
|
+
if (!declaration || typeof declaration != "object" || Array.isArray(declaration)) continue
|
|
412
|
+
const name = Reflect.get(declaration, "name")
|
|
413
|
+
|
|
414
|
+
if (typeof name == "string") targetNames.add(language == "php" ? name.toLowerCase() : name)
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
for (let interfaceIndex = 0; interfaceIndex < interfaces.length; interfaceIndex += 1) {
|
|
418
|
+
const declaration = interfaces[interfaceIndex]
|
|
419
|
+
const location = diagnosticLocation(declaration?.location, moduleLocation)
|
|
420
|
+
const keys = declaration && typeof declaration == "object" && !Array.isArray(declaration)
|
|
421
|
+
? Object.keys(declaration).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
422
|
+
|
|
423
|
+
if (!declaration || declaration.kind != "InterfaceDeclaration" ||
|
|
424
|
+
!["extends,id,kind,location,methods,name", "id,kind,location,methods,name"].includes(keys) ||
|
|
425
|
+
!isDenseArray(declaration.methods) ||
|
|
426
|
+
!new RegExp(`^(?:[a-z][a-z0-9._-]*#)?interface:${interfaceIndex}$`, "u").test(String(declaration.id))) {
|
|
427
|
+
unsupportedCapability(language, "missing or invalid interface declaration", location)
|
|
428
|
+
}
|
|
429
|
+
const declarationLocation = requireSemanticLocation(declaration.location, language, "interface declaration location", location)
|
|
430
|
+
|
|
431
|
+
validateTargetTypeIdentifier(language, declaration.name, declarationLocation)
|
|
432
|
+
const targetName = language == "php" ? declaration.name.toLowerCase() : declaration.name
|
|
433
|
+
|
|
434
|
+
if (targetNames.has(targetName)) unsupportedCapability(language, `interface collision '${declaration.name}'`, declarationLocation)
|
|
435
|
+
targetNames.add(targetName)
|
|
436
|
+
const methodNames = new Set()
|
|
437
|
+
|
|
438
|
+
for (let methodIndex = 0; methodIndex < declaration.methods.length; methodIndex += 1) {
|
|
439
|
+
const method = declaration.methods[methodIndex]
|
|
440
|
+
const methodLocation = diagnosticLocation(method?.location, declarationLocation)
|
|
441
|
+
const methodKeys = method && typeof method == "object" && !Array.isArray(method)
|
|
442
|
+
? Object.keys(method).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
443
|
+
|
|
444
|
+
if (!method || method.kind != "InterfaceMethodDeclaration" || methodKeys != "id,kind,location,name,parameters,returnType" ||
|
|
445
|
+
method.id != `${declaration.id}:method:${methodIndex}` || !isDenseArray(method.parameters)) {
|
|
446
|
+
unsupportedCapability(language, "missing or invalid interface method declaration", methodLocation)
|
|
447
|
+
}
|
|
448
|
+
const exactMethodLocation = requireSemanticLocation(method.location, language, "interface method declaration location", declarationLocation)
|
|
449
|
+
|
|
450
|
+
validateTargetIdentifier(language, method.name, "interface method", exactMethodLocation)
|
|
451
|
+
const normalized = language == "php" ? method.name.toLowerCase() : method.name
|
|
452
|
+
|
|
453
|
+
if (methodNames.has(normalized)) unsupportedCapability(language, `duplicate interface method '${method.name}'`, exactMethodLocation)
|
|
454
|
+
methodNames.add(normalized)
|
|
455
|
+
validateReferenceClassParameters(method.parameters, "interface method", language, exactMethodLocation)
|
|
456
|
+
}
|
|
457
|
+
const parents = declaration.extends ?? []
|
|
458
|
+
|
|
459
|
+
for (const parent of parents) {
|
|
460
|
+
const parentKeys = parent && typeof parent == "object" && !Array.isArray(parent)
|
|
461
|
+
? Object.keys(parent).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
462
|
+
|
|
463
|
+
if (!parent || parent.kind != "InterfaceType" || parentKeys != "declarationId,kind" ||
|
|
464
|
+
!byId.has(parent.declarationId)) {
|
|
465
|
+
unsupportedCapability(language, "missing or invalid interface parent reference", declarationLocation)
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
for (const declaration of interfaces) {
|
|
470
|
+
for (const parent of declaration.extends ?? []) {
|
|
471
|
+
if (parent.declarationId == declaration.id) unsupportedCapability(language, "interface extends itself", declaration.location)
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Validates the exact target-visible shape of every reference class before output allocation.
|
|
478
|
+
* @param {import("../semantic/types.js").ClassDeclaration[]} classes - Candidate classes.
|
|
479
|
+
* @param {import("../semantic/types.js").RecordDeclaration[]} records - Existing records.
|
|
480
|
+
* @param {import("../semantic/types.js").ErrorDeclaration[]} errors - Existing typed errors.
|
|
481
|
+
* @param {import("../semantic/types.js").FunctionDeclaration[]} functions - Existing functions.
|
|
482
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration[]} interfaces - Existing interfaces.
|
|
483
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
484
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} moduleLocation - Fallback location.
|
|
485
|
+
* @returns {void}
|
|
486
|
+
*/
|
|
487
|
+
function validateReferenceClassTargets(classes, records, errors, functions, interfaces, language, moduleLocation) {
|
|
386
488
|
const targetNames = new Set()
|
|
387
489
|
|
|
388
490
|
for (const declaration of [...records, ...errors, ...functions]) {
|
|
@@ -400,7 +502,8 @@ function validateReferenceClassTargets(classes, records, errors, functions, lang
|
|
|
400
502
|
? Object.keys(declaration).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
401
503
|
|
|
402
504
|
if (!declaration || declaration.kind != "ClassDeclaration" ||
|
|
403
|
-
!["constructor,fields,id,kind,location,methods,name", "constructor,fields,id,kind,location,methods,name,ownership"
|
|
505
|
+
!["constructor,fields,id,implements,kind,location,methods,name", "constructor,fields,id,implements,kind,location,methods,name,ownership",
|
|
506
|
+
"constructor,fields,id,kind,location,methods,name", "constructor,fields,id,kind,location,methods,name,ownership"].includes(keys) ||
|
|
404
507
|
!isDenseArray(declaration.fields) || declaration.fields.length == 0 || !isDenseArray(declaration.methods) ||
|
|
405
508
|
!new RegExp(`^(?:[a-z][a-z0-9._-]*#)?class:${classIndex}$`, "u").test(String(declaration.id))) {
|
|
406
509
|
unsupportedCapability(language, "missing or invalid reference class declaration", location)
|
|
@@ -459,7 +562,9 @@ function validateReferenceClassTargets(classes, records, errors, functions, lang
|
|
|
459
562
|
? Object.keys(method).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
460
563
|
|
|
461
564
|
if (!method || method.kind != "MethodDeclaration" ||
|
|
462
|
-
!["body,id,kind,location,name,parameters,returnType",
|
|
565
|
+
!["body,id,implements,kind,location,name,parameters,returnType",
|
|
566
|
+
"body,effects,failureIds,id,implements,kind,location,name,parameters,returnType",
|
|
567
|
+
"body,id,kind,location,name,parameters,returnType",
|
|
463
568
|
"body,effects,failureIds,id,kind,location,name,parameters,returnType"].includes(methodKeys) ||
|
|
464
569
|
method.id != `${declaration.id}:method:${methodIndex}` || !isDenseArray(method.parameters)) {
|
|
465
570
|
unsupportedCapability(language, "missing or invalid method declaration", methodLocation)
|
|
@@ -492,13 +597,104 @@ function validateReferenceClassTargets(classes, records, errors, functions, lang
|
|
|
492
597
|
methodNames.add(normalized)
|
|
493
598
|
validateReferenceClassParameters(method.parameters, "method", language, exactMethodLocation)
|
|
494
599
|
}
|
|
600
|
+
const implemented = declaration.implements ?? []
|
|
601
|
+
|
|
602
|
+
for (const reference of implemented) {
|
|
603
|
+
const referenceKeys = reference && typeof reference == "object" && !Array.isArray(reference)
|
|
604
|
+
? Object.keys(reference).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
605
|
+
|
|
606
|
+
if (!reference || reference.kind != "InterfaceType" || referenceKeys != "declarationId,kind" ||
|
|
607
|
+
!interfaces.some((candidate) => candidate.id == reference.declarationId)) {
|
|
608
|
+
unsupportedCapability(language, "missing or invalid implemented interface reference", location)
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
const satisfied = new Set()
|
|
612
|
+
|
|
613
|
+
for (const reference of implemented) {
|
|
614
|
+
for (const member of flattenedTargetInterfaceMembers(reference.declarationId, interfaces, language, location)) {
|
|
615
|
+
const method = declaration.methods.find((candidate) => candidate.name == member.name)
|
|
616
|
+
|
|
617
|
+
if (!method) unsupportedCapability(language, `interface method '${member.name}' is not implemented`, location)
|
|
618
|
+
if (satisfied.has(member.name)) unsupportedCapability(language, `duplicate interface implementation '${member.name}'`, location)
|
|
619
|
+
if (method.parameters.length != member.parameters.length ||
|
|
620
|
+
method.parameters.some((parameter, index) => !sameTargetType(parameter.type, member.parameters[index].type))) {
|
|
621
|
+
unsupportedCapability(language, `interface method signature mismatch '${member.name}'`, location)
|
|
622
|
+
}
|
|
623
|
+
if (!sameTargetType(method.returnType, member.returnType)) {
|
|
624
|
+
unsupportedCapability(language, `interface method return mismatch '${member.name}'`, location)
|
|
625
|
+
}
|
|
626
|
+
satisfied.add(member.name)
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/**
|
|
633
|
+
* Flattens one target-validated interface signature set in declaration order.
|
|
634
|
+
* @param {string} interfaceId - Interface identity.
|
|
635
|
+
* @param {import("../semantic/types.js").InterfaceDeclaration[]} interfaces - Interfaces in declaration order.
|
|
636
|
+
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
637
|
+
* @param {import("../semantic/types.js").SourceLocation | undefined} location - Diagnostic location.
|
|
638
|
+
* @param {Set<string>} [active] - Active extends path.
|
|
639
|
+
* @returns {import("../semantic/types.js").InterfaceMethodDeclaration[]} Flattened members.
|
|
640
|
+
*/
|
|
641
|
+
function flattenedTargetInterfaceMembers(interfaceId, interfaces, language, location, active = new Set()) {
|
|
642
|
+
const declaration = interfaces.find((candidate) => candidate.id == interfaceId)
|
|
643
|
+
|
|
644
|
+
if (!declaration) unsupportedCapability(language, "missing or invalid implemented interface reference", location)
|
|
645
|
+
if (active.has(interfaceId)) unsupportedCapability(language, "interface extends itself", declaration.location)
|
|
646
|
+
active.add(interfaceId)
|
|
647
|
+
/** @type {import("../semantic/types.js").InterfaceMethodDeclaration[]} */
|
|
648
|
+
const members = []
|
|
649
|
+
|
|
650
|
+
for (const parent of declaration.extends ?? []) {
|
|
651
|
+
for (const member of flattenedTargetInterfaceMembers(parent.declarationId, interfaces, language, location, active)) {
|
|
652
|
+
if (!members.some((candidate) => candidate.name == member.name)) members.push(member)
|
|
653
|
+
}
|
|
495
654
|
}
|
|
655
|
+
for (const method of declaration.methods) {
|
|
656
|
+
if (!members.some((candidate) => candidate.name == method.name)) members.push(method)
|
|
657
|
+
}
|
|
658
|
+
active.delete(interfaceId)
|
|
659
|
+
|
|
660
|
+
return members
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Compares two backend-validated semantic types structurally.
|
|
665
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} left - First type.
|
|
666
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} right - Second type.
|
|
667
|
+
* @returns {boolean} Whether the types are identical.
|
|
668
|
+
*/
|
|
669
|
+
function sameTargetType(left, right) {
|
|
670
|
+
return JSON.stringify(targetTypeIdentity(left)) == JSON.stringify(targetTypeIdentity(right))
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
/**
|
|
674
|
+
* Reduces one semantic type to its comparable target identity.
|
|
675
|
+
* @param {import("../semantic/types.js").SemanticFunctionReturnType} type - Semantic type.
|
|
676
|
+
* @returns {unknown} Comparable identity.
|
|
677
|
+
*/
|
|
678
|
+
function targetTypeIdentity(type) {
|
|
679
|
+
if (typeof type == "string") return type
|
|
680
|
+
if (type.kind == "TypeReference") return type.name
|
|
681
|
+
if (type.kind == "TypeVariableReference") return {kind: "TypeVariableReference", parameterId: type.parameterId}
|
|
682
|
+
if (type.kind == "RecordType") return {declarationId: type.declarationId, kind: "RecordType"}
|
|
683
|
+
if (type.kind == "ReferenceType") return {declarationId: type.declarationId, kind: "ReferenceType"}
|
|
684
|
+
if (type.kind == "InterfaceType") return {declarationId: type.declarationId, kind: "InterfaceType"}
|
|
685
|
+
if (type.kind == "OwnedResourceType") return {kind: "OwnedResourceType", resourceId: type.resourceId}
|
|
686
|
+
if (type.kind == "OwnedReferenceType") return {declarationId: type.declarationId, kind: "OwnedReferenceType"}
|
|
687
|
+
if (type.kind == "ListType") return {elementType: targetTypeIdentity(type.elementType), kind: "ListType"}
|
|
688
|
+
if (type.kind == "OptionalType") return {kind: "OptionalType", valueType: targetTypeIdentity(type.valueType)}
|
|
689
|
+
if (type.kind == "OrderedMapType") return {keyType: targetTypeIdentity(type.keyType), kind: "OrderedMapType", valueType: targetTypeIdentity(type.valueType)}
|
|
690
|
+
|
|
691
|
+
return {keyType: targetTypeIdentity(type.keyType), kind: "MapType", valueType: targetTypeIdentity(type.valueType)}
|
|
496
692
|
}
|
|
497
693
|
|
|
498
694
|
/**
|
|
499
695
|
* Validates one reference callable's exact parameter shape and target names.
|
|
500
696
|
* @param {import("../semantic/types.js").Parameter[]} parameters - Candidate parameters.
|
|
501
|
-
* @param {"constructor" | "method"} owner - Callable kind.
|
|
697
|
+
* @param {"constructor" | "interface method" | "method"} owner - Callable kind.
|
|
502
698
|
* @param {import("../semantic/types.js").BackendLanguage} language - Target language.
|
|
503
699
|
* @param {import("../semantic/types.js").SourceLocation | undefined} ownerLocation - Fallback location.
|
|
504
700
|
* @returns {void}
|
|
@@ -627,7 +823,8 @@ function rejectBlockConditionLoop(block, language, seen = new Set()) {
|
|
|
627
823
|
rejectBlockConditionLoop(Reflect.get(statement, "body"), language, seen)
|
|
628
824
|
} else if (kind == "TryStatement") {
|
|
629
825
|
rejectBlockConditionLoop(Reflect.get(statement, "body"), language, seen)
|
|
630
|
-
|
|
826
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) rejectBlockConditionLoop(Reflect.get(handler, "catchBody"), language, seen)
|
|
827
|
+
rejectBlockConditionLoop(Reflect.get(statement, "finallyBody"), language, seen)
|
|
631
828
|
}
|
|
632
829
|
}
|
|
633
830
|
}
|
|
@@ -658,7 +855,8 @@ function rejectBlockIteration(block, language, seen = new Set()) {
|
|
|
658
855
|
rejectBlockIteration(Reflect.get(statement, "alternate"), language, seen)
|
|
659
856
|
} else if (kind == "TryStatement") {
|
|
660
857
|
rejectBlockIteration(Reflect.get(statement, "body"), language, seen)
|
|
661
|
-
|
|
858
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) rejectBlockIteration(Reflect.get(handler, "catchBody"), language, seen)
|
|
859
|
+
rejectBlockIteration(Reflect.get(statement, "finallyBody"), language, seen)
|
|
662
860
|
}
|
|
663
861
|
}
|
|
664
862
|
}
|
|
@@ -713,7 +911,8 @@ function rejectBlockOptionalTypes(block, language) {
|
|
|
713
911
|
if (statement.kind == "WhileStatement") rejectBlockOptionalTypes(statement.body, language)
|
|
714
912
|
if (statement.kind == "TryStatement") {
|
|
715
913
|
rejectBlockOptionalTypes(statement.body, language)
|
|
716
|
-
|
|
914
|
+
for (const handler of statement.handlers ?? []) rejectBlockOptionalTypes(handler.catchBody, language)
|
|
915
|
+
if (statement.finallyBody) rejectBlockOptionalTypes(statement.finallyBody, language)
|
|
717
916
|
}
|
|
718
917
|
}
|
|
719
918
|
}
|
|
@@ -788,7 +987,8 @@ function rejectBlockCollectionTypes(block, language) {
|
|
|
788
987
|
if (statement.kind == "WhileStatement") rejectBlockCollectionTypes(statement.body, language)
|
|
789
988
|
if (statement.kind == "TryStatement") {
|
|
790
989
|
rejectBlockCollectionTypes(statement.body, language)
|
|
791
|
-
|
|
990
|
+
for (const handler of statement.handlers ?? []) rejectBlockCollectionTypes(handler.catchBody, language)
|
|
991
|
+
if (statement.finallyBody) rejectBlockCollectionTypes(statement.finallyBody, language)
|
|
792
992
|
}
|
|
793
993
|
}
|
|
794
994
|
}
|
|
@@ -856,9 +1056,13 @@ function validateScaffoldingNames(module, language) {
|
|
|
856
1056
|
}
|
|
857
1057
|
}
|
|
858
1058
|
}
|
|
859
|
-
if (statement.kind == "TryStatement"
|
|
860
|
-
|
|
861
|
-
|
|
1059
|
+
if (statement.kind == "TryStatement") {
|
|
1060
|
+
for (const handler of statement.handlers ?? []) {
|
|
1061
|
+
if (ownedPrintReceiverNames.has(handler.catchBinding.name)) {
|
|
1062
|
+
unsupportedCapability(language, `${owner} catch binding '${handler.catchBinding.name}' captures backend scaffolding`,
|
|
1063
|
+
handler.catchBinding.location)
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
862
1066
|
}
|
|
863
1067
|
}
|
|
864
1068
|
}
|
|
@@ -878,9 +1082,13 @@ function validateScaffoldingNames(module, language) {
|
|
|
878
1082
|
}
|
|
879
1083
|
}
|
|
880
1084
|
}
|
|
881
|
-
if (statement.kind == "TryStatement"
|
|
882
|
-
|
|
883
|
-
|
|
1085
|
+
if (statement.kind == "TryStatement") {
|
|
1086
|
+
for (const handler of statement.handlers ?? []) {
|
|
1087
|
+
if (ownedEntryNames.has(handler.catchBinding.name)) {
|
|
1088
|
+
unsupportedCapability(language, `entry catch binding '${handler.catchBinding.name}' captures backend scaffolding`,
|
|
1089
|
+
handler.catchBinding.location)
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
884
1092
|
}
|
|
885
1093
|
}
|
|
886
1094
|
for (const declaration of module.functions) {
|
|
@@ -949,12 +1157,13 @@ function validateRecordConstructorNames(module, language) {
|
|
|
949
1157
|
}
|
|
950
1158
|
}
|
|
951
1159
|
}
|
|
952
|
-
if (statement.kind == "TryStatement"
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
1160
|
+
if (statement.kind == "TryStatement") {
|
|
1161
|
+
const captured = (statement.handlers ?? []).find((handler) => recordNames.has(handler.catchBinding.name))
|
|
1162
|
+
|
|
1163
|
+
if (captured) {
|
|
1164
|
+
unsupportedCapability(language, `${owner} catch binding '${captured.catchBinding.name}' captures nominal constructor`,
|
|
1165
|
+
bindingNameLocation(captured.catchBinding))
|
|
1166
|
+
}
|
|
958
1167
|
}
|
|
959
1168
|
}
|
|
960
1169
|
}
|
|
@@ -1052,12 +1261,15 @@ function validateJavaUtilFactoryNames(module) {
|
|
|
1052
1261
|
}
|
|
1053
1262
|
if (statement.kind == "TryStatement") {
|
|
1054
1263
|
validateBlockNames(statement.body, capture, owner)
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1264
|
+
for (const handler of statement.handlers ?? []) {
|
|
1265
|
+
const catchCapture = handler.catchBinding.name == "java" ? {
|
|
1266
|
+
detail: `${owner} catch binding 'java' captures java.util factory syntax`,
|
|
1267
|
+
location: handler.catchBinding.location
|
|
1268
|
+
} : capture
|
|
1059
1269
|
|
|
1060
|
-
|
|
1270
|
+
validateBlockNames(handler.catchBody, catchCapture, owner)
|
|
1271
|
+
}
|
|
1272
|
+
if (statement.finallyBody) validateBlockNames(statement.finallyBody, capture, owner)
|
|
1061
1273
|
}
|
|
1062
1274
|
}
|
|
1063
1275
|
}
|
|
@@ -1220,7 +1432,11 @@ function allStatements(block) {
|
|
|
1220
1432
|
return [statement, ...allStatements(statement.body)]
|
|
1221
1433
|
}
|
|
1222
1434
|
if (statement.kind == "WhileStatement") return [statement, ...allStatements(statement.body)]
|
|
1223
|
-
if (statement.kind == "TryStatement")
|
|
1435
|
+
if (statement.kind == "TryStatement") {
|
|
1436
|
+
return [statement, ...allStatements(statement.body),
|
|
1437
|
+
...(statement.handlers ?? []).flatMap((handler) => allStatements(handler.catchBody)),
|
|
1438
|
+
...(statement.finallyBody ? allStatements(statement.finallyBody) : [])]
|
|
1439
|
+
}
|
|
1224
1440
|
|
|
1225
1441
|
return [statement]
|
|
1226
1442
|
})
|
|
@@ -1274,7 +1490,8 @@ function collectLoopOwners(module, language) {
|
|
|
1274
1490
|
visit(Reflect.get(statement, "alternate"), owner, path)
|
|
1275
1491
|
} else if (kind == "TryStatement") {
|
|
1276
1492
|
visit(Reflect.get(statement, "body"), owner, path)
|
|
1277
|
-
|
|
1493
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) visit(Reflect.get(handler, "catchBody"), owner, path)
|
|
1494
|
+
visit(Reflect.get(statement, "finallyBody"), owner, path)
|
|
1278
1495
|
}
|
|
1279
1496
|
}
|
|
1280
1497
|
}
|
|
@@ -1536,30 +1753,55 @@ function validateStatement(statement, language, ownerLocation, loopContext, acti
|
|
|
1536
1753
|
validateExpression(raised.message, language, raised.location, false, new Set(), externalDeclarationIds)
|
|
1537
1754
|
return
|
|
1538
1755
|
}
|
|
1756
|
+
if (kind == "RethrowStatement") {
|
|
1757
|
+
const rethrow = /** @type {import("../semantic/types.js").RethrowStatement} */ (statement)
|
|
1758
|
+
const rethrowLocation = requireSemanticLocation(ownLocation, language, "RethrowStatement location", ownerLocation)
|
|
1759
|
+
|
|
1760
|
+
if (Object.keys(rethrow).filter((key) => key != "sourceProvenance").sort().join(",") != "binding,bindingNode,kind,location" ||
|
|
1761
|
+
typeof rethrow.binding != "string" || rethrow.binding.length == 0 ||
|
|
1762
|
+
!rethrow.bindingNode || rethrow.bindingNode.kind != "CatchBinding" || rethrow.bindingNode.name != rethrow.binding) {
|
|
1763
|
+
unsupportedCapability(language, "malformed RethrowStatement", rethrowLocation)
|
|
1764
|
+
}
|
|
1765
|
+
validateTargetBindingIdentifier(language, rethrow.binding, "catch binding", rethrowLocation)
|
|
1766
|
+
return
|
|
1767
|
+
}
|
|
1539
1768
|
if (kind == "TryStatement") {
|
|
1540
|
-
const
|
|
1769
|
+
const tryStatement = /** @type {import("../semantic/types.js").TryStatement} */ (statement)
|
|
1541
1770
|
const tryLocation = requireSemanticLocation(ownLocation, language, "TryStatement location", ownerLocation)
|
|
1542
|
-
const fields = Object.keys(
|
|
1771
|
+
const fields = Object.keys(tryStatement).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1543
1772
|
|
|
1544
|
-
if (fields != "body,
|
|
1773
|
+
if (fields != "body,handlers,kind,location" && fields != "body,finallyBody,handlers,kind,location") {
|
|
1545
1774
|
unsupportedCapability(language, "malformed TryStatement", tryLocation)
|
|
1546
1775
|
}
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
const bindingFields = binding && typeof binding == "object" && !Array.isArray(binding)
|
|
1550
|
-
? Object.keys(binding).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1551
|
-
const bindingLocation = requireSemanticLocation(binding?.location, language, "catch binding location", tryLocation)
|
|
1552
|
-
|
|
1553
|
-
if (!binding || binding.kind != "CatchBinding" || bindingFields != "kind,location,mutable,name,type" || binding.mutable !== false) {
|
|
1554
|
-
unsupportedCapability(language, "malformed catch binding", bindingLocation)
|
|
1776
|
+
if (!Array.isArray(tryStatement.handlers) || (tryStatement.handlers.length == 0 && !tryStatement.finallyBody)) {
|
|
1777
|
+
unsupportedCapability(language, "TryStatement without an ordered handler list", tryLocation)
|
|
1555
1778
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1779
|
+
validateBlock(tryStatement.body, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1780
|
+
for (const handler of tryStatement.handlers) {
|
|
1781
|
+
const handlerLocation = requireSemanticLocation(handler?.location, language, "catch handler location", tryLocation)
|
|
1782
|
+
const handlerFields = handler && typeof handler == "object" && !Array.isArray(handler)
|
|
1783
|
+
? Object.keys(handler).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1784
|
+
|
|
1785
|
+
if (!handler || handler.kind != "CatchHandler" || handlerFields != "catchBinding,catchBody,catchType,kind,location") {
|
|
1786
|
+
unsupportedCapability(language, "malformed catch handler", handlerLocation)
|
|
1787
|
+
}
|
|
1788
|
+
validateErrorTypeShape(handler.catchType, language, handlerLocation)
|
|
1789
|
+
const binding = handler.catchBinding
|
|
1790
|
+
const bindingFields = binding && typeof binding == "object" && !Array.isArray(binding)
|
|
1791
|
+
? Object.keys(binding).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1792
|
+
const bindingLocation = requireSemanticLocation(binding?.location, language, "catch binding location", handlerLocation)
|
|
1793
|
+
|
|
1794
|
+
if (!binding || binding.kind != "CatchBinding" || bindingFields != "kind,location,mutable,name,type" || binding.mutable !== false) {
|
|
1795
|
+
unsupportedCapability(language, "malformed catch binding", bindingLocation)
|
|
1796
|
+
}
|
|
1797
|
+
validateTargetBindingIdentifier(language, binding.name, "catch binding", bindingLocation)
|
|
1798
|
+
validateErrorTypeShape(binding.type, language, bindingLocation)
|
|
1799
|
+
if (binding.type.declarationId != handler.catchType.declarationId) {
|
|
1800
|
+
unsupportedCapability(language, "catch binding type mismatch", bindingLocation)
|
|
1801
|
+
}
|
|
1802
|
+
validateBlock(handler.catchBody, language, handlerLocation, loopContext, activePath, externalDeclarationIds)
|
|
1560
1803
|
}
|
|
1561
|
-
validateBlock(
|
|
1562
|
-
validateBlock(handler.catchBody, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1804
|
+
if (tryStatement.finallyBody) validateBlock(tryStatement.finallyBody, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1563
1805
|
return
|
|
1564
1806
|
}
|
|
1565
1807
|
|
|
@@ -2049,6 +2291,14 @@ export function emitType(writer, type, path, language, javaBoxed = false) {
|
|
|
2049
2291
|
})
|
|
2050
2292
|
return
|
|
2051
2293
|
}
|
|
2294
|
+
if (type.kind == "InterfaceType") {
|
|
2295
|
+
const declaration = writer.interfaceForId(type.declarationId)
|
|
2296
|
+
|
|
2297
|
+
writer.mapped(writer.interfaceNameForId(type.declarationId), {
|
|
2298
|
+
mappingKind: "exact", name: declaration.name, node: type, path, role: "type"
|
|
2299
|
+
})
|
|
2300
|
+
return
|
|
2301
|
+
}
|
|
2052
2302
|
if (type.kind == "OwnedResourceType") {
|
|
2053
2303
|
const resource = writer.resourceForId(type.resourceId)
|
|
2054
2304
|
writer.mapped(writer.capabilityNominalName(resource.name), {mappingKind: "exact", name: resource.name, node: type, path, role: "type"})
|
|
@@ -2664,7 +2914,8 @@ function blockContainsSignProducingOperation(block) {
|
|
|
2664
2914
|
return expressionContainsSignProducingOperation(statement.condition) || blockContainsSignProducingOperation(statement.body)
|
|
2665
2915
|
}
|
|
2666
2916
|
if (statement.kind == "TryStatement") {
|
|
2667
|
-
return blockContainsSignProducingOperation(statement.body) ||
|
|
2917
|
+
return blockContainsSignProducingOperation(statement.body) || (statement.handlers ?? []).some((handler) => blockContainsSignProducingOperation(handler.catchBody)) ||
|
|
2918
|
+
(statement.finallyBody ? blockContainsSignProducingOperation(statement.finallyBody) : false)
|
|
2668
2919
|
}
|
|
2669
2920
|
if (statement.kind == "RaiseStatement") return expressionContainsSignProducingOperation(statement.error.message)
|
|
2670
2921
|
if (statement.kind == "LocalDeclaration") return expressionContainsSignProducingOperation(statement.initializer)
|
|
@@ -376,10 +376,14 @@ function emitStatement(writer, statement, indent, path, canonicalizeZero) {
|
|
|
376
376
|
writer.synthetic(")\n", "error construction close", [statement.error], [`${path}/error`])
|
|
377
377
|
return
|
|
378
378
|
}
|
|
379
|
+
if (statement.kind == "RethrowStatement") {
|
|
380
|
+
writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
|
|
381
|
+
writer.synthetic(" ", "throw spacing", [statement], [path])
|
|
382
|
+
writer.mapped(statement.binding, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
383
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
384
|
+
return
|
|
385
|
+
}
|
|
379
386
|
if (statement.kind == "TryStatement") {
|
|
380
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
381
|
-
const targetName = writer.errorNameForId(statement.catchType.declarationId)
|
|
382
|
-
|
|
383
387
|
writer.mapped("try", {mappingKind: "anchor", node: statement, path})
|
|
384
388
|
writer.synthetic(" ", "try spacing", [statement], [path])
|
|
385
389
|
writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
@@ -387,21 +391,15 @@ function emitStatement(writer, statement, indent, path, canonicalizeZero) {
|
|
|
387
391
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`, canonicalizeZero)
|
|
388
392
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
389
393
|
writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
writer.synthetic(`)) {\n${indent} throw `, "unmatched error propagation", [statement], [path])
|
|
400
|
-
writer.mapped(statement.catchBinding.name, {mappingKind: "exact", node: statement.catchBinding, path: `${path}/catchBinding`, role: "name"})
|
|
401
|
-
writer.synthetic(`\n${indent} }\n`, "unmatched error propagation", [statement], [path])
|
|
402
|
-
emitBlock(writer, statement.catchBody, `${indent} `, `${path}/catchBody`, canonicalizeZero)
|
|
403
|
-
writer.synthetic(indent, "indentation", [statement], [path])
|
|
404
|
-
writer.mapped("}", {mappingKind: "anchor", node: statement.catchBody, path: `${path}/catchBody`})
|
|
394
|
+
if (statement.handlers.length > 0) emitTypeScriptHandler(writer, statement, 0, indent, path, canonicalizeZero)
|
|
395
|
+
if (statement.finallyBody) {
|
|
396
|
+
writer.synthetic(" ", "finally spacing", [statement], [path])
|
|
397
|
+
writer.mapped("finally", {mappingKind: "anchor", node: statement, path, role: "finally"})
|
|
398
|
+
writer.synthetic(" {\n", "finally scaffolding", [statement], [path])
|
|
399
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`, canonicalizeZero)
|
|
400
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
401
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
402
|
+
}
|
|
405
403
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
406
404
|
return
|
|
407
405
|
}
|
|
@@ -453,6 +451,73 @@ function emitStatement(writer, statement, indent, path, canonicalizeZero) {
|
|
|
453
451
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
454
452
|
}
|
|
455
453
|
|
|
454
|
+
/**
|
|
455
|
+
* Emits one ordered TypeScript catch handler with its exact nominal guard.
|
|
456
|
+
*
|
|
457
|
+
* TypeScript has no sibling catch clauses, so handler k > 0 hangs off handler k - 1's
|
|
458
|
+
* unmatched rethrow inside a nested try. Each handler body sits outside its own guard,
|
|
459
|
+
* so a raise from one handler body escapes outward instead of being caught by a later
|
|
460
|
+
* sibling handler, exactly like native ordered multi-catch.
|
|
461
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
462
|
+
* @param {import("../semantic/types.js").TryStatement} statement - Semantic try statement.
|
|
463
|
+
* @param {number} index - Zero-based ordered handler index.
|
|
464
|
+
* @param {string} indent - Leading indentation for this catch clause.
|
|
465
|
+
* @param {string} path - Exact JSON Pointer for the owning try statement.
|
|
466
|
+
* @param {boolean} canonicalizeZero - Whether scalar output canonicalizes zero.
|
|
467
|
+
* @returns {void}
|
|
468
|
+
*/
|
|
469
|
+
function emitTypeScriptHandler(writer, statement, index, indent, path, canonicalizeZero) {
|
|
470
|
+
const handler = statement.handlers[index]
|
|
471
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
472
|
+
const bodyIndent = `${indent} `
|
|
473
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
474
|
+
const targetName = writer.errorNameForId(handler.catchType.declarationId)
|
|
475
|
+
|
|
476
|
+
writer.synthetic(" ", "catch spacing", [statement], [path])
|
|
477
|
+
writer.mapped("catch", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
478
|
+
writer.synthetic(" (", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
479
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
480
|
+
writer.synthetic(") {\n", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
481
|
+
writer.synthetic(`${bodyIndent}if (!(`, "exact catch guard", [handler], [handlerPath])
|
|
482
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
483
|
+
writer.synthetic(" instanceof ", "exact catch guard", [handler], [handlerPath])
|
|
484
|
+
writer.mapped(targetName, {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
485
|
+
writer.synthetic(")) {\n", "exact catch guard", [handler], [handlerPath])
|
|
486
|
+
if (index + 1 < statement.handlers.length) {
|
|
487
|
+
emitTypeScriptDispatch(writer, statement, index, indent, path, canonicalizeZero)
|
|
488
|
+
writer.synthetic(`${bodyIndent}} else {\n`, "matched error handling", [handler], [handlerPath])
|
|
489
|
+
emitBlock(writer, handler.catchBody, `${bodyIndent} `, `${handlerPath}/catchBody`, canonicalizeZero)
|
|
490
|
+
writer.synthetic(`${bodyIndent}}\n`, "matched error handling close", [handler], [handlerPath])
|
|
491
|
+
} else {
|
|
492
|
+
writer.synthetic(`${bodyIndent} throw `, "unmatched error propagation", [handler], [handlerPath])
|
|
493
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
494
|
+
writer.synthetic(`\n${bodyIndent}}\n`, "unmatched error propagation", [handler], [handlerPath])
|
|
495
|
+
emitBlock(writer, handler.catchBody, bodyIndent, `${handlerPath}/catchBody`, canonicalizeZero)
|
|
496
|
+
}
|
|
497
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
498
|
+
writer.mapped("}", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Emits the nested re-dispatch of one unmatched caught value to the remaining ordered handlers.
|
|
503
|
+
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|
|
504
|
+
* @param {import("../semantic/types.js").TryStatement} statement - Semantic try statement.
|
|
505
|
+
* @param {number} index - Index of the handler whose value is re-dispatched.
|
|
506
|
+
* @param {string} indent - Leading indentation for this dispatch try.
|
|
507
|
+
* @param {string} path - Exact JSON Pointer for the owning try statement.
|
|
508
|
+
* @param {boolean} canonicalizeZero - Whether scalar output canonicalizes zero.
|
|
509
|
+
* @returns {void}
|
|
510
|
+
*/
|
|
511
|
+
function emitTypeScriptDispatch(writer, statement, index, indent, path, canonicalizeZero) {
|
|
512
|
+
writer.synthetic(`${indent} try {\n`, "ordered handler dispatch", [statement], [path])
|
|
513
|
+
writer.synthetic(`${indent} throw `, "ordered handler dispatch", [statement], [path])
|
|
514
|
+
writer.mapped(statement.handlers[index].catchBinding.name, {
|
|
515
|
+
mappingKind: "exact", node: statement.handlers[index].catchBinding, path: `${path}/handlers/${index}/catchBinding`, role: "name"
|
|
516
|
+
})
|
|
517
|
+
writer.synthetic(`\n${indent} }`, "ordered handler dispatch", [statement], [path])
|
|
518
|
+
emitTypeScriptHandler(writer, statement, index + 1, `${indent} `, path, canonicalizeZero)
|
|
519
|
+
}
|
|
520
|
+
|
|
456
521
|
/**
|
|
457
522
|
* Emits one TypeScript local statement.
|
|
458
523
|
* @param {import("./writer.js").SourceWriter} writer - Source-aware writer.
|