From d0f6f03380e915578c0094a66bf246215cb1c5c9 Mon Sep 17 00:00:00 2001 From: Michael Bogdanov Date: Tue, 28 Oct 2014 14:16:47 +0300 Subject: [PATCH] Fixes for call site finally generation before non-local returns --- .../inline/CoveringTryCatchNodeProcessor.kt | 143 ++++++++ .../jet/codegen/inline/InlineCodegen.java | 73 ++++- .../inline/InternalFinallyBlockInliner.java | 309 +++++++----------- .../jet/codegen/inline/MethodInliner.java | 11 +- .../jet/codegen/inline/TryBlockClustering.kt | 2 +- .../callSite/exceptionTableSplit.1.kt | 101 ++++++ .../callSite/exceptionTableSplit.2.kt | 33 ++ .../callSite/exceptionTableSplitNoReturn.1.kt | 101 ++++++ .../callSite/exceptionTableSplitNoReturn.2.kt | 33 ++ .../inline/splitedExceptionTable.kt | 28 +- .../BlackBoxInlineCodegenTestGenerated.java | 12 + ...otlinAgainstInlineKotlinTestGenerated.java | 12 + 12 files changed, 625 insertions(+), 233 deletions(-) create mode 100644 compiler/backend/src/org/jetbrains/jet/codegen/inline/CoveringTryCatchNodeProcessor.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.2.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt create mode 100644 compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.2.kt diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/CoveringTryCatchNodeProcessor.kt b/compiler/backend/src/org/jetbrains/jet/codegen/inline/CoveringTryCatchNodeProcessor.kt new file mode 100644 index 00000000000..dccae460bea --- /dev/null +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/CoveringTryCatchNodeProcessor.kt @@ -0,0 +1,143 @@ +/* + * Copyright 2010-2014 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.jet.codegen.inline + +import org.jetbrains.org.objectweb.asm.tree.MethodNode +import org.jetbrains.org.objectweb.asm.tree.LabelNode +import com.google.common.collect.LinkedListMultimap +import org.jetbrains.org.objectweb.asm.Label +import java.util.ArrayList +import com.intellij.util.containers.Stack +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode +import com.google.common.collect.Lists +import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode +import java.util.Comparator +import java.util.Collections + +public abstract class CoveringTryCatchNodeProcessor() { + + private val tryBlockStarts = LinkedListMultimap.create() + + private val tryBlockEnds = LinkedListMultimap.create() + + public val allTryCatchNodes: ArrayList = arrayListOf() + + private val currentCoveringBlocks: Stack = Stack() + + public val coveringFromInnermost: List + get() = currentCoveringBlocks.reverse() + + fun addNewTryCatchNode(newInfo: T) { + tryBlockStarts.put(newInfo.startLabel, newInfo) + tryBlockEnds.put(newInfo.endLabel, newInfo) + allTryCatchNodes.add(newInfo) + } + + fun remapStartLabel(oldStart: LabelNode, remapped: T) { + tryBlockStarts.remove(oldStart, remapped) + tryBlockStarts.put(remapped.startLabel, remapped) + } + + public fun getStartNodes(label: LabelNode): List { + return tryBlockStarts.get(label) + } + + public fun getEndNodes(label: LabelNode): List { + return tryBlockEnds.get(label) + } + + //Keep information about try blocks that cover current instruction - + // pushing and popping it to stack entering and exiting tryCatchBlock start and end labels + public open fun updateCoveringTryBlocks(curIns: AbstractInsnNode, directOrder: Boolean) { + if (curIns !is LabelNode) return + + val infosToClose = if (!directOrder) getStartNodes(curIns) else getEndNodes(curIns) + for (startNode in infosToClose) { + val pop = currentCoveringBlocks.pop() + //Temporary disabled cause during patched structure of exceptions changed +// if (startNode != pop) { +// throw RuntimeException("Wrong try-catch structure " + startNode + " " + pop + " " + infosToClose.size()) +// }; + } + + //Reversing list order cause we should pop external block before internal one + // (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method) + val infoToOpen = if (!directOrder) getEndNodes(curIns) else getStartNodes(curIns) + for (info in infoToOpen.reverse()) { + currentCoveringBlocks.add(info) + } + } + + public abstract fun instructionIndex(inst: AbstractInsnNode): Int + + private fun isEmptyInterval(node: T): Boolean { + val start = node.startLabel + var end: AbstractInsnNode = node.endLabel + while (end != start && end is LabelNode) { + end = end.getPrevious() + } + return start == end; + } + + public fun getNonEmptyNodes(): List { + return allTryCatchNodes.filterNot { isEmptyInterval(it) } + } + + public fun sortTryCatchBlocks() { + val comp = Comparator {(t1: T, t2: T): Int -> + var result = instructionIndex(t1.handler) - instructionIndex(t2.handler) + if (result == 0) { + result = instructionIndex(t1.startLabel) - instructionIndex(t2.startLabel) + if (result == 0) { + assert(false, "Error: support multicatch finallies!") + result = instructionIndex(t1.endLabel) - instructionIndex(t2.endLabel) + } + } + result + } + + Collections.sort(allTryCatchNodes, comp) + } + +} + +public class DefaultProcessor(val node: MethodNode) : CoveringTryCatchNodeProcessor() { + + { + node.tryCatchBlocks.forEach { addNode(it) } + } + + fun addNode(node: TryCatchBlockNode) { + addNewTryCatchNode(TryCatchBlockNodeWrapper(node)) + } + + override fun instructionIndex(inst: AbstractInsnNode): Int { + return node.instructions.indexOf(inst) + } + +} + +public class TryCatchBlockNodeWrapper(val node: TryCatchBlockNode) : IntervalWithHandler { + 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 +} \ No newline at end of file diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java index 1cae22e4ce4..b96fa0b210e 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InlineCodegen.java @@ -18,6 +18,7 @@ package org.jetbrains.jet.codegen.inline; import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.PsiElement; +import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.codegen.*; @@ -41,17 +42,18 @@ import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature; import org.jetbrains.jet.lang.types.lang.InlineStrategy; import org.jetbrains.jet.lang.types.lang.InlineUtil; import org.jetbrains.jet.renderer.DescriptorRenderer; +import org.jetbrains.org.objectweb.asm.Label; import org.jetbrains.org.objectweb.asm.MethodVisitor; import org.jetbrains.org.objectweb.asm.Opcodes; import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.commons.Method; +import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode; +import org.jetbrains.org.objectweb.asm.tree.LabelNode; import org.jetbrains.org.objectweb.asm.tree.MethodNode; +import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode; import java.io.IOException; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; +import java.util.*; import static org.jetbrains.jet.codegen.AsmUtil.getMethodAsmFlags; import static org.jetbrains.jet.codegen.AsmUtil.isPrimitive; @@ -239,7 +241,7 @@ public class InlineCodegen implements CallGenerator { } } }; - List infos = MethodInliner.processReturns(adapter, labelOwner, true, null); + List infos = MethodInliner.processReturns(adapter, labelOwner, true, null); generateAndInsertFinallyBlocks(adapter, infos); adapter.accept(new InliningInstructionAdapter(codegen.v)); @@ -481,19 +483,62 @@ public class InlineCodegen implements CallGenerator { } - public void generateAndInsertFinallyBlocks(MethodNode intoNode, List insertPoints) { + public void generateAndInsertFinallyBlocks(MethodNode intoNode, List insertPoints) { if (!codegen.hasFinallyBlocks()) return; - for (MethodInliner.ExternalFinallyBlockInfo insertPoint : insertPoints) { - MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode(); - ExpressionCodegen finallyCodegen = - new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(), - codegen.getContext(), codegen.getState(), codegen.getParentCodegen()); - finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements()); + Map extensionPoints = + new HashMap(); + for (MethodInliner.PointForExternalFinallyBlocks insertPoint : insertPoints) { + extensionPoints.put(insertPoint.beforeIns, insertPoint); + } - finallyCodegen.generateFinallyBlocksIfNeeded(insertPoint.returnType); + DefaultProcessor processor = new DefaultProcessor(intoNode); - InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, insertPoint.beforeIns); + AbstractInsnNode curInstr = intoNode.instructions.getFirst(); + while (curInstr != null) { + processor.updateCoveringTryBlocks(curInstr, true); + + MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr); + if (extension != null) { + Label start = new Label(); + Label end = new Label(); + + MethodNode finallyNode = InlineCodegenUtil.createEmptyMethodNode(); + finallyNode.visitLabel(start); + + ExpressionCodegen finallyCodegen = + new ExpressionCodegen(finallyNode, codegen.getFrameMap(), codegen.getReturnType(), + codegen.getContext(), codegen.getState(), codegen.getParentCodegen()); + finallyCodegen.addBlockStackElementsForNonLocalReturns(codegen.getBlockStackElements()); + + finallyCodegen.generateFinallyBlocksIfNeeded(extension.returnType); + finallyNode.visitLabel(end); + + //Exception table for external try/catch/finally blocks will be generated in original codegen after exiting this method + InlineCodegenUtil.insertNodeBefore(finallyNode, intoNode, curInstr); + + List blocks = processor.getCoveringFromInnermost(); + ListIterator iterator = blocks.listIterator(blocks.size()); + while (iterator.hasPrevious()) { + TryCatchBlockNodeWrapper previous = iterator.previous(); + LabelNode oldStart = previous.getStartLabel(); + TryCatchBlockNode node = previous.getNode(); + node.start = (LabelNode) end.info; + processor.remapStartLabel(oldStart, previous); + + TryCatchBlockNode additionalNode = new TryCatchBlockNode(oldStart, (LabelNode) start.info, node.handler, node.type); + processor.addNode(additionalNode); + } + } + + curInstr = curInstr.getNext(); + } + + processor.sortTryCatchBlocks(); + Iterable nodes = processor.getNonEmptyNodes(); + intoNode.tryCatchBlocks.clear(); + for (TryCatchBlockNodeWrapper node : nodes) { + intoNode.tryCatchBlocks.add(node.getNode()); } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InternalFinallyBlockInliner.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InternalFinallyBlockInliner.java index 1f30b51c812..c686d8ca3a0 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/InternalFinallyBlockInliner.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/InternalFinallyBlockInliner.java @@ -17,14 +17,14 @@ 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; -import com.intellij.util.containers.Stack; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.TestOnly; import org.jetbrains.jet.codegen.AsmUtil; -import org.jetbrains.org.objectweb.asm.*; +import org.jetbrains.org.objectweb.asm.Label; +import org.jetbrains.org.objectweb.asm.Opcodes; +import org.jetbrains.org.objectweb.asm.Type; import org.jetbrains.org.objectweb.asm.tree.*; import org.jetbrains.org.objectweb.asm.util.Textifier; import org.jetbrains.org.objectweb.asm.util.TraceMethodVisitor; @@ -35,7 +35,7 @@ import java.util.*; import static org.jetbrains.jet.codegen.inline.InlineCodegenUtil.*; -public class InternalFinallyBlockInliner { +public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { private static class FinallyBlockInfo { @@ -76,22 +76,18 @@ public class InternalFinallyBlockInliner { @NotNull private final MethodNode inlineFun; - private final List inlineFunTryBlockInfo; - - private final ListMultimap tryBlockStarts = LinkedListMultimap.create(); - - private final ListMultimap tryBlockEnds = LinkedListMultimap.create(); //lambdaTryCatchBlockNodes is number of TryCatchBlockNodes that was inlined with lambdas into function //due to code generation specific they placed before function TryCatchBlockNodes private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun, List inlineFunTryBlockInfo) { this.inlineFun = inlineFun; - this.inlineFunTryBlockInfo = inlineFunTryBlockInfo; + for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { + addNewTryCatchNode(block); + } } private int initAndGetVarIndexForNonLocalReturnValue() { //sortTryCatchBlocks();/*TODO maybe remove*/ - mapLabelsToTryCatchBlocks(); MaxLocalsCalculator tempCalcNode = new MaxLocalsCalculator( InlineCodegenUtil.API, @@ -104,7 +100,6 @@ public class InternalFinallyBlockInliner { private void processInlineFunFinallyBlocks() { int nextTempNonLocalVarIndex = initAndGetVarIndexForNonLocalReturnValue(); - Stack coveringTryCatchBlocks = new Stack(); InsnList instructions = inlineFun.instructions; //As we do finally block code search after non-local return instruction @@ -112,13 +107,19 @@ public class InternalFinallyBlockInliner { // So we do instruction processing in reverse order! AbstractInsnNode curIns = instructions.getLast(); while (curIns != null) { - updateCoveringTryBlocks(coveringTryCatchBlocks, curIns); + updateCoveringTryBlocks(curIns); //At this point only global return is possible, local one already substituted with: goto endLabel if (!InlineCodegenUtil.isReturnOpcode(curIns.getOpcode()) || - !InlineCodegenUtil.isMarkedReturn(curIns) || - coveringTryCatchBlocks.isEmpty() || - coveringTryCatchBlocks.get(0).getOnlyCopyNotProcess()) { + !InlineCodegenUtil.isMarkedReturn(curIns)) { + curIns = curIns.getPrevious(); + continue; + } + + List currentCoveringNodes = getCoveringFromInnermost(); + checkCoveringBlocksInvariant(currentCoveringNodes); + if (currentCoveringNodes.isEmpty() || + currentCoveringNodes.get(currentCoveringNodes.size() - 1).getOnlyCopyNotProcess()) { curIns = curIns.getPrevious(); continue; } @@ -132,25 +133,22 @@ 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> clusters = InlinePackage.doClustering(coveringTryCatchBlocks); - ListIterator> tryCatchBlockIterator = clusters.listIterator(clusters.size()); + List> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodes); + Iterator> tryCatchBlockIterator = clustersFromInnermost.iterator(); - checkClusterInvariant(clusters); + checkClusterInvariant(clustersFromInnermost); - - //Reverse visiting cause innermost tryCatchBlocks in the end - List patched = new ArrayList(); - while (tryCatchBlockIterator.hasPrevious()) { - - TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.previous(); + List additionalNodesToSplit = new ArrayList(); + while (tryCatchBlockIterator.hasNext()) { + TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next(); List clusterBlocks = clusterToFindFinally.getBlocks(); - TryCatchBlockNodeInfo originalTryCatchBlock = clusterBlocks.get(0); + TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1); - FinallyBlockInfo finallyInfo = findFinallyBlockBody(originalTryCatchBlock, inlineFunTryBlockInfo); + FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getAllTryCatchNodes()); if (finallyInfo == null) continue; - if (originalTryCatchBlock.getOnlyCopyNotProcess()) { - patched.addAll(clusterBlocks); + if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) { + additionalNodesToSplit.addAll(clusterBlocks); continue; } @@ -158,54 +156,36 @@ public class InternalFinallyBlockInliner { List tryCatchBlockInlinedInFinally = findTryCatchBlocksInlinedInFinally(finallyInfo); - //Keep some information about label nodes, we need it to understand whether it's jump inside finally block or outside - // in first case we do call VISIT on instruction otherwise recreating jump instruction (see below) - Set labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo); - //Creating temp node for finally block copy with some additional instruction MethodNode finallyBlockCopy = createEmptyMethodNode(); Label newFinallyStart = new Label(); Label newFinallyEnd = new Label(); Label insertedBlockEnd = new Label(); - if (nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty()) { + boolean generateAloadAstore = nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty(); + if (generateAloadAstore) { finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ISTORE), nextTempNonLocalVarIndex); } finallyBlockCopy.visitLabel(newFinallyStart); + //Keep some information about label nodes, we need it to understand whether it's jump inside finally block or outside + // in first case we do call VISIT on instruction otherwise recreating jump instruction (see below) + Set labelsInsideFinally = rememberOriginalLabelNodes(finallyInfo); //Writing finally block body to temporary node AbstractInsnNode currentIns = finallyInfo.startIns; while (currentIns != finallyInfo.endInsExclusive) { - //This condition allows another model for non-local returns processing - if (false && InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns)) { - //substitute all local returns in finally finallyInfo with non-local one lambdaFinallyBlocks try finallyInfo - //TODO same for jumps - Type localReturnType = InlineCodegenUtil.getReturnType(currentIns.getOpcode()); - substituteReturnValueInFinally(nextTempNonLocalVarIndex, nonLocalReturnType, finallyBlockCopy, - localReturnType, true); + boolean isInsOrJumpInsideFinally = + !(currentIns instanceof JumpInsnNode) || + labelsInsideFinally.contains(((JumpInsnNode) currentIns).label); - instrInsertFinallyBefore.accept(finallyBlockCopy); - curIns.accept(finallyBlockCopy); - } - else { - boolean isInsOrJumpInsideFinally = - !(currentIns instanceof JumpInsnNode) || - labelsInsideFinally.contains(((JumpInsnNode) currentIns).label); - - if (isInsOrJumpInsideFinally) { - currentIns.accept(finallyBlockCopy); //VISIT - } - else { - //keep original jump: add currentIns clone - finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label)); - } - } + copyInstruction(nextTempNonLocalVarIndex, curIns, instrInsertFinallyBefore, nonLocalReturnType, finallyBlockCopy, + currentIns, isInsOrJumpInsideFinally); currentIns = currentIns.getNext(); } finallyBlockCopy.visitLabel(newFinallyEnd); - if (nonLocalReturnType != Type.VOID_TYPE && !finallyInfo.isEmpty()) { + if (generateAloadAstore) { finallyBlockCopy.visitVarInsn(nonLocalReturnType.getOpcode(Opcodes.ILOAD), nextTempNonLocalVarIndex); nextTempNonLocalVarIndex += nonLocalReturnType.getSize(); //TODO: do more wise indexing } @@ -216,27 +196,59 @@ public class InternalFinallyBlockInliner { InlineCodegenUtil.insertNodeBefore(finallyBlockCopy, inlineFun, instrInsertFinallyBefore); updateExceptionTable(clusterBlocks, newFinallyStart, newFinallyEnd, - tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, patched); - - - } - + tryCatchBlockInlinedInFinally, labelsInsideFinally, (LabelNode) insertedBlockEnd.info, additionalNodesToSplit); } + //skip just inserted finally curIns = curIns.getPrevious(); while (curIns != null && curIns != nextPrev) { - updateCoveringTryBlocks(coveringTryCatchBlocks, curIns); + updateCoveringTryBlocks(curIns); curIns = curIns.getPrevious(); } - //curIns = nextPrev; } - substitureTryBlockNodes(); + substituteTryBlockNodes(); } - private void checkCoveringBlocksInvariant(Stack coveringTryCatchBlocks) { + private static void copyInstruction( + int nextTempNonLocalVarIndex, + @NotNull AbstractInsnNode curIns, + @NotNull AbstractInsnNode instrInsertFinallyBefore, + @NotNull Type nonLocalReturnType, + @NotNull MethodNode finallyBlockCopy, + @NotNull AbstractInsnNode currentIns, + boolean isInsOrJumpInsideFinally + ) { + //This condition allows another model for non-local returns processing + if (false) { + boolean isReturnForSubstitution = + InlineCodegenUtil.isReturnOpcode(currentIns.getOpcode()) && !InlineCodegenUtil.isMarkedReturn(currentIns); + if (!isInsOrJumpInsideFinally || isReturnForSubstitution) { + //substitute all local returns and jumps outside finally with non-local return + Type localReturnType = InlineCodegenUtil.getReturnType(currentIns.getOpcode()); + substituteReturnValueInFinally(nextTempNonLocalVarIndex, nonLocalReturnType, finallyBlockCopy, + localReturnType, isReturnForSubstitution); + + instrInsertFinallyBefore.accept(finallyBlockCopy); + curIns.accept(finallyBlockCopy); + } else { + currentIns.accept(finallyBlockCopy); //VISIT + } + } + else { + if (isInsOrJumpInsideFinally) { + currentIns.accept(finallyBlockCopy); //VISIT + } + else { + //keep original jump: add currentIns clone + finallyBlockCopy.instructions.add(new JumpInsnNode(currentIns.getOpcode(), ((JumpInsnNode) currentIns).label)); + } + } + } + + private static void checkCoveringBlocksInvariant(List coveringTryCatchBlocks) { boolean isWasOnlyLocal = false; - for (TryCatchBlockNodeInfo info : coveringTryCatchBlocks) { + for (TryCatchBlockNodeInfo info : Lists.reverse(coveringTryCatchBlocks)) { assert !isWasOnlyLocal || info.getOnlyCopyNotProcess(); if (info.getOnlyCopyNotProcess()) { isWasOnlyLocal = true; @@ -244,10 +256,10 @@ public class InternalFinallyBlockInliner { } } - private void checkClusterInvariant(List> clusters) { + private static void checkClusterInvariant(List> clusters) { boolean isWasOnlyLocal; isWasOnlyLocal = false; - for (TryBlockCluster cluster : clusters) { + for (TryBlockCluster cluster : Lists.reverse(clusters)) { TryCatchBlockNodeInfo info = cluster.getBlocks().get(0); assert !isWasOnlyLocal || info.getOnlyCopyNotProcess(); if (info.getOnlyCopyNotProcess()) { @@ -267,7 +279,6 @@ public class InternalFinallyBlockInliner { return labelsInsideFinally; } - @Nullable private void updateExceptionTable( @NotNull List updatingClusterBlocks, @NotNull Label newFinallyStart, @@ -303,9 +314,7 @@ public class InternalFinallyBlockInliner { 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); + addNewTryCatchNode(newInfo); } } else if (clusterPosition == TryCatchPosition.END) { @@ -350,7 +359,7 @@ public class InternalFinallyBlockInliner { } assert handler2Cluster.isEmpty() : "Unmatched clusters " + handler2Cluster.size(); - List toProcess = new ArrayList(); + List toProcess = new ArrayList(); toProcess.addAll(patched); toProcess.addAll(updatingClusterBlocks); patched.clear(); @@ -360,33 +369,25 @@ public class InternalFinallyBlockInliner { //update exception mapping LabelNode oldStartNode = block.getNode().start; block.getNode().start = (LabelNode) newFinallyEnd.info; - tryBlockStarts.remove(oldStartNode, block); - tryBlockStarts.put(block.getStartLabel(), block); + remapStartLabel(oldStartNode, block); - //if (!block.getOnlyCopyNotProcess()) { - patched.add(block); - //} + patched.add(block); TryCatchBlockNode additionalTryCatchBlock = new TryCatchBlockNode(oldStartNode, (LabelNode) newFinallyStart.info, block.getNode().handler, block.getNode().type); TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, block.getOnlyCopyNotProcess()); - tryBlockStarts.put(additionalTryCatchBlock.start, newInfo); - tryBlockEnds.put(additionalTryCatchBlock.end, newInfo); - - inlineFunTryBlockInfo.add(newInfo); + addNewTryCatchNode(newInfo); //TODO add assert } - sortTryCatchBlocks(inlineFunTryBlockInfo); + sortTryCatchBlocks(); } private void patchTryBlocks(@NotNull LabelNode newStartLabelNode, @NotNull TryCatchBlockNodeInfo endNode) { LabelNode oldStart = endNode.getStartLabel(); endNode.getNode().start = newStartLabelNode; - tryBlockStarts.remove(oldStart, endNode); - tryBlockStarts.put(endNode.getNode().start, endNode); - + remapStartLabel(oldStart, endNode); TryCatchBlockNode endTryBlock = endNode.getNode(); TryCatchBlockNode additionalTryCatchBlock = @@ -396,10 +397,7 @@ public class InternalFinallyBlockInliner { endTryBlock.type); TryCatchBlockNodeInfo newInfo = new TryCatchBlockNodeInfo(additionalTryCatchBlock, endNode.getOnlyCopyNotProcess()); - tryBlockStarts.put(newInfo.getStartLabel(), newInfo); - tryBlockEnds.put(newInfo.getEndLabel(), newInfo); - - inlineFunTryBlockInfo.add(newInfo); + addNewTryCatchNode(newInfo); } private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set labelsInsideFinally) { @@ -412,23 +410,9 @@ public class InternalFinallyBlockInliner { //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 coveringTryBlocks, AbstractInsnNode curIns) { - if (!(curIns instanceof LabelNode)) return; - - List infos = tryBlockStarts.get((LabelNode) curIns); - for (TryCatchBlockNodeInfo startNode : infos) { - TryCatchBlockNodeInfo pop = coveringTryBlocks.pop(); - //Temporary disabled cause during patched structure of exceptions changed - //assert startNode == pop : "Wrong try-catch structure " + startNode + " " + pop + " " + infos.size(); - } - - //Reversing list order cause we should pop external block before internal one - // (originally internal blocks goes before external one, such invariant preserved via sortTryCatchBlocks method) - for (TryCatchBlockNodeInfo info : Lists.reverse(tryBlockEnds.get((LabelNode) curIns))) { - coveringTryBlocks.add(info); - } - - checkCoveringBlocksInvariant(coveringTryBlocks); + public void updateCoveringTryBlocks(@NotNull AbstractInsnNode curIns) { + super.updateCoveringTryBlocks(curIns, false); + checkCoveringBlocksInvariant(getCoveringFromInnermost()); } private static boolean hasFinallyBlocks(List inlineFunTryBlockInfo) { @@ -440,13 +424,6 @@ public class InternalFinallyBlockInliner { return false; } - private void mapLabelsToTryCatchBlocks() { - for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { - tryBlockStarts.put(block.getNode().start, block); - tryBlockEnds.put(block.getNode().end, block); - } - } - //As described above all tryCatch group that have finally block also should contains tryCatchBlockNode with default handler. //So we assume that instructions between end of tryCatchBlock and start of next tryCatchBlock with same default handler is required finally body. //There is at least two tryCatchBlockNodes in list cause there is always tryCatchBlockNode on first instruction of default handler: @@ -499,10 +476,7 @@ public class InternalFinallyBlockInliner { FinallyBlockInfo finallyInfo = new FinallyBlockInfo(startFinallyChain.getNext(), endFinallyChainExclusive); if (inlineFun.instructions.indexOf(finallyInfo.startIns) > inlineFun.instructions.indexOf(finallyInfo.endInsExclusive)) { - AbstractInsnNode startNode = finallyInfo.endInsExclusive; - AbstractInsnNode stopNode = finallyInfo.startIns; - writeNodes(startNode, stopNode); - throw new AssertionError(); + throw new AssertionError("Inconsistent finally: block end occurs before start " + traceInterval(finallyInfo.endInsExclusive, finallyInfo.startIns)); } return finallyInfo; @@ -553,18 +527,15 @@ public class InternalFinallyBlockInliner { if (!(curInstr instanceof LabelNode)) continue; LabelNode curLabel = (LabelNode) curInstr; - List startedTryBlocks = tryBlockStarts.get(curLabel); - if (startedTryBlocks != null) { - for (TryCatchBlockNodeInfo block : startedTryBlocks) { - 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); - } + List startedTryBlocks = getStartNodes(curLabel); + for (TryCatchBlockNodeInfo block : startedTryBlocks) { + 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); } - List endedTryBlocks = tryBlockEnds.get(curLabel); - if (endedTryBlocks == null) continue; + List endedTryBlocks = getEndNodes(curLabel); for (TryCatchBlockNodeInfo block : endedTryBlocks) { TryCatchBlockNodePosition info = processedBlocks.get(block); @@ -582,19 +553,6 @@ public class InternalFinallyBlockInliner { return result; } - private void writeNodes(AbstractInsnNode startNode, AbstractInsnNode stopNode) { - Textifier p = new Textifier(); - TraceMethodVisitor visitor = new TraceMethodVisitor(p); - while (startNode != stopNode) { - startNode.accept(visitor); - startNode = startNode.getNext(); - } - startNode.accept(visitor); - StringWriter out = new StringWriter(); - p.print(new PrintWriter(out)); - System.out.println(out.toString()); - } - private static void substituteReturnValueInFinally( int nonLocalVarIndex, @NotNull Type nonLocalReturnType, @@ -619,59 +577,38 @@ public class InternalFinallyBlockInliner { return result; } - private void sortTryCatchBlocks(@NotNull List inlineFunTryBlockInfo) { - Comparator comp = new Comparator() { - @Override - public int compare(@NotNull TryCatchBlockNodeInfo t1, @NotNull TryCatchBlockNodeInfo t2) { - int result = inlineFun.instructions.indexOf(t1.getNode().handler) - inlineFun.instructions.indexOf(t2.getNode().handler); - if (result == 0) { - result = inlineFun.instructions.indexOf(t1.getNode().start) - inlineFun.instructions.indexOf(t2.getNode().start); - if (result == 0) { - assert false : "Error: support multicatch finallies!"; - result = inlineFun.instructions.indexOf(t1.getNode().end) - inlineFun.instructions.indexOf(t2.getNode().end); - } - } - return result; - } - }; - Collections.sort(inlineFunTryBlockInfo, comp); + @Override + public int instructionIndex(@NotNull AbstractInsnNode inst) { + return inlineFun.instructions.indexOf(inst); + } - for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) { - TryCatchBlockNode node = info.getNode(); - assertNotEmptyTryNode(node); + private void substituteTryBlockNodes() { + inlineFun.tryCatchBlocks.clear(); + for (TryCatchBlockNodeInfo info : getNonEmptyNodes()) { + inlineFun.tryCatchBlocks.add(info.getNode()); } } - private void assertNotEmptyTryNode(TryCatchBlockNode node) { - LabelNode start = node.start; - AbstractInsnNode end = node.end; - while (end != start && end instanceof LabelNode) { - end = end.getPrevious(); + private static String traceInterval(AbstractInsnNode startNode, AbstractInsnNode stopNode) { + Textifier p = new Textifier(); + TraceMethodVisitor visitor = new TraceMethodVisitor(p); + while (startNode != stopNode) { + startNode.accept(visitor); + startNode = startNode.getNext(); } - assert start != end; + startNode.accept(visitor); + StringWriter out = new StringWriter(); + p.print(new PrintWriter(out)); + return out.toString(); } - private static int counter = 0; - + @SuppressWarnings({"UnusedDeclaration", "UseOfSystemOutOrSystemErr"}) + @TestOnly private void flushCurrentState(@NotNull AbstractInsnNode curNonLocal) { - substitureTryBlockNodes(); - - System.out.println( ); - System.out.println(); - System.out.println("Iteration: " + counter++); + substituteTryBlockNodes(); System.out.println("Will process instruction at : " + inlineFun.instructions.indexOf(curNonLocal) + " " + curNonLocal.toString()); String text = getNodeText(inlineFun); System.out.println(text); } - private void substitureTryBlockNodes() { - inlineFun.tryCatchBlocks.clear(); - for (TryCatchBlockNodeInfo info : inlineFunTryBlockInfo) { - inlineFun.tryCatchBlocks.add(info.getNode()); - } - } - - private int indexOf(AbstractInsnNode node) { - return inlineFun.instructions.indexOf(node) + 1; - } } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java index 9e70170d5fa..51e723a1c1f 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/MethodInliner.java @@ -586,11 +586,11 @@ public class MethodInliner { @NotNull //process local and global returns (local substituted with goto end-label global kept unchanged) - public static List processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { + public static List processReturns(@NotNull MethodNode node, @NotNull LabelOwner labelOwner, boolean remapReturn, Label endLabel) { if (!remapReturn) { return Collections.emptyList(); } - List result = new ArrayList(); + List result = new ArrayList(); InsnList instructions = node.instructions; AbstractInsnNode insnNode = instructions.getFirst(); while (insnNode != null) { @@ -621,7 +621,7 @@ public class MethodInliner { } //genetate finally block before nonLocalReturn flag/return/goto - result.add(new ExternalFinallyBlockInfo(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()) + result.add(new PointForExternalFinallyBlocks(isLocalReturn ? insnNode : insnNode.getPrevious(), getReturnType(insnNode.getOpcode()) )); } insnNode = insnNode.getNext(); @@ -629,13 +629,14 @@ public class MethodInliner { return result; } - public static class ExternalFinallyBlockInfo { + //Place to insert finally blocks from try blocks that wraps inline fun call + public static class PointForExternalFinallyBlocks { final AbstractInsnNode beforeIns; final Type returnType; - public ExternalFinallyBlockInfo(AbstractInsnNode beforeIns, Type returnType) { + public PointForExternalFinallyBlocks(AbstractInsnNode beforeIns, Type returnType) { this.beforeIns = beforeIns; this.returnType = returnType; } diff --git a/compiler/backend/src/org/jetbrains/jet/codegen/inline/TryBlockClustering.kt b/compiler/backend/src/org/jetbrains/jet/codegen/inline/TryBlockClustering.kt index 5ed54135855..48b48cb16e1 100644 --- a/compiler/backend/src/org/jetbrains/jet/codegen/inline/TryBlockClustering.kt +++ b/compiler/backend/src/org/jetbrains/jet/codegen/inline/TryBlockClustering.kt @@ -43,7 +43,7 @@ class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: override val endLabel: LabelNode get() = node.end override val handler: LabelNode - get() = node.handler!! + get() = node.handler override val type: String? get() = node.type } diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt new file mode 100644 index 00000000000..c901065d900 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt @@ -0,0 +1,101 @@ +import test.* +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.fail + +fun test1(): Holder { + val h = Holder("") + + try { + val internalResult = doCall(h) { + h += "in lambda body" + return h + } + } + finally { + h += "in call site finally" + } + + h += "local" + return h +} + +fun test1Lambda(): Holder { + val h = Holder("") + + val internalResult = doCall(h) { + try { + h += "in lambda body" + return h + } + finally { + h += "in lambda finally" + } + } + + + h += "local" + return h +} + +fun test2(h: Holder): Holder { + try { + val internalResult = doCallWithException(h) { + h += "in lambda body" + return h + } + } + finally { + h += "in call site finally" + } + + h += "local" + return h +} + +fun test2Lambda(h: Holder): Holder { + + val internalResult = doCallWithException(h) { + try { + h += "in lambda body" + return h + } + finally { + h += "in lambda finally" + } + } + + h += "local" + return h +} + +fun box(): String { + val test = test1() + if (test.value != "in lambda body -> inline fun finally -> in call site finally") return "fail 1: $test" + + val testLambda = test1Lambda() + if (testLambda.value != "in lambda body -> in lambda finally -> inline fun finally") return "fail 1 lambda: $testLambda" + + var h = Holder() + assertError(2, h, "in lambda body -> inline fun finally -> in call site finally") { + test2(h) + } + + h = Holder() + assertError(22, h, "in lambda body -> in lambda finally -> inline fun finally") { + test2Lambda(h) + } + + return "OK" +} + + +public fun assertError(index: Int, h: Holder, expected: String, l: (h: Holder) -> Holder) { + try { + l(h) + fail("fail $index: no error") + } + catch (e: Exception) { + assertEquals(expected, h.value, "failed on $index") + } +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.2.kt new file mode 100644 index 00000000000..c1b24ca2490 --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.2.kt @@ -0,0 +1,33 @@ +package test + +public class Holder(var value: String = "") { + + public fun plusAssign(s: String?) { + if (value.length != 0) { + value += " -> " + } + value += s + } + + override fun toString(): String { + return value + } + +} + +public inline fun doCall(h: Holder, block: ()-> R) : R { + try { + return block() + } finally { + h += "inline fun finally" + } +} + +public inline fun doCallWithException(h: Holder, block: ()-> R) : R { + try { + return block() + } finally { + h += "inline fun finally" + throw RuntimeException("fail"); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt new file mode 100644 index 00000000000..3b3cf3c880b --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt @@ -0,0 +1,101 @@ +import test.* +import kotlin.test.assertEquals +import kotlin.test.assertTrue +import kotlin.test.fail + +fun test1(): Holder { + val h = Holder("") + + try { + doCall(h) { + h += "in lambda body" + return h + } + } + finally { + h += "in call site finally" + } + + h += "local" + return h +} + +fun test1Lambda(): Holder { + val h = Holder("") + + doCall(h) { + try { + h += "in lambda body" + return h + } + finally { + h += "in lambda finally" + } + } + + + h += "local" + return h +} + +fun test2(h: Holder): Holder { + try { + doCallWithException(h) { + h += "in lambda body" + return h + } + } + finally { + h += "in call site finally" + } + + h += "local" + return h +} + +fun test2Lambda(h: Holder): Holder { + + doCallWithException(h) { + try { + h += "in lambda body" + return h + } + finally { + h += "in lambda finally" + } + } + + h += "local" + return h +} + +fun box(): String { + val test = test1() + if (test.value != "in lambda body -> inline fun finally -> in call site finally") return "fail 1: $test" + + val testLambda = test1Lambda() + if (testLambda.value != "in lambda body -> in lambda finally -> inline fun finally") return "fail 1 lambda: $testLambda" + + var h = Holder() + assertError(2, h, "in lambda body -> inline fun finally -> in call site finally") { + test2(h) + } + + h = Holder() + assertError(22, h, "in lambda body -> in lambda finally -> inline fun finally") { + test2Lambda(h) + } + + return "OK" +} + + +public fun assertError(index: Int, h: Holder, expected: String, l: (h: Holder) -> Holder) { + try { + l(h) + fail("fail $index: no error") + } + catch (e: Exception) { + assertEquals(expected, h.value, "failed on $index") + } +} diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.2.kt new file mode 100644 index 00000000000..79c3e6ff2de --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.2.kt @@ -0,0 +1,33 @@ +package test + +public class Holder(var value: String = "") { + + public fun plusAssign(s: String?) { + if (value.length != 0) { + value += " -> " + } + value += s + } + + override fun toString(): String { + return value + } + +} + +public inline fun doCall(h: Holder, block: ()-> R) { + try { + block() + } finally { + h += "inline fun finally" + } +} + +public inline fun doCallWithException(h: Holder, block: ()-> R) { + try { + block() + } finally { + h += "inline fun finally" + throw RuntimeException("fail"); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt b/compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt index 2be9035a4ad..c8a20c43cf1 100644 --- a/compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt +++ b/compiler/testData/codegen/bytecodeText/inline/splitedExceptionTable.kt @@ -25,31 +25,5 @@ fun box() : String { return "fail" } -// maybe we shouldinline fun test(s: ()->Int): Int { -var i = 0; -try { -i = s() -i = i + 10 -} finally { -return i -} -} - -fun box() : String { - var p: Int = 1 - test { - try { - p = 1 - return "OK" //finally from inline fun doen't split this try - } catch(e: Exception) { - p = -1; - p - } finally { - p++ - } - - } - return "fail" -} -check test data +// maybe we should check test data // 13 TRYCATCHBLOCK diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java index 6211670f4db..eaea62dfd38 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxInlineCodegenTestGenerated.java @@ -422,6 +422,18 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxCodegenT String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/callSite.1.kt"); doTestMultiFileWithInlineCheck(fileName); } + + @TestMetadata("exceptionTableSplit.1.kt") + public void testExceptionTableSplit() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + + @TestMetadata("exceptionTableSplitNoReturn.1.kt") + public void testExceptionTableSplitNoReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } } @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite") diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java index dda9a2830d5..8b30844ba5e 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -422,6 +422,18 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/callSite.1.kt"); doBoxTestWithInlineCheck(fileName); } + + @TestMetadata("exceptionTableSplit.1.kt") + public void testExceptionTableSplit() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplit.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + + @TestMetadata("exceptionTableSplitNoReturn.1.kt") + public void testExceptionTableSplitNoReturn() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/callSite/exceptionTableSplitNoReturn.1.kt"); + doBoxTestWithInlineCheck(fileName); + } } @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/declSite")