diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt index a5b9cf19ce2..8be21ccd75f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt @@ -34,9 +34,6 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) { public var nextFreeLocalIndex: Int = parameterSize private set - public val coveringFromInnermost: List - get() = tryBlocksMetaInfo.currentIntervals.reverse() - public fun getStartNodes(label: LabelNode): List { return tryBlocksMetaInfo.intervalStarts.get(label) } @@ -53,48 +50,14 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) { } if (curInstr is LabelNode) { - updateCoveringTryBlocks(curInstr, directOrder) - updateCoveringLocalVars(curInstr, directOrder) - } - } - - //Keep information about try blocks that cover current instruction - - // pushing and popping it to stack entering and exiting tryCatchBlock start and end labels - protected open fun updateCoveringTryBlocks(curIns: LabelNode, directOrder: Boolean) { - for (startNode in tryBlocksMetaInfo.closeIntervals(curIns, directOrder)) { - assert(!startNode.isEmpty(), {"Try block should be non-empty"}) - val pop = tryBlocksMetaInfo.currentIntervals.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) - for (info in tryBlocksMetaInfo.openIntervals(curIns, directOrder).reverse()) { - assert(!info.isEmpty(), {"Try block should be non-empty"}) - tryBlocksMetaInfo.currentIntervals.add(info) - } - } - - protected open fun updateCoveringLocalVars(curIns: LabelNode, directOrder: Boolean) { - localVarsMetaInfo.closeIntervals(curIns, directOrder).filterNot { - it.isEmpty() - } forEach { - localVarsMetaInfo.currentIntervals.pop() - } - - localVarsMetaInfo.openIntervals(curIns, directOrder).filterNot { - it.isEmpty() - } forEach { - localVarsMetaInfo.currentIntervals.add(it) + tryBlocksMetaInfo.processCurrent(curInstr, directOrder) + localVarsMetaInfo.processCurrent(curInstr, directOrder) } } public abstract fun instructionIndex(inst: AbstractInsnNode): Int - public fun sortTryCatchBlocks() { + public fun sortTryCatchBlocks(intervals: List): List { val comp = Comparator { t1: TryCatchBlockNodeInfo, t2: TryCatchBlockNodeInfo -> var result = instructionIndex(t1.handler) - instructionIndex(t2.handler) if (result == 0) { @@ -107,11 +70,13 @@ public abstract class CoveringTryCatchNodeProcessor(parameterSize: Int) { result } - Collections.sort(tryBlocksMetaInfo.allIntervals, comp) + Collections.sort(intervals, comp) + return intervals; } protected fun substituteTryBlockNodes(node: MethodNode) { node.tryCatchBlocks.clear() + sortTryCatchBlocks(tryBlocksMetaInfo.allIntervals) for (info in tryBlocksMetaInfo.getMeaningfulIntervals()) { node.tryCatchBlocks.add(info.node) } @@ -134,7 +99,7 @@ class IntervalMetaInfo> { val allIntervals: ArrayList = arrayListOf() - val currentIntervals: Stack = Stack() + val currentIntervals: MutableSet = linkedSetOf() fun addNewInterval(newInfo: T) { intervalStarts.put(newInfo.startLabel, newInfo) @@ -156,6 +121,18 @@ class IntervalMetaInfo> { return currentIntervals.map { split(it, by, keepStart) } } + fun processCurrent(curIns: LabelNode, directOrder: Boolean) { + getInterval(curIns, directOrder).forEach { + val b = currentIntervals.add(it) + assert(b, "Wrong interval structure: $curIns, $it") + } + + getInterval(curIns, !directOrder).forEach { + val b = currentIntervals.remove(it) + assert(b, "Wrong interval structure: $curIns, $it") + } + } + fun split(interval: T, by : Interval, keepStart: Boolean): SplittedPair { val splittedPair = interval.split(by, keepStart) if (!keepStart) { @@ -167,9 +144,7 @@ class IntervalMetaInfo> { return splittedPair } - fun closeIntervals(curIns: LabelNode, directOrder: Boolean) = if (!directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns) - - fun openIntervals(curIns: LabelNode, directOrder: Boolean) = if (directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns) + fun getInterval(curIns: LabelNode, isOpen: Boolean) = if (isOpen) intervalStarts.get(curIns) else intervalEnds.get(curIns) } private fun Interval.isMeaningless(): Boolean { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java index 2ae8f5bdf59..bdf85b4ae43 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -698,7 +698,6 @@ public class InlineCodegen extends CallGenerator { curInstr = curInstr.getNext(); } - processor.sortTryCatchBlocks(); processor.substituteTryBlockNodes(intoNode); //processor.substituteLocalVarTable(intoNode); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java index 5423f6bd02a..e63069f5e0f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java @@ -128,10 +128,12 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { continue; } - List currentCoveringNodesFromOuterMost = getTryBlocksMetaInfo().getCurrentIntervals(); - checkCoveringBlocksInvariant(currentCoveringNodesFromOuterMost); - if (currentCoveringNodesFromOuterMost.isEmpty() || - currentCoveringNodesFromOuterMost.get(0).getOnlyCopyNotProcess()) { + List currentCoveringNodesFromInnermost = + sortTryCatchBlocks(new ArrayList(getTryBlocksMetaInfo().getCurrentIntervals())); + checkCoveringBlocksInvariant(Lists.reverse(currentCoveringNodesFromInnermost)); + + if (currentCoveringNodesFromInnermost.isEmpty() || + currentCoveringNodesFromInnermost.get(currentCoveringNodesFromInnermost.size() - 1).getOnlyCopyNotProcess()) { curIns = curIns.getPrevious(); continue; } @@ -146,14 +148,14 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { // 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> clustersFromInnermost = InlinePackage.doClustering(getCoveringFromInnermost()); + List> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodesFromInnermost); Iterator> tryCatchBlockIterator = clustersFromInnermost.iterator(); checkClusterInvariant(clustersFromInnermost); List additionalNodesToSplit = new ArrayList(); while (tryCatchBlockIterator.hasNext()) { - TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next(); + TryBlockCluster clusterToFindFinally = tryCatchBlockIterator.next(); List clusterBlocks = clusterToFindFinally.getBlocks(); TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1); @@ -401,7 +403,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { //TODO add assert } - sortTryCatchBlocks(); + sortTryCatchBlocks(tryBlocksMetaInfo.getAllIntervals()); } private static LabelNode getNewOrOldLabel(LabelNode oldHandler, @NotNull Set labelsInsideFinally) { @@ -412,14 +414,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { 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 - @Override - protected void updateCoveringTryBlocks(@NotNull LabelNode curIns, boolean directOrder) { - super.updateCoveringTryBlocks(curIns, directOrder); - checkCoveringBlocksInvariant(getTryBlocksMetaInfo().getCurrentIntervals()); - } - private static boolean hasFinallyBlocks(List inlineFunTryBlockInfo) { for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { if (!block.getOnlyCopyNotProcess() && block.getNode().type == null) { diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt new file mode 100644 index 00000000000..986cc9cdc5e --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt @@ -0,0 +1,40 @@ +import test.* + +public class ClassA { + val LOCK = Object() + + var result = "fail" + + fun test(name1: String?, name2: String, cond: Boolean) { + mySynchronized (LOCK) { + var name = name1 + + if (name == null) { + if (cond) { + result = "NLR" + name2 + return + } + + name = name2 + } + + result = name + name2 + + val length = name.length() + } + } +} + +fun box(): String { + val classA = ClassA() + classA.test(null, "2", true) + if (classA.result != "NLR2") return "fail 1: ${classA.result}" + + classA.test(null, "K", false) + if (classA.result != "KK") return "fail 1: ${classA.result}" + + + classA.test("O", "K", false) + return classA.result +} + diff --git a/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.2.kt b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.2.kt new file mode 100644 index 00000000000..767b6472bba --- /dev/null +++ b/compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.2.kt @@ -0,0 +1,15 @@ +package test + +inline fun mySynchronized(lock: Any, block: () -> R): R { + monitorCall(lock) + try { + return block() + } + finally { + monitorCall(lock) + } +} + +fun monitorCall(lock: Any) { + +} \ No newline at end of file diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java index 31f9c7def62..8cfbcc7ed01 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxInlineCodegenTestGenerated.java @@ -725,6 +725,21 @@ public class BlackBoxInlineCodegenTestGenerated extends AbstractBlackBoxInlineCo doTestMultiFileWithInlineCheck(fileName); } } + + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Variables extends AbstractBlackBoxInlineCodegenTest { + public void testAllFilesPresentInVariables() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("kt7792.1.kt") + public void testKt7792() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt"); + doTestMultiFileWithInlineCheck(fileName); + } + } } } diff --git a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java index 67278f2cf5f..67d5f2a4b06 100644 --- a/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/jvm/compiler/CompileKotlinAgainstInlineKotlinTestGenerated.java @@ -725,6 +725,21 @@ public class CompileKotlinAgainstInlineKotlinTestGenerated extends AbstractCompi doBoxTestWithInlineCheck(fileName); } } + + @TestMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Variables extends AbstractCompileKotlinAgainstInlineKotlinTest { + public void testAllFilesPresentInVariables() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables"), Pattern.compile("^(.+)\\.1.kt$"), true); + } + + @TestMetadata("kt7792.1.kt") + public void testKt7792() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxInline/nonLocalReturns/tryFinally/variables/kt7792.1.kt"); + doBoxTestWithInlineCheck(fileName); + } + } } }