[FIR][CFG] Support multiple levels of finally blocks in jumps

Jump inside try expressions now goes through all finally blocks between
it and the target
This commit is contained in:
Andrey Zinovyev
2021-08-04 16:52:42 +03:00
committed by teamcityserver
parent 06b23d5937
commit 32426da625
2 changed files with 38 additions and 16 deletions
@@ -6,6 +6,7 @@
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.asReversedFrozen
import org.jetbrains.kotlin.fir.declarations.FirDeclaration
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
@@ -16,6 +17,11 @@ abstract class Stack<T> {
abstract fun pop(): T
abstract fun push(value: T)
abstract fun reset()
/**
* returns all elements of the stack in order of retrieval
*/
abstract fun all(): List<T>
}
fun <T> stackOf(vararg values: T): Stack<T> = StackImpl(*values)
@@ -38,6 +44,8 @@ private class StackImpl<T>(vararg values: T) : Stack<T>() {
override fun reset() {
stack.clear()
}
override fun all(): List<T> = stack.asReversedFrozen()
}
class NodeStorage<T : FirElement, N : CFGNode<T>> : Stack<N>(){
@@ -65,6 +73,8 @@ class NodeStorage<T : FirElement, N : CFGNode<T>> : Stack<N>(){
stack.reset()
map.clear()
}
override fun all(): List<N> = stack.all()
}
class SymbolBasedNodeStorage<T, N : CFGNode<T>> : Stack<N>() where T : FirElement {
@@ -92,4 +102,6 @@ class SymbolBasedNodeStorage<T, N : CFGNode<T>> : Stack<N>() where T : FirElemen
stack.reset()
map.clear()
}
override fun all(): List<N> = stack.all()
}
@@ -1389,21 +1389,16 @@ class ControlFlowGraphBuilder {
addBackEdge(node, targetNode, label = label)
}
} else {
//TODO this supports single try-finally block only
//need to get all try-finally up to target
//go through all final nodes between node and target
val finallyNodes = finallyBefore(targetNode)
if (finallyNodes != null) {
val (finallyEnter, finallyExit) = finallyNodes
addEdge(node, finallyEnter, propagateDeadness = false, label = label)
if (!finallyExit.followingNodes.contains(targetNode)) {
addEdge(finallyExit, targetNode, propagateDeadness = false, label = label)
}
if (trackJump) {
//actually we can store all returns like this, but not sure if it make anything better
nonDirectJumps.put(targetNode, node)
}
} else {
addEdge(node, targetNode, propagateDeadness = false, label = label)
val finalFrom = finallyNodes.fold(node) { from, (finallyEnter, tryExit) ->
addEdgeIfNotExist(from, finallyEnter, propagateDeadness = false, label = label)
tryExit
}
addEdgeIfNotExist(finalFrom, targetNode, propagateDeadness = false, label = label)
if (trackJump && finallyNodes.isNotEmpty()) {
//actually we can store all returns like this, but not sure if it makes anything better
nonDirectJumps.put(targetNode, node)
}
}
}
@@ -1428,14 +1423,29 @@ class ControlFlowGraphBuilder {
lastNodes.push(stub)
}
private fun finallyBefore(target: CFGNode<*>): Pair<FinallyBlockEnterNode, TryExpressionExitNode>? {
return finallyEnterNodes.topOrNull()?.takeIf { it.level > target.level }?.let { it to tryExitNodes.top() }
private fun finallyBefore(target: CFGNode<*>): List<Pair<FinallyBlockEnterNode, TryExpressionExitNode>> {
return finallyEnterNodes.all().takeWhile { it.level > target.level }.map {
it to tryExitNodes[it.fir]!!
}
}
private fun popAndAddEdge(to: CFGNode<*>, preferredKind: EdgeKind = EdgeKind.Forward) {
addEdge(lastNodes.pop(), to, preferredKind = preferredKind)
}
private fun addEdgeIfNotExist(
from: CFGNode<*>,
to: CFGNode<*>,
propagateDeadness: Boolean = true,
isDead: Boolean = false,
preferredKind: EdgeKind = EdgeKind.Forward,
label: EdgeLabel = NormalPath
) {
if (!from.followingNodes.contains(to)) {
addEdge(from, to, propagateDeadness, isDead, preferredKind = preferredKind, label = label)
}
}
private fun addEdge(
from: CFGNode<*>,
to: CFGNode<*>,