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 a01d0fa96b9..630b53cd41e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt @@ -23,29 +23,34 @@ import org.jetbrains.org.objectweb.asm.tree.* import java.util.Comparator import java.util.Collections -public abstract class CoveringTryCatchNodeProcessor where T : SplittableInterval { +public abstract class CoveringTryCatchNodeProcessor { - public val tryBlocksMetaInfo: IntervalMetaInfo = IntervalMetaInfo(); + public val tryBlocksMetaInfo: IntervalMetaInfo = IntervalMetaInfo(); public val localVarsMetaInfo: IntervalMetaInfo = IntervalMetaInfo(); - public val coveringFromInnermost: List + public val coveringFromInnermost: List get() = tryBlocksMetaInfo.currentIntervals.reverse() - public fun getStartNodes(label: LabelNode): List { + public fun getStartNodes(label: LabelNode): List { return tryBlocksMetaInfo.intervalStarts.get(label) } - public fun getEndNodes(label: LabelNode): List { + public fun getEndNodes(label: LabelNode): List { return tryBlocksMetaInfo.intervalEnds.get(label) } + public open fun processInstruction(curInstr: AbstractInsnNode, directOrder: Boolean) { + if (curInstr !is LabelNode) return + + 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 - public open fun updateCoveringTryBlocks(curIns: AbstractInsnNode, directOrder: Boolean) { - if (curIns !is LabelNode) return - - for (startNode in tryBlocksMetaInfo.infoToClose(curIns, directOrder)) { + protected open fun updateCoveringTryBlocks(curIns: LabelNode, directOrder: Boolean) { + for (startNode in tryBlocksMetaInfo.closeIntervals(curIns, directOrder)) { val pop = tryBlocksMetaInfo.currentIntervals.pop() //Temporary disabled cause during patched structure of exceptions changed // if (startNode != pop) { @@ -55,19 +60,17 @@ public abstract class CoveringTryCatchNodeProcessor wh //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.infoToOpen(curIns, directOrder).reverse()) { + for (info in tryBlocksMetaInfo.openIntervals(curIns, directOrder).reverse()) { tryBlocksMetaInfo.currentIntervals.add(info) } } - public open fun updateCoveringLocalVars(curIns: AbstractInsnNode, directOrder: Boolean) { - if (curIns !is LabelNode) return - - localVarsMetaInfo.infoToClose(curIns, directOrder).forEach { + protected open fun updateCoveringLocalVars(curIns: LabelNode, directOrder: Boolean) { + localVarsMetaInfo.closeIntervals(curIns, directOrder).forEach { localVarsMetaInfo.currentIntervals.pop() } - localVarsMetaInfo.infoToOpen(curIns, directOrder).forEach { + localVarsMetaInfo.openIntervals(curIns, directOrder).forEach { localVarsMetaInfo.currentIntervals.add(it) } } @@ -75,23 +78,38 @@ public abstract class CoveringTryCatchNodeProcessor wh public abstract fun instructionIndex(inst: AbstractInsnNode): Int public fun sortTryCatchBlocks() { - val comp = Comparator { t1: T, t2: T -> + val comp = Comparator { t1: TryCatchBlockNodeInfo, t2: TryCatchBlockNodeInfo -> var result = instructionIndex(t1.handler) - instructionIndex(t2.handler) if (result == 0) { - result = instructionIndex((t1 as Interval).startLabel) - instructionIndex((t2 as Interval).startLabel) + result = instructionIndex(t1.startLabel) - instructionIndex(t2.startLabel) if (result == 0) { assert(false, "Error: support multicatch finallies!") - result = instructionIndex((t1 as Interval).endLabel) - instructionIndex((t2 as Interval).endLabel) + result = instructionIndex(t1.endLabel) - instructionIndex(t2.endLabel) } } result } - Collections.sort(tryBlocksMetaInfo.allIntervals, comp) + Collections.sort(tryBlocksMetaInfo.allIntervals, comp) + } + + protected fun substituteTryBlockNodes(node: MethodNode) { + node.tryCatchBlocks.clear() + for (info in tryBlocksMetaInfo.getNonEmptyNodes()) { + node.tryCatchBlocks.add(info.node) + } + } + + + public fun substituteLocalVarTable(node: MethodNode) { + node.localVariables.clear() + for (info in localVarsMetaInfo.getNonEmptyNodes()) { + node.localVariables.add(info.node) + } } } -class IntervalMetaInfo { +class IntervalMetaInfo> { val intervalStarts = LinkedListMultimap.create() @@ -107,22 +125,26 @@ class IntervalMetaInfo { allIntervals.add(newInfo) } - fun remapStartLabel(oldStart: LabelNode, remapped: T) { + private fun remapStartLabel(oldStart: LabelNode, remapped: T) { intervalStarts.remove(oldStart, remapped) intervalStarts.put(remapped.startLabel, remapped) } - fun remapEndLabel(oldEnd: LabelNode, remapped: T) { + private fun remapEndLabel(oldEnd: LabelNode, remapped: T) { intervalEnds.remove(oldEnd, remapped) intervalEnds.put(remapped.endLabel, remapped) } - fun split(interval: SplittableInterval, by : Interval, keepStart: Boolean): SplittedPair { + fun splitCurrentIntervals(by : Interval, keepStart: Boolean): List> { + return currentIntervals.map { split(it, by, keepStart) } + } + + fun split(interval: T, by : Interval, keepStart: Boolean): SplittedPair { val splittedPair = interval.split(by, keepStart) if (!keepStart) { - remapStartLabel((splittedPair.newPart as Interval).startLabel, splittedPair.patchedPart) + remapStartLabel(splittedPair.newPart.startLabel, splittedPair.patchedPart) } else { - remapEndLabel((splittedPair.newPart as Interval).endLabel, splittedPair.patchedPart) + remapEndLabel(splittedPair.newPart.endLabel, splittedPair.patchedPart) } addNewInterval(splittedPair.newPart) return splittedPair @@ -132,9 +154,9 @@ class IntervalMetaInfo { return allIntervals.filterNot { isEmptyInterval(it) } } - fun infoToClose(curIns: LabelNode, directOrder: Boolean) = if (!directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns) + fun closeIntervals(curIns: LabelNode, directOrder: Boolean) = if (!directOrder) intervalStarts.get(curIns) else intervalEnds.get(curIns) - fun infoToOpen(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) private fun isEmptyInterval(node: T): Boolean { val start = node.startLabel @@ -146,7 +168,7 @@ class IntervalMetaInfo { } } -public class DefaultProcessor(val node: MethodNode) : CoveringTryCatchNodeProcessor() { +public class DefaultProcessor(val node: MethodNode) : CoveringTryCatchNodeProcessor() { init { node.tryCatchBlocks.forEach { addTryNode(it) } 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 2239fc65aac..bc4e60f724c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -637,8 +637,7 @@ public class InlineCodegen extends CallGenerator { AbstractInsnNode curInstr = intoNode.instructions.getFirst(); while (curInstr != null) { - processor.updateCoveringTryBlocks(curInstr, true); - processor.updateCoveringLocalVars(curInstr, true); + processor.processInstruction(curInstr, true); MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr); if (extension != null) { @@ -655,34 +654,25 @@ public class InlineCodegen extends CallGenerator { 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 = new ArrayList(processor.getTryBlocksMetaInfo().getCurrentIntervals()); - for (TryCatchBlockNodeInfo block : blocks) { - processor.getTryBlocksMetaInfo().split(block, new SimpleInterval((LabelNode) start.info, (LabelNode) end.info), false); - } + SimpleInterval splitBy = new SimpleInterval((LabelNode) start.info, (LabelNode) end.info); + processor.getTryBlocksMetaInfo().splitCurrentIntervals(splitBy, false); + + processor.getLocalVarsMetaInfo().splitCurrentIntervals(splitBy, false); } curInstr = curInstr.getNext(); } processor.sortTryCatchBlocks(); - Iterable nodes = processor.getNonEmptyNodes(); - intoNode.tryCatchBlocks.clear(); - for (TryCatchBlockNodeInfo node : nodes) { - intoNode.tryCatchBlocks.add(node.getNode()); - } + processor.substituteTryBlockNodes(intoNode); - intoNode.localVariables.clear(); - for (LocalVarNodeWrapper interval : processor.getLocalVarsMetaInfo().getAllIntervals()) { - intoNode.localVariables.add(interval.getNode()); - } + //processor.substituteLocalVarTable(intoNode); } private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) { return new NestedSourceMapper(sourceMapper, nodeAndSmap.getRanges(), nodeAndSmap.getClassSMAP().getSourceInfo()); } - } 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 fe3c2b93905..9c8e83eeb1a 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java @@ -20,6 +20,7 @@ import com.google.common.base.Objects; import com.google.common.collect.Lists; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; +import org.jetbrains.annotations.ReadOnly; import org.jetbrains.annotations.TestOnly; import org.jetbrains.kotlin.codegen.AsmUtil; import org.jetbrains.org.objectweb.asm.Label; @@ -35,7 +36,7 @@ import java.util.*; import static org.jetbrains.kotlin.codegen.inline.InlineCodegenUtil.*; -public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { +public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { private static class FinallyBlockInfo { @@ -68,8 +69,13 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor localVars = new ArrayList(); + for (LocalVariableNode var : inlineFun.localVariables) { + localVars.add(new LocalVarNodeWrapper(var)); + } + if (hasFinallyBlocks(inlineFunTryBlockInfo)) { - new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo).processInlineFunFinallyBlocks(); + new InternalFinallyBlockInliner(inlineFun, inlineFunTryBlockInfo, localVars).processInlineFunFinallyBlocks(); } } @@ -79,11 +85,14 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor inlineFunTryBlockInfo) { + private InternalFinallyBlockInliner(@NotNull MethodNode inlineFun, List inlineFunTryBlockInfo, List localVariableInfo) { this.inlineFun = inlineFun; - IntervalMetaInfo info = getTryBlocksMetaInfo(); for (TryCatchBlockNodeInfo block : inlineFunTryBlockInfo) { - info.addNewInterval(block); + getTryBlocksMetaInfo().addNewInterval(block); + } + + for (LocalVarNodeWrapper wrapper : localVariableInfo) { + getLocalVarsMetaInfo().addNewInterval(wrapper); } } @@ -108,7 +117,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor currentCoveringNodes = getCoveringFromInnermost(); - checkCoveringBlocksInvariant(currentCoveringNodes); - if (currentCoveringNodes.isEmpty() || - currentCoveringNodes.get(currentCoveringNodes.size() - 1).getOnlyCopyNotProcess()) { + List currentCoveringNodesFromOuterMost = getTryBlocksMetaInfo().getCurrentIntervals(); + checkCoveringBlocksInvariant(currentCoveringNodesFromOuterMost); + if (currentCoveringNodesFromOuterMost.isEmpty() || + currentCoveringNodesFromOuterMost.get(0).getOnlyCopyNotProcess()) { curIns = curIns.getPrevious(); continue; } @@ -134,7 +143,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor> clustersFromInnermost = InlinePackage.doClustering(currentCoveringNodes); + List> clustersFromInnermost = InlinePackage.doClustering(getCoveringFromInnermost()); Iterator> tryCatchBlockIterator = clustersFromInnermost.iterator(); checkClusterInvariant(clustersFromInnermost); @@ -203,12 +212,13 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor coveringTryCatchBlocks) { + private static void checkCoveringBlocksInvariant(@ReadOnly @NotNull List currentCoveringNodesFromOuterMost) { boolean isWasOnlyLocal = false; - for (TryCatchBlockNodeInfo info : Lists.reverse(coveringTryCatchBlocks)) { - assert !isWasOnlyLocal || info.getOnlyCopyNotProcess(); - if (info.getOnlyCopyNotProcess()) { - isWasOnlyLocal = true; - } + for (TryCatchBlockNodeInfo info : currentCoveringNodesFromOuterMost) { + assert !isWasOnlyLocal || info.getOnlyCopyNotProcess() : "There are some problems with try-catch structure"; + isWasOnlyLocal = info.getOnlyCopyNotProcess(); } } @@ -339,7 +347,9 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor labelsInsideFinally) { @@ -390,9 +402,10 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor inlineFunTryBlockInfo) { @@ -411,7 +424,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor tryCatchBlocks + @ReadOnly @NotNull List tryCatchBlocks ) { if (tryCatchBlock.getOnlyCopyNotProcess()) { AbstractInsnNode start = new LabelNode(); @@ -539,13 +552,6 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor { +open class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: Boolean) : IntervalWithHandler, SplittableInterval { override val startLabel: LabelNode get() = node.start override val endLabel: LabelNode