[FIR] Hide modifications of CFG from public API

This commit is contained in:
Dmitriy Novozhilov
2020-01-29 12:57:39 +03:00
parent 6716cb0bf3
commit ec0f8a9c77
2 changed files with 46 additions and 27 deletions
@@ -3,6 +3,8 @@
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@file:Suppress("Reformat")
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
import org.jetbrains.kotlin.fir.FirElement
@@ -15,9 +17,18 @@ import org.jetbrains.kotlin.fir.visitors.FirTransformer
import org.jetbrains.kotlin.fir.visitors.FirVisitor
class ControlFlowGraph(val name: String, val kind: Kind) {
val nodes = mutableListOf<CFGNode<*>>()
private val _nodes: MutableList<CFGNode<*>> = mutableListOf()
val nodes: List<CFGNode<*>> get() = _nodes
internal fun addNode(node: CFGNode<*>) {
_nodes += node
}
lateinit var enterNode: CFGNode<*>
internal set
lateinit var exitNode: CFGNode<*>
internal set
enum class Kind {
Function, ClassInitializer, PropertyInitializer, TopLevel
@@ -30,12 +41,12 @@ enum class EdgeKind {
sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level: Int, private val id: Int) {
companion object {
fun addEdge(from: CFGNode<*>, to: CFGNode<*>, kind: EdgeKind, propagateDeadness: Boolean) {
from.followingNodes += to
to.previousNodes += from
internal fun addEdge(from: CFGNode<*>, to: CFGNode<*>, kind: EdgeKind, propagateDeadness: Boolean) {
from._followingNodes += to
to._previousNodes += from
if (kind != EdgeKind.Simple) {
from.outgoingEdges[to] = kind
to.incomingEdges[from] = kind
from._outgoingEdges[to] = kind
to._incomingEdges[from] = kind
}
if (propagateDeadness && kind == EdgeKind.Dead) {
to.isDead = true
@@ -44,19 +55,29 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
}
init {
owner.nodes += this
@Suppress("LeakingThis")
owner.addNode(this)
}
val previousNodes: MutableList<CFGNode<*>> = mutableListOf()
val followingNodes: MutableList<CFGNode<*>> = mutableListOf()
private val _previousNodes: MutableList<CFGNode<*>> = mutableListOf()
private val _followingNodes: MutableList<CFGNode<*>> = mutableListOf()
val incomingEdges = mutableMapOf<CFGNode<*>, EdgeKind>().withDefault { EdgeKind.Simple }
val outgoingEdges = mutableMapOf<CFGNode<*>, EdgeKind>().withDefault { EdgeKind.Simple }
val previousNodes: List<CFGNode<*>> get() = _previousNodes
val followingNodes: List<CFGNode<*>> get() = _followingNodes
val firstPreviousNode: CFGNode<*> get() = previousNodes.first()
private val _incomingEdges = mutableMapOf<CFGNode<*>, EdgeKind>().withDefault { EdgeKind.Simple }
private val _outgoingEdges = mutableMapOf<CFGNode<*>, EdgeKind>().withDefault { EdgeKind.Simple }
val incomingEdges: Map<CFGNode<*>, EdgeKind> get() = _incomingEdges
val outgoingEdges: Map<CFGNode<*>, EdgeKind> get() = _outgoingEdges
abstract val fir: E
var isDead: Boolean = false
protected set
internal fun updateDeadStatus() {
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all { it == EdgeKind.Dead }
}
final override fun equals(other: Any?): Boolean {
if (other !is CFGNode<*>) return false
@@ -68,6 +89,8 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
}
}
val CFGNode<*>.firstPreviousNode: CFGNode<*> get() = previousNodes.first()
interface EnterNode
interface ExitNode
@@ -131,7 +131,7 @@ class ControlFlowGraphBuilder {
}
lexicalScopes.pop()
}
exitNode.markAsDeadIfNecessary()
exitNode.updateDeadStatus()
val graph = if (!isInplace) {
graphs.pop()
} else {
@@ -175,7 +175,7 @@ class ControlFlowGraphBuilder {
functionExitNodes.top().also {
addEdge(lastNodes.pop(), it)
lastNodes.push(it)
it.markAsDeadIfNecessary()
it.updateDeadStatus()
}
} else {
levelCounter--
@@ -201,7 +201,7 @@ class ControlFlowGraphBuilder {
fun exitProperty(property: FirProperty): Pair<PropertyInitializerExitNode, ControlFlowGraph> {
val topLevelVariableExitNode = topLevelVariableInitializerExitNodes.pop().also {
addNewSimpleNode(it)
it.markAsDeadIfNecessary()
it.updateDeadStatus()
}
levelCounter--
exitNodes.pop()
@@ -285,7 +285,7 @@ class ControlFlowGraphBuilder {
addEdge(this, whenExitNode)
}
} else null
whenExitNode.markAsDeadIfNecessary()
whenExitNode.updateDeadStatus()
lastNodes.push(whenExitNode)
levelCounter--
return whenExitNode to syntheticElseBranchNode
@@ -332,7 +332,7 @@ class ControlFlowGraphBuilder {
addEdge(loopBlockExitNode, conditionEnterNode, propagateDeadness = false)
}
val loopExitNode = loopExitNodes.pop()
loopExitNode.markAsDeadIfNecessary()
loopExitNode.updateDeadStatus()
lastNodes.push(loopExitNode)
levelCounter--
return loopBlockExitNode to loopExitNode
@@ -373,7 +373,7 @@ class ControlFlowGraphBuilder {
addEdge(conditionExitNode, blockEnterNode, propagateDeadness = false, isDead = conditionBooleanValue == false)
val loopExit = loopExitNodes.pop()
addEdge(conditionExitNode, loopExit, propagateDeadness = false, isDead = conditionBooleanValue == true)
loopExit.markAsDeadIfNecessary()
loopExit.updateDeadStatus()
lastNodes.push(loopExit)
levelCounter--
return conditionExitNode to loopExit
@@ -410,7 +410,7 @@ class ControlFlowGraphBuilder {
return binaryAndExitNodes.pop().also {
val rightNode = lastNodes.pop()
addEdge(rightNode, it, propagateDeadness = false, isDead = it.leftOperandNode.booleanConstValue == false)
it.markAsDeadIfNecessary()
it.updateDeadStatus()
lastNodes.push(it)
}
}
@@ -456,7 +456,7 @@ class ControlFlowGraphBuilder {
return binaryOrExitNodes.pop().also {
val rightNode = lastNodes.pop()
addEdge(rightNode, it, propagateDeadness = false)
it.markAsDeadIfNecessary()
it.updateDeadStatus()
lastNodes.push(it)
}
}
@@ -531,7 +531,7 @@ class ControlFlowGraphBuilder {
levelCounter--
catchNodeStorages.pop()
val node = tryExitNodes.pop()
node.markAsDeadIfNecessary()
node.updateDeadStatus()
lastNodes.push(node)
return node
}
@@ -616,7 +616,7 @@ class ControlFlowGraphBuilder {
levelCounter--
return initBlockExitNodes.pop().also {
addNewSimpleNode(it)
it.markAsDeadIfNecessary()
it.updateDeadStatus()
lexicalScopes.pop()
exitNodes.pop()
graphs.pop()
@@ -639,16 +639,12 @@ class ControlFlowGraphBuilder {
fun exitSafeCall(qualifiedAccess: FirQualifiedAccess): ExitSafeCallNode {
return exitSafeCallNodes.pop().also {
addNewSimpleNode(it)
it.markAsDeadIfNecessary()
it.updateDeadStatus()
}
}
// -------------------------------------------------------------------------------------------------------------------------
private fun CFGNode<*>.markAsDeadIfNecessary() {
isDead = incomingEdges.size == previousNodes.size && incomingEdges.values.all { it == EdgeKind.Dead }
}
private fun addNodeThatReturnsNothing(node: CFGNode<*>) {
/*
* `return` is temporary solution that is needed for init block