Support returns in nested finallies
This commit is contained in:
+130
-40
@@ -16,6 +16,7 @@
|
||||
|
||||
package org.jetbrains.jet.codegen.inline;
|
||||
|
||||
import com.google.common.base.Objects;
|
||||
import com.google.common.collect.LinkedListMultimap;
|
||||
import com.google.common.collect.ListMultimap;
|
||||
import com.google.common.collect.Lists;
|
||||
@@ -28,7 +29,6 @@ import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.jetbrains.org.objectweb.asm.tree.*;
|
||||
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*;
|
||||
@@ -112,8 +112,8 @@ public class InternalFinallyBlockInliner {
|
||||
// Each group that corresponds to try/*catches*/finally contains tryCatch block with default handler.
|
||||
// For each such group we should insert corresponding finally before non-local return.
|
||||
// So we split all try blocks on current instructions to groups and process them independently
|
||||
List<TryBlockCluster> clusters = InlinePackage.doClustering(coveringTryCatchBlocks);
|
||||
ListIterator<TryBlockCluster> tryCatchBlockIterator = clusters.listIterator(clusters.size());
|
||||
List<TryBlockCluster<TryCatchBlockNodeInfo>> clusters = InlinePackage.doClustering(coveringTryCatchBlocks);
|
||||
ListIterator<TryBlockCluster<TryCatchBlockNodeInfo>> tryCatchBlockIterator = clusters.listIterator(clusters.size());
|
||||
//Reverse visiting cause innermost tryCatchBlocks in the end
|
||||
while (tryCatchBlockIterator.hasPrevious()) {
|
||||
TryBlockCluster originalFinallyCluster = tryCatchBlockIterator.previous();
|
||||
@@ -135,6 +135,7 @@ public class InternalFinallyBlockInliner {
|
||||
MethodNode finallyBlockCopy = createEmptyMethodNode();
|
||||
Label newFinallyStart = new Label();
|
||||
Label newFinallyEnd = new Label();
|
||||
Label insertedBlockEnd = new Label();
|
||||
|
||||
if (nonLocalReturnType != Type.VOID_TYPE) {
|
||||
finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ISTORE), nextTempNonLocalVarIndex);
|
||||
@@ -178,11 +179,13 @@ public class InternalFinallyBlockInliner {
|
||||
nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing
|
||||
}
|
||||
|
||||
finallyBlockCopy.visitLabel(insertedBlockEnd);
|
||||
|
||||
//Copying finally body before non-local return instruction
|
||||
InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore);
|
||||
|
||||
nextPrev = updateExceptionTable(coveringTryCatchBlocks, nextPrev, clusterBlocks, newFinallyStart, newFinallyEnd,
|
||||
tryCatchBlockInlinedInFinally);
|
||||
tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info);
|
||||
}
|
||||
curIns = nextPrev;
|
||||
}
|
||||
@@ -208,35 +211,95 @@ public class InternalFinallyBlockInliner {
|
||||
private AbstractInsnNode updateExceptionTable(
|
||||
@NotNull Stack<TryCatchBlockNodeInfo> coveringTryBlocks,
|
||||
@Nullable AbstractInsnNode nextPrev,
|
||||
@NotNull List<TryCatchBlockNodeInfo> clusterBlocks,
|
||||
@NotNull List<TryCatchBlockNodeInfo> updatingClusterBlocks,
|
||||
@NotNull Label newFinallyStart,
|
||||
@NotNull Label newFinallyEnd,
|
||||
@NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally
|
||||
@NotNull List<TryCatchBlockNodePosition> tryCatchBlockPresentInFinally,
|
||||
@NotNull Set<LabelNode> labelsInsideFinally,
|
||||
@NotNull LabelNode insertedBlockEnd
|
||||
|
||||
) {
|
||||
|
||||
//copy tryCatchFinallies that totally in finally block
|
||||
for (TryCatchBlockNodePosition position : tryCatchBlockPresentInFinally) {
|
||||
//TODO assert INNER
|
||||
TryCatchBlockNode tryCatchBlockNode = position.getNodeInfo().getNode();
|
||||
TryCatchBlockNode additionalTryCatchBlock =
|
||||
new TryCatchBlockNode((LabelNode) tryCatchBlockNode.start.getLabel().info,
|
||||
(LabelNode) tryCatchBlockNode.end.getLabel().info,
|
||||
(LabelNode) tryCatchBlockNode.handler.getLabel().info,
|
||||
tryCatchBlockNode.type);
|
||||
List<TryBlockCluster<TryCatchBlockNodePosition>> clusters = InlinePackage.doClustering(tryCatchBlockPresentInFinally);
|
||||
Map<LabelNode, TryBlockCluster<TryCatchBlockNodePosition>> handler2Cluster = new HashMap<LabelNode, TryBlockCluster<TryCatchBlockNodePosition>>();
|
||||
|
||||
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, true);
|
||||
tryBlockStarts.put(additionalTryCatchBlock.start, newInfo);
|
||||
tryBlockEnds.put(additionalTryCatchBlock.end, newInfo);
|
||||
inlineFunTryBlockInfo.add(newInfo);
|
||||
for (TryBlockCluster<TryCatchBlockNodePosition> cluster : clusters) {
|
||||
List<TryCatchBlockNodePosition> clusterBlocks = cluster.getBlocks();
|
||||
TryCatchBlockNodePosition block0 = clusterBlocks.get(0);
|
||||
TryCatchPosition clusterPosition = block0.getPosition();
|
||||
if (clusterPosition == TryCatchPosition.INNER) {
|
||||
for (TryCatchBlockNodePosition position : clusterBlocks) {
|
||||
assert clusterPosition == position.getPosition() : "Wrong inner tryCatchBlock structure";
|
||||
TryCatchBlockNode tryCatchBlockNode = position.getNodeInfo().getNode();
|
||||
|
||||
assert inlineFun.instructions.indexOf(tryCatchBlockNode.start) <= inlineFun.instructions.indexOf(tryCatchBlockNode.end);
|
||||
|
||||
TryCatchBlockNode additionalTryCatchBlock =
|
||||
new TryCatchBlockNode((LabelNode) tryCatchBlockNode.start.getLabel().info,
|
||||
(LabelNode) tryCatchBlockNode.end.getLabel().info,
|
||||
getNewOrOldLabel(tryCatchBlockNode.handler, labelsInsideFinally),
|
||||
tryCatchBlockNode.type);
|
||||
|
||||
|
||||
assert inlineFun.instructions.indexOf(additionalTryCatchBlock.start) <= inlineFun.instructions.indexOf(additionalTryCatchBlock.end);
|
||||
|
||||
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, true);
|
||||
tryBlockStarts.put(newInfo.getStartLabel(), newInfo);
|
||||
tryBlockEnds.put(newInfo.getEndLabel(), newInfo);
|
||||
inlineFunTryBlockInfo.add(newInfo);
|
||||
}
|
||||
}
|
||||
else if (clusterPosition == TryCatchPosition.END) {
|
||||
TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler();
|
||||
assert defaultHandler != null : "Default handler should be present";
|
||||
handler2Cluster.put(defaultHandler.getHandler(), cluster);
|
||||
}
|
||||
else {
|
||||
assert clusterPosition == TryCatchPosition.START;
|
||||
TryCatchBlockNodePosition defaultHandler = cluster.getDefaultHandler();
|
||||
assert defaultHandler != null : "Default handler should be present";
|
||||
TryBlockCluster<TryCatchBlockNodePosition> endCluster = handler2Cluster.remove(defaultHandler.getHandler());
|
||||
assert endCluster != null : "Could find start cluster for " + clusterPosition;
|
||||
|
||||
//at this point only external finallies could occurs
|
||||
//they don't collision with updatingClusterBlocks, but may with external ones on next updateExceptionTable invocation
|
||||
Iterator<TryCatchBlockNodePosition> startBlockPositions = clusterBlocks.iterator();
|
||||
for (TryCatchBlockNodePosition endBlockPosition : endCluster.getBlocks()) {
|
||||
TryCatchBlockNodeInfo startNode = startBlockPositions.next().getNodeInfo();
|
||||
TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo();
|
||||
|
||||
assert Objects.equal(startNode.getType(), endNode.getType()) : "Different handler types : " + startNode.getType() + " " + endNode.getType();
|
||||
|
||||
patchTryBlocks((LabelNode) startNode.getStartLabel().getLabel().info, endNode, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (handler2Cluster.size() == 1) {
|
||||
TryBlockCluster<TryCatchBlockNodePosition> singleCluster = handler2Cluster.values().iterator().next();
|
||||
if (singleCluster.getBlocks().get(0).getPosition() == TryCatchPosition.END) {
|
||||
//Pair that starts on default handler don't added to tryCatchBlockPresentInFinally cause it's out of finally block
|
||||
//TODO rewrite to clusters
|
||||
for (TryCatchBlockNodePosition endBlockPosition : singleCluster.getBlocks()) {
|
||||
TryCatchBlockNodeInfo endNode = endBlockPosition.getNodeInfo();
|
||||
patchTryBlocks((LabelNode) insertedBlockEnd.getLabel().info, endNode, true);
|
||||
//nextPrev = (AbstractInsnNode) insertedBlockEnd.getLabel().info;
|
||||
}
|
||||
|
||||
handler2Cluster.clear();
|
||||
}
|
||||
}
|
||||
assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size();
|
||||
|
||||
// Inserted finally shouldn't be handled by corresponding catches,
|
||||
// so we should split original interval by inserted finally one
|
||||
for (TryCatchBlockNodeInfo block : clusterBlocks) {
|
||||
for (TryCatchBlockNodeInfo block : updatingClusterBlocks) {
|
||||
//update exception mapping
|
||||
LabelNode oldStartNode = block.getNode().start;
|
||||
tryBlockStarts.remove(oldStartNode, block);
|
||||
block.getNode().start = (LabelNode) newFinallyEnd.info;
|
||||
//tryBlockStarts.put(block.getStartLabel(), block);
|
||||
|
||||
TryCatchBlockNode additionalTryCatchBlock =
|
||||
new TryCatchBlockNode(oldStartNode, (LabelNode) newFinallyStart.info, block.getNode().handler, block.getNode().type);
|
||||
@@ -251,19 +314,50 @@ public class InternalFinallyBlockInliner {
|
||||
nextPrev = additionalTryCatchBlock.end;
|
||||
coveringTryBlocks.pop();
|
||||
}
|
||||
sortTryCatchBlocks();
|
||||
sortTryCatchBlocks(inlineFunTryBlockInfo);
|
||||
return nextPrev;
|
||||
}
|
||||
|
||||
private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode, boolean sort) {
|
||||
LabelNode oldStart = endNode.getStartLabel();
|
||||
endNode.getNode().start = newStartLabelNode;
|
||||
tryBlockStarts.remove(oldStart, endNode);
|
||||
tryBlockStarts.put(endNode.getNode().start, endNode);
|
||||
|
||||
|
||||
TryCatchBlockNode endTryBlock = endNode.getNode();
|
||||
TryCatchBlockNode additionalTryCatchBlock =
|
||||
new TryCatchBlockNode(oldStart,
|
||||
(LabelNode) endTryBlock.end.getLabel().info,
|
||||
endTryBlock.handler,
|
||||
endTryBlock.type);
|
||||
|
||||
TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, endNode.getOnlyCopyNotProcess());
|
||||
tryBlockStarts.put(newInfo.getStartLabel(), newInfo);
|
||||
tryBlockEnds.put(newInfo.getEndLabel(), newInfo);
|
||||
|
||||
inlineFunTryBlockInfo.add(newInfo);
|
||||
}
|
||||
|
||||
private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set<LabelNode> labelsInsideFinally) {
|
||||
if (labelsInsideFinally.contains(oldHandler)) {
|
||||
return (LabelNode) oldHandler.getLabel().info;
|
||||
}
|
||||
|
||||
return oldHandler;
|
||||
}
|
||||
|
||||
//Keep information about try blocks that cover current instruction -
|
||||
// pushing and popping it to stack entering and exiting tryCatchBlock start and end labels
|
||||
private void updateCoveringTryBlocks(Stack<TryCatchBlockNodeInfo> coveringTryBlocks, AbstractInsnNode curIns) {
|
||||
if (!(curIns instanceof LabelNode)) return;
|
||||
|
||||
for (TryCatchBlockNodeInfo startNode : tryBlockStarts.get((LabelNode) curIns)) {
|
||||
List<TryCatchBlockNodeInfo> infos = tryBlockStarts.get((LabelNode) curIns);
|
||||
for (TryCatchBlockNodeInfo startNode : infos) {
|
||||
if (!startNode.getOnlyCopyNotProcess()) {
|
||||
TryCatchBlockNodeInfo pop = coveringTryBlocks.pop();
|
||||
assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop;
|
||||
//Temporary disabled cause during patched structure of exceptions changed
|
||||
//assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop + " " + infos.size();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -382,12 +476,10 @@ public class InternalFinallyBlockInliner {
|
||||
List<TryCatchBlockNodeInfo> startedTryBlocks = tryBlockStarts.get(curLabel);
|
||||
if (startedTryBlocks != null) {
|
||||
for (TryCatchBlockNodeInfo block : startedTryBlocks) {
|
||||
if (block.getOnlyCopyNotProcess()) {
|
||||
assert !processedBlocks.containsKey(block) : "Try catch block already processed before start label!!! " + block;
|
||||
TryCatchBlockNodePosition info = new TryCatchBlockNodePosition(block, TryCatchPosition.START);
|
||||
processedBlocks.put(block, info);
|
||||
result.add(info);
|
||||
}
|
||||
assert !processedBlocks.containsKey(block) : "Try catch block already processed before start label!!! " + block;
|
||||
TryCatchBlockNodePosition info = new TryCatchBlockNodePosition(block, TryCatchPosition.START);
|
||||
processedBlocks.put(block, info);
|
||||
result.add(info);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -395,17 +487,15 @@ public class InternalFinallyBlockInliner {
|
||||
if (endedTryBlocks == null) continue;
|
||||
|
||||
for (TryCatchBlockNodeInfo block : endedTryBlocks) {
|
||||
if (block.getOnlyCopyNotProcess()) {
|
||||
TryCatchBlockNodePosition info = processedBlocks.get(block);
|
||||
if (info != null) {
|
||||
assert info.getPosition() == TryCatchPosition.START;
|
||||
info.setPosition(TryCatchPosition.INNER);
|
||||
}
|
||||
else {
|
||||
info = new TryCatchBlockNodePosition(block, TryCatchPosition.END);
|
||||
processedBlocks.put(block, info);
|
||||
result.add(info);
|
||||
}
|
||||
TryCatchBlockNodePosition info = processedBlocks.get(block);
|
||||
if (info != null) {
|
||||
assert info.getPosition() == TryCatchPosition.START;
|
||||
info.setPosition(TryCatchPosition.INNER);
|
||||
}
|
||||
else {
|
||||
info = new TryCatchBlockNodePosition(block, TryCatchPosition.END);
|
||||
processedBlocks.put(block, info);
|
||||
result.add(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -436,7 +526,7 @@ public class InternalFinallyBlockInliner {
|
||||
return result;
|
||||
}
|
||||
|
||||
public void sortTryCatchBlocks() {
|
||||
private void sortTryCatchBlocks(@NotNull List<TryCatchBlockNodeInfo> inlineFunTryBlockInfo) {
|
||||
Comparator<TryCatchBlockNodeInfo> comp = new Comparator<TryCatchBlockNodeInfo>() {
|
||||
@Override
|
||||
public int compare(@NotNull TryCatchBlockNodeInfo t1, @NotNull TryCatchBlockNodeInfo t2) {
|
||||
|
||||
@@ -21,6 +21,7 @@ import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.LabelNode
|
||||
import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode
|
||||
import org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
enum class TryCatchPosition {
|
||||
START
|
||||
@@ -28,19 +29,39 @@ enum class TryCatchPosition {
|
||||
INNER
|
||||
}
|
||||
|
||||
class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: Boolean)
|
||||
trait IntervalWithHandler {
|
||||
val startLabel: LabelNode
|
||||
val endLabel: LabelNode
|
||||
val handler: LabelNode
|
||||
val `type`: String?
|
||||
}
|
||||
|
||||
class TryCatchBlockNodePosition(val nodeInfo: TryCatchBlockNodeInfo, var position: TryCatchPosition)
|
||||
class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: Boolean) : IntervalWithHandler {
|
||||
|
||||
class TryBlockCluster(val blocks: MutableList<TryCatchBlockNodeInfo>)
|
||||
override val startLabel: LabelNode
|
||||
get() = node.start
|
||||
override val endLabel: LabelNode
|
||||
get() = node.end
|
||||
override val handler: LabelNode
|
||||
get() = node.handler!!
|
||||
override val `type`: String?
|
||||
get() = node.`type`
|
||||
}
|
||||
|
||||
class TryCatchBlockNodePosition(val nodeInfo: TryCatchBlockNodeInfo, var position: TryCatchPosition): IntervalWithHandler by nodeInfo
|
||||
|
||||
class TryBlockCluster<T : IntervalWithHandler>(val blocks: MutableList<T>) {
|
||||
val defaultHandler: T?
|
||||
get() = blocks.firstOrNull() { it.`type` == null }
|
||||
}
|
||||
|
||||
|
||||
fun doClustering(blocks: List<TryCatchBlockNodeInfo>) : List<TryBlockCluster> {
|
||||
fun <T: IntervalWithHandler> doClustering(blocks: List<T>) : List<TryBlockCluster<T>> {
|
||||
[data] class TryBlockInterval(val startLabel: LabelNode, val endLabel: LabelNode)
|
||||
|
||||
val clusters = linkedMapOf<TryBlockInterval, TryBlockCluster>()
|
||||
val clusters = linkedMapOf<TryBlockInterval, TryBlockCluster<T>>()
|
||||
blocks.forEach { block ->
|
||||
val interval = TryBlockInterval(firstLabelInChain(block.node.start), firstLabelInChain(block.node.end))
|
||||
val interval = TryBlockInterval(firstLabelInChain(block.startLabel), firstLabelInChain(block.endLabel))
|
||||
val cluster = clusters.getOrPut(interval, {TryBlockCluster(arrayListOf())})
|
||||
cluster.blocks.add(block)
|
||||
}
|
||||
|
||||
-55
@@ -43,60 +43,5 @@ fun box(): String {
|
||||
test0 = test0(h, throwExternalFinEx1 = true, res = "OK")
|
||||
if (test0 != "EXCEPTION_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_FINALLY1, OK_FINALLY2") return "test0_2: ${test0}, holder: ${h.value}"
|
||||
|
||||
// h = Holder()
|
||||
// test0 = test0(h, throwExternalFinEx2 = true, res = "OK")
|
||||
// if (test0 != "EXCEPTION222_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_FINALLY1, OK_FINALLY2") return "test0_4: ${test0}, holder: ${h.value}"
|
||||
|
||||
|
||||
|
||||
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwExternalFinEx1 = true, res = "OK")
|
||||
// if (test0 != "EXCEPTION_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_FINALLY2") return "test0_3: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwInternalEx2 = true, throwExternalFinEx1 = true, res = "OK")
|
||||
// if (test0 != "EXCEPTION_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_FINALLY2") return "test0_5: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwInternalEx2 = true, throwExternalFinEx2 = true, res = "OK")
|
||||
// if (test0 != "EXCEPTION222_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_FINALLY2") return "test0_6: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
//
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, false, throwInternalFinEx1 = true, res = "FAIL")
|
||||
// if (test0 != "EXCEPTION_IN_INTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_FINALLY1, OK_FINALLY2") return "test0_7: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, false, throwInternalFinEx1 = true, throwExternalFinEx2 = true, res = "FAIL")
|
||||
// if (test0 != "EXCEPTION222_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_FINALLY1, OK_FINALLY2") return "test0_71: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, false, throwInternalFinEx2 = true, res = "FAIL")
|
||||
// if (test0 != "OK_EXTERNAL_EXCEPTION2" || h.value != "OK_NON_LOCAL, OK_FINALLY1, OK_EXTERNAL_EXCEPTION2, OK_FINALLY2") return "test0_8: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, false, throwInternalFinEx2 = true, throwExternalFinEx2 = true, res = "FAIL")
|
||||
// if (test0 != "EXCEPTION222_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_FINALLY1, OK_EXTERNAL_EXCEPTION2, OK_FINALLY2") return "test0_81: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
//
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwInternalFinEx1 = true, res = "FAIL")
|
||||
// if (test0 != "EXCEPTION_IN_INTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_FINALLY2") return "test0_9: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwInternalFinEx1 = true, throwExternalFinEx2 = true, res = "FAIL")
|
||||
// if (test0 != "EXCEPTION222_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_FINALLY2") return "test0_10: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwInternalFinEx2 = true, res = "FAIL")
|
||||
// if (test0 != "OK_EXTERNAL_EXCEPTION2" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_EXTERNAL_EXCEPTION2, OK_FINALLY2") return "test0_11: ${test0}, holder: ${h.value}"
|
||||
//
|
||||
// h = Holder()
|
||||
// test0 = test0(h, true, throwInternalFinEx2 = true, throwExternalFinEx2 = true, res = "FAIL")
|
||||
// if (test0 != "EXCEPTION222_IN_EXTERNAL_FINALLY" || h.value != "OK_NON_LOCAL, OK_INTERNAL_EXCEPTION1, OK_FINALLY1, OK_EXTERNAL_EXCEPTION2, OK_FINALLY2") return "test0_12: ${test0}, holder: ${h.value}"
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user