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 c4931262e77..1207f8a2a7c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/CoveringTryCatchNodeProcessor.kt @@ -16,46 +16,40 @@ package org.jetbrains.kotlin.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 java.util.ArrayList import com.intellij.util.containers.Stack -import org.jetbrains.org.objectweb.asm.tree.AbstractInsnNode -import org.jetbrains.org.objectweb.asm.tree.TryCatchBlockNode +import org.jetbrains.org.objectweb.asm.tree.* import java.util.Comparator import java.util.Collections public abstract class CoveringTryCatchNodeProcessor() { - private val tryBlockStarts = LinkedListMultimap.create() + public val tryBlocksMetaInfo: IntervalMetaInfo = IntervalMetaInfo(); - private val tryBlockEnds = LinkedListMultimap.create() - - public val allTryCatchNodes: ArrayList = arrayListOf() - - private val currentCoveringBlocks: Stack = Stack() + public val localVarsMetaInfo: IntervalMetaInfo = IntervalMetaInfo(); public val coveringFromInnermost: List - get() = currentCoveringBlocks.reverse() + get() = tryBlocksMetaInfo.currentIntervals.reverse() - fun addNewTryCatchNode(newInfo: T) { - tryBlockStarts.put(newInfo.startLabel, newInfo) - tryBlockEnds.put(newInfo.endLabel, newInfo) - allTryCatchNodes.add(newInfo) + protected fun addNewTryCatchNode(newInfo: T) { + tryBlocksMetaInfo.addNewInterval(newInfo) + } + + protected fun addNewLocalVar(newInfo: LocalVarNodeWrapper) { + localVarsMetaInfo.addNewInterval(newInfo) } fun remapStartLabel(oldStart: LabelNode, remapped: T) { - tryBlockStarts.remove(oldStart, remapped) - tryBlockStarts.put(remapped.startLabel, remapped) + tryBlocksMetaInfo.remapStartLabel(oldStart, remapped) } public fun getStartNodes(label: LabelNode): List { - return tryBlockStarts.get(label) + return tryBlocksMetaInfo.intervalStarts.get(label) } public fun getEndNodes(label: LabelNode): List { - return tryBlockEnds.get(label) + return tryBlocksMetaInfo.intervalEnds.get(label) } //Keep information about try blocks that cover current instruction - @@ -63,9 +57,8 @@ public abstract class CoveringTryCatchNodeProcessor() { 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() + for (startNode in tryBlocksMetaInfo.infoToClose(curIns, directOrder)) { + 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()) @@ -74,25 +67,28 @@ public abstract class CoveringTryCatchNodeProcessor() { //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) + for (info in tryBlocksMetaInfo.infoToOpen(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 { + localVarsMetaInfo.currentIntervals.pop() + } + + localVarsMetaInfo.infoToOpen(curIns, directOrder).forEach { + localVarsMetaInfo.currentIntervals.add(it) } } 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) } + return tryBlocksMetaInfo.getNonEmptyNodes() } public fun sortTryCatchBlocks() { @@ -108,25 +104,79 @@ public abstract class CoveringTryCatchNodeProcessor() { result } - Collections.sort(allTryCatchNodes, comp) + Collections.sort(tryBlocksMetaInfo.allIntervals, comp) + } +} + +class IntervalMetaInfo { + + val intervalStarts = LinkedListMultimap.create() + + val intervalEnds = LinkedListMultimap.create() + + val allIntervals: ArrayList = arrayListOf() + + val currentIntervals: Stack = Stack() + + fun addNewInterval(newInfo: T) { + intervalStarts.put(newInfo.startLabel, newInfo) + intervalEnds.put(newInfo.endLabel, newInfo) + allIntervals.add(newInfo) } + fun remapStartLabel(oldStart: LabelNode, remapped: T) { + intervalStarts.remove(oldStart, remapped) + intervalStarts.put(remapped.startLabel, remapped) + } + + fun remapEndLabel(oldEnd: LabelNode, remapped: T) { + intervalEnds.remove(oldEnd, remapped) + intervalEnds.put(remapped.startLabel, remapped) + } + + public fun getNonEmptyNodes(): List { + return allIntervals.filterNot { isEmptyInterval(it) } + } + + fun infoToClose(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) + + 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 class DefaultProcessor(val node: MethodNode) : CoveringTryCatchNodeProcessor() { init { - node.tryCatchBlocks.forEach { addNode(it) } + node.tryCatchBlocks.forEach { addTryNode(it) } + node.localVariables.forEach { addLocalVarNode(it) } } - fun addNode(node: TryCatchBlockNode) { + fun addLocalVarNode(it: LocalVariableNode) { + addNewLocalVar(LocalVarNodeWrapper(it)) + } + + fun addTryNode(node: TryCatchBlockNode) { addNewTryCatchNode(TryCatchBlockNodeWrapper(node)) } override fun instructionIndex(inst: AbstractInsnNode): Int { return node.instructions.indexOf(inst) } +} +public class LocalVarNodeWrapper(val node: LocalVariableNode) : Interval { + override val startLabel: LabelNode + get() = node.start + override val endLabel: LabelNode + get() = node.end } public class TryCatchBlockNodeWrapper(val node: TryCatchBlockNode) : IntervalWithHandler { 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 00346c59ded..5f2d9f09c6e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InlineCodegen.java @@ -638,6 +638,7 @@ public class InlineCodegen extends CallGenerator { AbstractInsnNode curInstr = intoNode.instructions.getFirst(); while (curInstr != null) { processor.updateCoveringTryBlocks(curInstr, true); + processor.updateCoveringLocalVars(curInstr, true); MethodInliner.PointForExternalFinallyBlocks extension = extensionPoints.get(curInstr); if (extension != null) { @@ -668,7 +669,7 @@ public class InlineCodegen extends CallGenerator { processor.remapStartLabel(oldStart, previous); TryCatchBlockNode additionalNode = new TryCatchBlockNode(oldStart, (LabelNode) start.info, node.handler, node.type); - processor.addNode(additionalNode); + processor.addTryNode(additionalNode); } } @@ -681,6 +682,12 @@ public class InlineCodegen extends CallGenerator { for (TryCatchBlockNodeWrapper node : nodes) { intoNode.tryCatchBlocks.add(node.getNode()); } + + intoNode.localVariables.clear(); + List intervals = processor.getLocalVarsMetaInfo().getAllIntervals(); + for (LocalVarNodeWrapper interval : intervals) { + intoNode.localVariables.add(interval.getNode()); + } } private SourceMapper createNestedSourceMapper(@NotNull SMAPAndMethodNode nodeAndSmap) { 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 93b148e2a5f..abc0a806c6e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/InternalFinallyBlockInliner.java @@ -144,7 +144,7 @@ public class InternalFinallyBlockInliner extends CoveringTryCatchNodeProcessor clusterBlocks = clusterToFindFinally.getBlocks(); TryCatchBlockNodeInfo nodeWithDefaultHandlerIfExists = clusterBlocks.get(clusterBlocks.size() - 1); - FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getAllTryCatchNodes()); + FinallyBlockInfo finallyInfo = findFinallyBlockBody(nodeWithDefaultHandlerIfExists, getTryBlocksMetaInfo().getAllIntervals()); if (finallyInfo == null) continue; if (nodeWithDefaultHandlerIfExists.getOnlyCopyNotProcess()) { diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/TryBlockClustering.kt b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/TryBlockClustering.kt index 4b4be36519f..f0e255d7e8e 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/TryBlockClustering.kt +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/inline/TryBlockClustering.kt @@ -26,14 +26,24 @@ enum class TryCatchPosition { INNER } -trait IntervalWithHandler { +public class SplittedPair(val patchedPart: SplittableInterval, val newPart: SplittableInterval) + +trait Interval { val startLabel: LabelNode val endLabel: LabelNode +} + +trait SplittableInterval : Interval { + fun split(split: Interval, keepStart: Boolean): SplittedPair +} + + +trait IntervalWithHandler : Interval { val handler: LabelNode val type: String? } -class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: Boolean) : IntervalWithHandler { +class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: Boolean) : IntervalWithHandler, SplittableInterval { override val startLabel: LabelNode get() = node.start override val endLabel: LabelNode @@ -42,9 +52,24 @@ class TryCatchBlockNodeInfo(val node: TryCatchBlockNode, val onlyCopyNotProcess: get() = node.handler override val type: String? get() = node.type + + override fun split(split: Interval, keepStart: Boolean): SplittedPair { + val newPart = if (keepStart) { + val oldEnd = endLabel + node.end = split.startLabel + TryCatchBlockNodeInfo(TryCatchBlockNode(split.endLabel, oldEnd, handler, type), onlyCopyNotProcess) + } else { + val oldStart = startLabel + node.start = split.endLabel + TryCatchBlockNodeInfo(TryCatchBlockNode(oldStart, split.startLabel, handler, type), onlyCopyNotProcess) + } + return SplittedPair(this, newPart) + } } -class TryCatchBlockNodePosition(val nodeInfo: TryCatchBlockNodeInfo, var position: TryCatchPosition): IntervalWithHandler by nodeInfo +class TryCatchBlockNodePosition(val nodeInfo: TryCatchBlockNodeInfo, var position: TryCatchPosition): IntervalWithHandler by nodeInfo { + +} class TryBlockCluster(val blocks: MutableList) { val defaultHandler: T?