semantifold 0.12.0 → 0.13.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 +1 -0
- package/build/src/backends/effects.js +3 -2
- package/build/src/backends/java.js +33 -14
- package/build/src/backends/javascript.js +83 -18
- package/build/src/backends/php.js +33 -14
- package/build/src/backends/ruby.js +37 -12
- package/build/src/backends/shared.d.ts.map +1 -1
- package/build/src/backends/shared.js +91 -36
- package/build/src/backends/typescript.js +83 -18
- package/build/src/frontends/java.d.ts +8 -0
- package/build/src/frontends/java.d.ts.map +1 -1
- package/build/src/frontends/java.js +77 -33
- package/build/src/frontends/javascript-typescript.d.ts +2 -0
- package/build/src/frontends/javascript-typescript.d.ts.map +1 -1
- package/build/src/frontends/javascript-typescript.js +118 -37
- package/build/src/frontends/php.d.ts +8 -0
- package/build/src/frontends/php.d.ts.map +1 -1
- package/build/src/frontends/php.js +68 -29
- package/build/src/frontends/ruby.d.ts +3 -0
- package/build/src/frontends/ruby.d.ts.map +1 -1
- package/build/src/frontends/ruby.js +103 -30
- package/build/src/semantic/effects.d.ts.map +1 -1
- package/build/src/semantic/effects.js +18 -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 +26 -7
- package/build/src/semantic/types.d.ts +76 -16
- package/build/src/semantic/types.d.ts.map +1 -1
- package/build/src/semantic/types.js +23 -7
- package/build/src/semantic/validate.d.ts.map +1 -1
- package/build/src/semantic/validate.js +105 -34
- package/docs/adr/20261026-typed-error-handling-extension.md +55 -0
- package/docs/adr-numeric-tower.md +156 -0
- package/docs/architecture.md +1 -1
- package/docs/language-support.md +7 -7
- package/docs/testing.md +5 -3
- package/package.json +1 -1
- package/src/backends/effects.js +2 -1
- package/src/backends/java.js +33 -14
- package/src/backends/javascript.js +83 -18
- package/src/backends/php.js +33 -14
- package/src/backends/ruby.js +36 -12
- package/src/backends/shared.js +88 -41
- package/src/backends/typescript.js +83 -18
- package/src/frontends/java.js +82 -33
- package/src/frontends/javascript-typescript.js +130 -38
- package/src/frontends/php.js +68 -27
- package/src/frontends/ruby.js +107 -28
- package/src/semantic/effects.js +17 -5
- package/src/semantic/lifetime.js +29 -3
- package/src/semantic/provenance.js +27 -12
- package/src/semantic/types.js +24 -6
- package/src/semantic/validate.js +104 -35
package/src/backends/php.js
CHANGED
|
@@ -489,9 +489,15 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
489
489
|
writer.synthetic(");\n", "error construction close", [statement.error], [`${path}/error`])
|
|
490
490
|
return
|
|
491
491
|
}
|
|
492
|
+
if (statement.kind == "RethrowStatement") {
|
|
493
|
+
writer.mapped("throw", {mappingKind: "anchor", node: statement, path})
|
|
494
|
+
writer.synthetic(" ", "throw spacing", [statement], [path])
|
|
495
|
+
writer.mapped(`$${statement.binding}`, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
496
|
+
writer.mapped(";", {mappingKind: "anchor", node: statement, path})
|
|
497
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
498
|
+
return
|
|
499
|
+
}
|
|
492
500
|
if (statement.kind == "TryStatement") {
|
|
493
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
494
|
-
|
|
495
501
|
writer.mapped("try", {mappingKind: "anchor", node: statement, path})
|
|
496
502
|
writer.synthetic(" ", "try spacing", [statement], [path])
|
|
497
503
|
writer.mapped("{", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
@@ -499,18 +505,31 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
499
505
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
|
|
500
506
|
writer.synthetic(indent, "indentation", [statement], [path])
|
|
501
507
|
writer.mapped("}", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
508
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
509
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
510
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
511
|
+
|
|
512
|
+
writer.synthetic(" ", "catch spacing", [handler], [handlerPath])
|
|
513
|
+
writer.mapped("catch", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
514
|
+
writer.synthetic(" (", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
515
|
+
writer.mapped(writer.errorNameForId(handler.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
516
|
+
writer.synthetic(" ", "catch binding spacing", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
517
|
+
writer.mapped(`$${handler.catchBinding.name}`, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
518
|
+
writer.synthetic(") ", "catch binding scaffolding", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
519
|
+
writer.mapped("{", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
520
|
+
writer.synthetic("\n", "line break", [handler], [handlerPath])
|
|
521
|
+
emitBlock(writer, handler.catchBody, `${indent} `, `${handlerPath}/catchBody`)
|
|
522
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
523
|
+
writer.mapped("}", {mappingKind: "anchor", node: handler.catchBody, path: `${handlerPath}/catchBody`})
|
|
524
|
+
}
|
|
525
|
+
if (statement.finallyBody) {
|
|
526
|
+
writer.synthetic(" ", "finally spacing", [statement], [path])
|
|
527
|
+
writer.mapped("finally", {mappingKind: "anchor", node: statement, path, role: "finally"})
|
|
528
|
+
writer.synthetic(" {\n", "finally scaffolding", [statement], [path])
|
|
529
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`)
|
|
530
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
531
|
+
writer.mapped("}", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
532
|
+
}
|
|
514
533
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
515
534
|
return
|
|
516
535
|
}
|
package/src/backends/ruby.js
CHANGED
|
@@ -364,22 +364,46 @@ function emitStatement(writer, statement, indent, path) {
|
|
|
364
364
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
365
365
|
return
|
|
366
366
|
}
|
|
367
|
+
if (statement.kind == "RethrowStatement") {
|
|
368
|
+
writer.mapped("raise", {mappingKind: "anchor", node: statement, path})
|
|
369
|
+
writer.synthetic(" ", "raise spacing", [statement], [path])
|
|
370
|
+
writer.mapped(statement.binding, {mappingKind: "exact", node: statement.bindingNode, path: `${path}/bindingNode`, role: "name"})
|
|
371
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
372
|
+
return
|
|
373
|
+
}
|
|
367
374
|
if (statement.kind == "TryStatement") {
|
|
368
|
-
const caught = writer.errorForId(statement.catchType.declarationId)
|
|
369
|
-
|
|
370
375
|
writer.mapped("begin", {mappingKind: "anchor", node: statement, path})
|
|
371
376
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
372
377
|
emitBlock(writer, statement.body, `${indent} `, `${path}/body`)
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
378
|
+
for (const [index, handler] of statement.handlers.entries()) {
|
|
379
|
+
const handlerPath = `${path}/handlers/${index}`
|
|
380
|
+
const caught = writer.errorForId(handler.catchType.declarationId)
|
|
381
|
+
|
|
382
|
+
writer.synthetic(indent, "indentation", [handler], [handlerPath])
|
|
383
|
+
writer.mapped("rescue", {mappingKind: "anchor", node: handler, path: handlerPath, role: "catch"})
|
|
384
|
+
writer.synthetic(" ", "rescue spacing", [handler], [handlerPath])
|
|
385
|
+
writer.mapped(writer.errorNameForId(handler.catchType.declarationId), {mappingKind: "exact", name: caught.name, node: handler.catchType, path: `${handlerPath}/catchType`, role: "type"})
|
|
386
|
+
writer.synthetic(" => ", "catch binding separator", [handler.catchBinding], [`${handlerPath}/catchBinding`])
|
|
387
|
+
writer.mapped(handler.catchBinding.name, {mappingKind: "exact", node: handler.catchBinding, path: `${handlerPath}/catchBinding`, role: "name"})
|
|
388
|
+
writer.synthetic("\n", "line break", [handler], [handlerPath])
|
|
389
|
+
emitBlock(writer, handler.catchBody, `${indent} `, `${handlerPath}/catchBody`)
|
|
390
|
+
}
|
|
391
|
+
if (statement.finallyBody) {
|
|
392
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
393
|
+
writer.mapped("ensure", {mappingKind: "anchor", node: statement, path, role: "ensure"})
|
|
394
|
+
writer.synthetic("\n", "line break", [statement], [path])
|
|
395
|
+
emitBlock(writer, statement.finallyBody, `${indent} `, `${path}/finallyBody`)
|
|
396
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
397
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement.finallyBody, path: `${path}/finallyBody`})
|
|
398
|
+
} else if (statement.handlers.length > 0) {
|
|
399
|
+
const lastIndex = statement.handlers.length - 1
|
|
400
|
+
|
|
401
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
402
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement.handlers[lastIndex].catchBody, path: `${path}/handlers/${lastIndex}/catchBody`})
|
|
403
|
+
} else {
|
|
404
|
+
writer.synthetic(indent, "indentation", [statement], [path])
|
|
405
|
+
writer.mapped("end", {mappingKind: "anchor", node: statement.body, path: `${path}/body`})
|
|
406
|
+
}
|
|
383
407
|
writer.synthetic("\n", "line break", [statement], [path])
|
|
384
408
|
return
|
|
385
409
|
}
|
package/src/backends/shared.js
CHANGED
|
@@ -627,7 +627,8 @@ function rejectBlockConditionLoop(block, language, seen = new Set()) {
|
|
|
627
627
|
rejectBlockConditionLoop(Reflect.get(statement, "body"), language, seen)
|
|
628
628
|
} else if (kind == "TryStatement") {
|
|
629
629
|
rejectBlockConditionLoop(Reflect.get(statement, "body"), language, seen)
|
|
630
|
-
|
|
630
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) rejectBlockConditionLoop(Reflect.get(handler, "catchBody"), language, seen)
|
|
631
|
+
rejectBlockConditionLoop(Reflect.get(statement, "finallyBody"), language, seen)
|
|
631
632
|
}
|
|
632
633
|
}
|
|
633
634
|
}
|
|
@@ -658,7 +659,8 @@ function rejectBlockIteration(block, language, seen = new Set()) {
|
|
|
658
659
|
rejectBlockIteration(Reflect.get(statement, "alternate"), language, seen)
|
|
659
660
|
} else if (kind == "TryStatement") {
|
|
660
661
|
rejectBlockIteration(Reflect.get(statement, "body"), language, seen)
|
|
661
|
-
|
|
662
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) rejectBlockIteration(Reflect.get(handler, "catchBody"), language, seen)
|
|
663
|
+
rejectBlockIteration(Reflect.get(statement, "finallyBody"), language, seen)
|
|
662
664
|
}
|
|
663
665
|
}
|
|
664
666
|
}
|
|
@@ -713,7 +715,8 @@ function rejectBlockOptionalTypes(block, language) {
|
|
|
713
715
|
if (statement.kind == "WhileStatement") rejectBlockOptionalTypes(statement.body, language)
|
|
714
716
|
if (statement.kind == "TryStatement") {
|
|
715
717
|
rejectBlockOptionalTypes(statement.body, language)
|
|
716
|
-
|
|
718
|
+
for (const handler of statement.handlers ?? []) rejectBlockOptionalTypes(handler.catchBody, language)
|
|
719
|
+
if (statement.finallyBody) rejectBlockOptionalTypes(statement.finallyBody, language)
|
|
717
720
|
}
|
|
718
721
|
}
|
|
719
722
|
}
|
|
@@ -788,7 +791,8 @@ function rejectBlockCollectionTypes(block, language) {
|
|
|
788
791
|
if (statement.kind == "WhileStatement") rejectBlockCollectionTypes(statement.body, language)
|
|
789
792
|
if (statement.kind == "TryStatement") {
|
|
790
793
|
rejectBlockCollectionTypes(statement.body, language)
|
|
791
|
-
|
|
794
|
+
for (const handler of statement.handlers ?? []) rejectBlockCollectionTypes(handler.catchBody, language)
|
|
795
|
+
if (statement.finallyBody) rejectBlockCollectionTypes(statement.finallyBody, language)
|
|
792
796
|
}
|
|
793
797
|
}
|
|
794
798
|
}
|
|
@@ -856,9 +860,13 @@ function validateScaffoldingNames(module, language) {
|
|
|
856
860
|
}
|
|
857
861
|
}
|
|
858
862
|
}
|
|
859
|
-
if (statement.kind == "TryStatement"
|
|
860
|
-
|
|
861
|
-
|
|
863
|
+
if (statement.kind == "TryStatement") {
|
|
864
|
+
for (const handler of statement.handlers ?? []) {
|
|
865
|
+
if (ownedPrintReceiverNames.has(handler.catchBinding.name)) {
|
|
866
|
+
unsupportedCapability(language, `${owner} catch binding '${handler.catchBinding.name}' captures backend scaffolding`,
|
|
867
|
+
handler.catchBinding.location)
|
|
868
|
+
}
|
|
869
|
+
}
|
|
862
870
|
}
|
|
863
871
|
}
|
|
864
872
|
}
|
|
@@ -878,9 +886,13 @@ function validateScaffoldingNames(module, language) {
|
|
|
878
886
|
}
|
|
879
887
|
}
|
|
880
888
|
}
|
|
881
|
-
if (statement.kind == "TryStatement"
|
|
882
|
-
|
|
883
|
-
|
|
889
|
+
if (statement.kind == "TryStatement") {
|
|
890
|
+
for (const handler of statement.handlers ?? []) {
|
|
891
|
+
if (ownedEntryNames.has(handler.catchBinding.name)) {
|
|
892
|
+
unsupportedCapability(language, `entry catch binding '${handler.catchBinding.name}' captures backend scaffolding`,
|
|
893
|
+
handler.catchBinding.location)
|
|
894
|
+
}
|
|
895
|
+
}
|
|
884
896
|
}
|
|
885
897
|
}
|
|
886
898
|
for (const declaration of module.functions) {
|
|
@@ -949,12 +961,13 @@ function validateRecordConstructorNames(module, language) {
|
|
|
949
961
|
}
|
|
950
962
|
}
|
|
951
963
|
}
|
|
952
|
-
if (statement.kind == "TryStatement"
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
964
|
+
if (statement.kind == "TryStatement") {
|
|
965
|
+
const captured = (statement.handlers ?? []).find((handler) => recordNames.has(handler.catchBinding.name))
|
|
966
|
+
|
|
967
|
+
if (captured) {
|
|
968
|
+
unsupportedCapability(language, `${owner} catch binding '${captured.catchBinding.name}' captures nominal constructor`,
|
|
969
|
+
bindingNameLocation(captured.catchBinding))
|
|
970
|
+
}
|
|
958
971
|
}
|
|
959
972
|
}
|
|
960
973
|
}
|
|
@@ -1052,12 +1065,15 @@ function validateJavaUtilFactoryNames(module) {
|
|
|
1052
1065
|
}
|
|
1053
1066
|
if (statement.kind == "TryStatement") {
|
|
1054
1067
|
validateBlockNames(statement.body, capture, owner)
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1068
|
+
for (const handler of statement.handlers ?? []) {
|
|
1069
|
+
const catchCapture = handler.catchBinding.name == "java" ? {
|
|
1070
|
+
detail: `${owner} catch binding 'java' captures java.util factory syntax`,
|
|
1071
|
+
location: handler.catchBinding.location
|
|
1072
|
+
} : capture
|
|
1059
1073
|
|
|
1060
|
-
|
|
1074
|
+
validateBlockNames(handler.catchBody, catchCapture, owner)
|
|
1075
|
+
}
|
|
1076
|
+
if (statement.finallyBody) validateBlockNames(statement.finallyBody, capture, owner)
|
|
1061
1077
|
}
|
|
1062
1078
|
}
|
|
1063
1079
|
}
|
|
@@ -1220,7 +1236,11 @@ function allStatements(block) {
|
|
|
1220
1236
|
return [statement, ...allStatements(statement.body)]
|
|
1221
1237
|
}
|
|
1222
1238
|
if (statement.kind == "WhileStatement") return [statement, ...allStatements(statement.body)]
|
|
1223
|
-
if (statement.kind == "TryStatement")
|
|
1239
|
+
if (statement.kind == "TryStatement") {
|
|
1240
|
+
return [statement, ...allStatements(statement.body),
|
|
1241
|
+
...(statement.handlers ?? []).flatMap((handler) => allStatements(handler.catchBody)),
|
|
1242
|
+
...(statement.finallyBody ? allStatements(statement.finallyBody) : [])]
|
|
1243
|
+
}
|
|
1224
1244
|
|
|
1225
1245
|
return [statement]
|
|
1226
1246
|
})
|
|
@@ -1274,7 +1294,8 @@ function collectLoopOwners(module, language) {
|
|
|
1274
1294
|
visit(Reflect.get(statement, "alternate"), owner, path)
|
|
1275
1295
|
} else if (kind == "TryStatement") {
|
|
1276
1296
|
visit(Reflect.get(statement, "body"), owner, path)
|
|
1277
|
-
|
|
1297
|
+
for (const handler of Reflect.get(statement, "handlers") ?? []) visit(Reflect.get(handler, "catchBody"), owner, path)
|
|
1298
|
+
visit(Reflect.get(statement, "finallyBody"), owner, path)
|
|
1278
1299
|
}
|
|
1279
1300
|
}
|
|
1280
1301
|
}
|
|
@@ -1536,30 +1557,55 @@ function validateStatement(statement, language, ownerLocation, loopContext, acti
|
|
|
1536
1557
|
validateExpression(raised.message, language, raised.location, false, new Set(), externalDeclarationIds)
|
|
1537
1558
|
return
|
|
1538
1559
|
}
|
|
1560
|
+
if (kind == "RethrowStatement") {
|
|
1561
|
+
const rethrow = /** @type {import("../semantic/types.js").RethrowStatement} */ (statement)
|
|
1562
|
+
const rethrowLocation = requireSemanticLocation(ownLocation, language, "RethrowStatement location", ownerLocation)
|
|
1563
|
+
|
|
1564
|
+
if (Object.keys(rethrow).filter((key) => key != "sourceProvenance").sort().join(",") != "binding,bindingNode,kind,location" ||
|
|
1565
|
+
typeof rethrow.binding != "string" || rethrow.binding.length == 0 ||
|
|
1566
|
+
!rethrow.bindingNode || rethrow.bindingNode.kind != "CatchBinding" || rethrow.bindingNode.name != rethrow.binding) {
|
|
1567
|
+
unsupportedCapability(language, "malformed RethrowStatement", rethrowLocation)
|
|
1568
|
+
}
|
|
1569
|
+
validateTargetBindingIdentifier(language, rethrow.binding, "catch binding", rethrowLocation)
|
|
1570
|
+
return
|
|
1571
|
+
}
|
|
1539
1572
|
if (kind == "TryStatement") {
|
|
1540
|
-
const
|
|
1573
|
+
const tryStatement = /** @type {import("../semantic/types.js").TryStatement} */ (statement)
|
|
1541
1574
|
const tryLocation = requireSemanticLocation(ownLocation, language, "TryStatement location", ownerLocation)
|
|
1542
|
-
const fields = Object.keys(
|
|
1575
|
+
const fields = Object.keys(tryStatement).filter((key) => key != "sourceProvenance").sort().join(",")
|
|
1543
1576
|
|
|
1544
|
-
if (fields != "body,
|
|
1577
|
+
if (fields != "body,handlers,kind,location" && fields != "body,finallyBody,handlers,kind,location") {
|
|
1545
1578
|
unsupportedCapability(language, "malformed TryStatement", tryLocation)
|
|
1546
1579
|
}
|
|
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)
|
|
1580
|
+
if (!Array.isArray(tryStatement.handlers) || (tryStatement.handlers.length == 0 && !tryStatement.finallyBody)) {
|
|
1581
|
+
unsupportedCapability(language, "TryStatement without an ordered handler list", tryLocation)
|
|
1555
1582
|
}
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1583
|
+
validateBlock(tryStatement.body, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1584
|
+
for (const handler of tryStatement.handlers) {
|
|
1585
|
+
const handlerLocation = requireSemanticLocation(handler?.location, language, "catch handler location", tryLocation)
|
|
1586
|
+
const handlerFields = handler && typeof handler == "object" && !Array.isArray(handler)
|
|
1587
|
+
? Object.keys(handler).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1588
|
+
|
|
1589
|
+
if (!handler || handler.kind != "CatchHandler" || handlerFields != "catchBinding,catchBody,catchType,kind,location") {
|
|
1590
|
+
unsupportedCapability(language, "malformed catch handler", handlerLocation)
|
|
1591
|
+
}
|
|
1592
|
+
validateErrorTypeShape(handler.catchType, language, handlerLocation)
|
|
1593
|
+
const binding = handler.catchBinding
|
|
1594
|
+
const bindingFields = binding && typeof binding == "object" && !Array.isArray(binding)
|
|
1595
|
+
? Object.keys(binding).filter((key) => key != "sourceProvenance").sort().join(",") : ""
|
|
1596
|
+
const bindingLocation = requireSemanticLocation(binding?.location, language, "catch binding location", handlerLocation)
|
|
1597
|
+
|
|
1598
|
+
if (!binding || binding.kind != "CatchBinding" || bindingFields != "kind,location,mutable,name,type" || binding.mutable !== false) {
|
|
1599
|
+
unsupportedCapability(language, "malformed catch binding", bindingLocation)
|
|
1600
|
+
}
|
|
1601
|
+
validateTargetBindingIdentifier(language, binding.name, "catch binding", bindingLocation)
|
|
1602
|
+
validateErrorTypeShape(binding.type, language, bindingLocation)
|
|
1603
|
+
if (binding.type.declarationId != handler.catchType.declarationId) {
|
|
1604
|
+
unsupportedCapability(language, "catch binding type mismatch", bindingLocation)
|
|
1605
|
+
}
|
|
1606
|
+
validateBlock(handler.catchBody, language, handlerLocation, loopContext, activePath, externalDeclarationIds)
|
|
1560
1607
|
}
|
|
1561
|
-
validateBlock(
|
|
1562
|
-
validateBlock(handler.catchBody, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1608
|
+
if (tryStatement.finallyBody) validateBlock(tryStatement.finallyBody, language, tryLocation, loopContext, activePath, externalDeclarationIds)
|
|
1563
1609
|
return
|
|
1564
1610
|
}
|
|
1565
1611
|
|
|
@@ -2664,7 +2710,8 @@ function blockContainsSignProducingOperation(block) {
|
|
|
2664
2710
|
return expressionContainsSignProducingOperation(statement.condition) || blockContainsSignProducingOperation(statement.body)
|
|
2665
2711
|
}
|
|
2666
2712
|
if (statement.kind == "TryStatement") {
|
|
2667
|
-
return blockContainsSignProducingOperation(statement.body) ||
|
|
2713
|
+
return blockContainsSignProducingOperation(statement.body) || (statement.handlers ?? []).some((handler) => blockContainsSignProducingOperation(handler.catchBody)) ||
|
|
2714
|
+
(statement.finallyBody ? blockContainsSignProducingOperation(statement.finallyBody) : false)
|
|
2668
2715
|
}
|
|
2669
2716
|
if (statement.kind == "RaiseStatement") return expressionContainsSignProducingOperation(statement.error.message)
|
|
2670
2717
|
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.
|
package/src/frontends/java.js
CHANGED
|
@@ -4,7 +4,7 @@ import {parser} from "@lezer/java"
|
|
|
4
4
|
import {SemantifoldDiagnostic, missingType, unsupportedSyntax} from "../diagnostic.js"
|
|
5
5
|
import {locationFromOffsets, moduleLocation} from "../semantic/location.js"
|
|
6
6
|
import {withAdaptedOperation} from "../semantic/operators.js"
|
|
7
|
-
import {withParserRanges} from "../semantic/provenance.js"
|
|
7
|
+
import {setParserRanges, withParserRanges} from "../semantic/provenance.js"
|
|
8
8
|
import {hasOnlyUnicodeScalars} from "../semantic/scalars.js"
|
|
9
9
|
import {isTask034ResourceProbe} from "../semantic/capabilities.js"
|
|
10
10
|
import {hasExactEffectSupport} from "../backends/effects.js"
|
|
@@ -41,6 +41,8 @@ const referenceMethodHooks = new Set(["clone", "equals", "finalize", "getClass",
|
|
|
41
41
|
/**
|
|
42
42
|
* @typedef JavaConversionContext
|
|
43
43
|
* @property {Map<string, import("../semantic/types.js").SemanticBindingType>} bindings - Explicitly typed visible bindings.
|
|
44
|
+
* @property {Set<string>} [catchBindingNames] - Lexically visible exact catch binding names.
|
|
45
|
+
* @property {Map<string, import("../semantic/types.js").CatchBinding>} [catchBindingNodes] - Exact catch binding nodes by visible name.
|
|
44
46
|
* @property {Map<string, import("../semantic/types.js").ClassDeclaration>} classes - Reference classes by identity.
|
|
45
47
|
* @property {import("../semantic/types.js").ClassDeclaration} [currentClass] - Enclosing reference class.
|
|
46
48
|
* @property {Map<string, import("../semantic/types.js").ErrorDeclaration>} errorNames - Errors by source name.
|
|
@@ -970,7 +972,7 @@ function convertStatement(statement, filename, source, context) {
|
|
|
970
972
|
* @param {string} filename - Source filename.
|
|
971
973
|
* @param {string} source - Complete source.
|
|
972
974
|
* @param {JavaConversionContext} context - Typed lexical context.
|
|
973
|
-
* @returns {import("../semantic/types.js").RaiseStatement} Semantic raise.
|
|
975
|
+
* @returns {import("../semantic/types.js").RaiseStatement | import("../semantic/types.js").RethrowStatement} Semantic raise or rethrow.
|
|
974
976
|
*/
|
|
975
977
|
function convertJavaRaise(statement, filename, source, context) {
|
|
976
978
|
const location = nodeLocation(statement, filename, source)
|
|
@@ -980,6 +982,23 @@ function convertJavaRaise(statement, filename, source, context) {
|
|
|
980
982
|
const arguments_ = argumentsNode ? structuralChildren(argumentsNode) : []
|
|
981
983
|
const declaration = typeNode ? context.errorNames.get(nodeText(typeNode, source)) : undefined
|
|
982
984
|
|
|
985
|
+
if (!construction && arguments_.length == 0) {
|
|
986
|
+
const throwNode = requiredChild(statement, "throw", filename, source)
|
|
987
|
+
const rethrown = structuralChildren(statement).find((candidate) => candidate.name == "Identifier")
|
|
988
|
+
const binding = rethrown ? context.bindings.get(nodeText(rethrown, source)) : undefined
|
|
989
|
+
|
|
990
|
+
if (rethrown && binding?.kind == "ErrorType" && context.catchBindingNames?.has(nodeText(rethrown, source))) {
|
|
991
|
+
const bindingNode = context.catchBindingNodes?.get(nodeText(rethrown, source))
|
|
992
|
+
|
|
993
|
+
if (!bindingNode) return unsupportedSyntax("java", "throw of a catch binding without an exact handler binding", location)
|
|
994
|
+
return withParserRanges({
|
|
995
|
+
binding: nodeText(rethrown, source),
|
|
996
|
+
bindingNode,
|
|
997
|
+
kind: /** @type {const} */ ("RethrowStatement"),
|
|
998
|
+
location
|
|
999
|
+
}, {keyword: nodeLocation(throwNode, filename, source)})
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
983
1002
|
if (!construction || !typeNode || !argumentsNode || !declaration || arguments_.length != 1 || construction.getChild("ClassBody")) {
|
|
984
1003
|
return unsupportedSyntax("java", "throw other than exact declared error construction", location)
|
|
985
1004
|
}
|
|
@@ -1009,46 +1028,76 @@ function convertJavaTry(statement, filename, source, context) {
|
|
|
1009
1028
|
const location = nodeLocation(statement, filename, source)
|
|
1010
1029
|
const body = statement.getChild("Block")
|
|
1011
1030
|
const catches = statement.getChildren("CatchClause")
|
|
1031
|
+
const finalizer = statement.getChild("FinallyClause")
|
|
1032
|
+
|
|
1033
|
+
if (!body || (catches.length == 0 && !finalizer) || statement.getChild("ResourceSpecification")) {
|
|
1034
|
+
return unsupportedSyntax("java", "try without catches or finally and without resources", location)
|
|
1035
|
+
}
|
|
1036
|
+
/** @type {import("../semantic/types.js").CatchHandler[]} */
|
|
1037
|
+
const handlers = []
|
|
1038
|
+
const seenHandlerNames = new Set(context.catchBindingNames ?? [])
|
|
1039
|
+
|
|
1040
|
+
for (const handler of catches) {
|
|
1041
|
+
const parameter = handler.getChild("CatchFormalParameter")
|
|
1042
|
+
const caught = parameter?.getChild("CatchType")
|
|
1043
|
+
const typeNodes = caught ? structuralChildren(caught) : []
|
|
1044
|
+
const definition = parameter?.getChild("Definition")
|
|
1045
|
+
const catchBody = handler.getChild("Block")
|
|
1046
|
+
const typeNode = typeNodes[0]
|
|
1047
|
+
const declaration = typeNode ? context.errorNames.get(nodeText(typeNode, source)) : undefined
|
|
1048
|
+
|
|
1049
|
+
if (!parameter || !caught || typeNodes.length != 1 || typeNode?.name != "TypeName" || !definition || !catchBody || !declaration ||
|
|
1050
|
+
parameter.getChild("Modifiers")) {
|
|
1051
|
+
return unsupportedSyntax("java", "broad, multiple, or malformed catch", nodeLocation(handler, filename, source))
|
|
1052
|
+
}
|
|
1053
|
+
const name = nodeText(definition, source)
|
|
1012
1054
|
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
const declaration = typeNode ? context.errorNames.get(nodeText(typeNode, source)) : undefined
|
|
1024
|
-
|
|
1025
|
-
if (!parameter || !caught || typeNodes.length != 1 || typeNode?.name != "TypeName" || !definition || !catchBody || !declaration ||
|
|
1026
|
-
parameter.getChild("Modifiers")) {
|
|
1027
|
-
return unsupportedSyntax("java", "broad, multiple, or malformed catch", nodeLocation(handler, filename, source))
|
|
1028
|
-
}
|
|
1029
|
-
const caughtLocation = nodeLocation(typeNode, filename, source)
|
|
1030
|
-
const catchType = withParserRanges({declarationId: /** @type {string} */ (declaration.id), kind: /** @type {const} */ ("ErrorType")}, {
|
|
1031
|
-
type: caughtLocation
|
|
1032
|
-
})
|
|
1033
|
-
const bodyContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
1034
|
-
const catchContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
1035
|
-
const name = nodeText(definition, source)
|
|
1055
|
+
if (seenHandlerNames.has(name)) {
|
|
1056
|
+
return unsupportedSyntax("java", `catch binding '${name}' shadows an outer handler binding`, nodeLocation(definition, filename, source))
|
|
1057
|
+
}
|
|
1058
|
+
seenHandlerNames.add(name)
|
|
1059
|
+
const caughtLocation = nodeLocation(typeNode, filename, source)
|
|
1060
|
+
const catchType = withParserRanges({declarationId: /** @type {string} */ (declaration.id), kind: /** @type {const} */ ("ErrorType")}, {
|
|
1061
|
+
type: caughtLocation
|
|
1062
|
+
})
|
|
1063
|
+
const catchContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames),
|
|
1064
|
+
catchBindingNames: new Set(seenHandlerNames), catchBindingNodes: new Map(context.catchBindingNodes)}
|
|
1036
1065
|
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
body: convertBlock(body, filename, source, bodyContext),
|
|
1040
|
-
catchBinding: withParserRanges({
|
|
1066
|
+
catchContext.bindings.set(name, catchType)
|
|
1067
|
+
const catchBindingNode = withParserRanges({
|
|
1041
1068
|
kind: /** @type {const} */ ("CatchBinding"),
|
|
1042
1069
|
location: nodeLocation(definition, filename, source),
|
|
1043
1070
|
mutable: /** @type {const} */ (false),
|
|
1044
1071
|
name,
|
|
1045
1072
|
type: catchType
|
|
1046
|
-
}, {name: nodeLocation(definition, filename, source)})
|
|
1047
|
-
|
|
1048
|
-
|
|
1073
|
+
}, {name: nodeLocation(definition, filename, source)})
|
|
1074
|
+
|
|
1075
|
+
catchContext.catchBindingNodes.set(name, catchBindingNode)
|
|
1076
|
+
handlers.push(withParserRanges({
|
|
1077
|
+
catchBinding: catchBindingNode,
|
|
1078
|
+
catchBody: convertBlock(catchBody, filename, source, catchContext),
|
|
1079
|
+
catchType,
|
|
1080
|
+
kind: /** @type {const} */ ("CatchHandler"),
|
|
1081
|
+
location: nodeLocation(handler, filename, source)
|
|
1082
|
+
}, {catch: nodeLocation(handler, filename, source)}))
|
|
1083
|
+
}
|
|
1084
|
+
const bodyContext = {...context, bindings: new Map(context.bindings), erasedBindingNames: new Set(context.erasedBindingNames)}
|
|
1085
|
+
const semantic = /** @type {import("../semantic/types.js").TryStatement} */ (withParserRanges({
|
|
1086
|
+
body: convertBlock(body, filename, source, bodyContext),
|
|
1087
|
+
handlers,
|
|
1049
1088
|
kind: /** @type {const} */ ("TryStatement"),
|
|
1050
1089
|
location
|
|
1051
|
-
}, {
|
|
1090
|
+
}, {try: nodeLocation(requiredChild(statement, "try", filename, source), filename, source)}))
|
|
1091
|
+
|
|
1092
|
+
if (finalizer) {
|
|
1093
|
+
const finallyBlock = finalizer.getChild("Block")
|
|
1094
|
+
|
|
1095
|
+
if (!finallyBlock) return unsupportedSyntax("java", "malformed finally block", nodeLocation(finalizer, filename, source))
|
|
1096
|
+
semantic.finallyBody = convertBlock(finallyBlock, filename, source, bodyContext)
|
|
1097
|
+
setParserRanges(semantic, {finally: nodeLocation(finalizer, filename, source),
|
|
1098
|
+
try: nodeLocation(requiredChild(statement, "try", filename, source), filename, source)})
|
|
1099
|
+
}
|
|
1100
|
+
return semantic
|
|
1052
1101
|
}
|
|
1053
1102
|
|
|
1054
1103
|
/**
|