[FIR] Add control flow graph and infrastructure for building it
For some of language constructions cfg is dummy (e.g. for finally blocks or anonymous initializers)
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.FirSymbolOwner
|
||||
|
||||
interface Stack<T> {
|
||||
val size: Int
|
||||
fun top(): T
|
||||
fun pop(): T
|
||||
fun push(value: T)
|
||||
}
|
||||
|
||||
fun <T> stackOf(vararg values: T): Stack<T> = StackImpl(*values, pushCallback = null, popCallback = null)
|
||||
fun <T> stackWithCallbacks(pushCallback: (T) -> Unit, popCallback: (T) -> Unit): Stack<T> =
|
||||
StackImpl(pushCallback = pushCallback, popCallback = popCallback)
|
||||
|
||||
val Stack<*>.isEmpty: Boolean get() = size == 0
|
||||
val Stack<*>.isNotEmpty: Boolean get() = size != 0
|
||||
fun <T> Stack<T>.topOrNull(): T? = if (size == 0) null else top()
|
||||
|
||||
private class StackImpl<T>(
|
||||
vararg values: T,
|
||||
private val pushCallback: ((T) -> Unit)?,
|
||||
private val popCallback: ((T) -> Unit)?
|
||||
) : Stack<T> {
|
||||
private val stack = mutableListOf(*values)
|
||||
|
||||
override fun top(): T = stack[stack.size - 1]
|
||||
override fun pop(): T = stack.removeAt(stack.size - 1).also { element ->
|
||||
popCallback?.let { it(element) }
|
||||
}
|
||||
|
||||
override fun push(value: T) {
|
||||
stack.add(value)
|
||||
pushCallback?.let { it(value) }
|
||||
}
|
||||
|
||||
override val size: Int get() = stack.size
|
||||
}
|
||||
|
||||
class NodeStorage<T : FirElement, N : CFGNode<T>>(
|
||||
pushCallback: ((N) -> Unit)? = null,
|
||||
popCallback: ((N) -> Unit)? = null
|
||||
) : Stack<N> {
|
||||
private val stack: Stack<N> = StackImpl(pushCallback = pushCallback, popCallback = popCallback)
|
||||
private val map: MutableMap<T, N> = mutableMapOf()
|
||||
|
||||
override val size: Int get() = stack.size
|
||||
|
||||
override fun top(): N = stack.top()
|
||||
|
||||
override fun pop(): N = stack.pop().also {
|
||||
map.remove(it.fir)
|
||||
}
|
||||
|
||||
override fun push(value: N) {
|
||||
stack.push(value)
|
||||
map[value.fir] = value
|
||||
}
|
||||
|
||||
operator fun get(key: T): N? {
|
||||
return map[key]
|
||||
}
|
||||
}
|
||||
|
||||
class SymbolBasedNodeStorage<T, N : CFGNode<T>> : Stack<N> where T : FirElement {
|
||||
private val stack: Stack<N> = stackOf()
|
||||
private val map: MutableMap<FirBasedSymbol<*>, N> = mutableMapOf()
|
||||
|
||||
override val size: Int get() = stack.size
|
||||
|
||||
override fun top(): N = stack.top()
|
||||
|
||||
override fun pop(): N = stack.pop().also {
|
||||
map.remove((it.fir as FirSymbolOwner<*>).symbol)
|
||||
}
|
||||
|
||||
override fun push(value: N) {
|
||||
stack.push(value)
|
||||
map[(value.fir as FirSymbolOwner<*>).symbol] = value
|
||||
}
|
||||
|
||||
operator fun get(key: FirBasedSymbol<*>): N? {
|
||||
return map[key]
|
||||
}
|
||||
}
|
||||
+159
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
|
||||
class ControlFlowGraph(val name: String) {
|
||||
val nodes = mutableListOf<CFGNode<*>>()
|
||||
lateinit var enterNode: CFGNode<*>
|
||||
lateinit var exitNode: CFGNode<*>
|
||||
}
|
||||
|
||||
sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level: Int) {
|
||||
init {
|
||||
owner.nodes += this
|
||||
}
|
||||
|
||||
val previousNodes = mutableListOf<CFGNode<*>>()
|
||||
val followingNodes = mutableListOf<CFGNode<*>>()
|
||||
|
||||
abstract val fir: E
|
||||
var isDead: Boolean = false
|
||||
}
|
||||
|
||||
val CFGNode<*>.usefulFollowingNodes: List<CFGNode<*>> get() = if (isDead) followingNodes else followingNodes.filterNot { it.isDead }
|
||||
val CFGNode<*>.usefulPreviousNodes: List<CFGNode<*>> get() = if (isDead) previousNodes else previousNodes.filterNot { it.isDead }
|
||||
|
||||
interface ReturnableNothingNode {
|
||||
val returnsNothing: Boolean
|
||||
}
|
||||
|
||||
// ----------------------------------- Named function -----------------------------------
|
||||
|
||||
class FunctionEnterNode(owner: ControlFlowGraph, override val fir: FirFunction<*>, level: Int) : CFGNode<FirFunction<*>>(owner, level)
|
||||
class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction<*>, level: Int) : CFGNode<FirFunction<*>>(owner, level)
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
class PropertyEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level)
|
||||
class PropertyExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level)
|
||||
|
||||
// ----------------------------------- Init -----------------------------------
|
||||
|
||||
class InitBlockEnterNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int) : CFGNode<FirAnonymousInitializer>(owner, level)
|
||||
class InitBlockExitNode(owner: ControlFlowGraph, override val fir: FirAnonymousInitializer, level: Int) : CFGNode<FirAnonymousInitializer>(owner, level)
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
class BlockEnterNode(owner: ControlFlowGraph, override val fir: FirBlock, level: Int) : CFGNode<FirBlock>(owner, level)
|
||||
class BlockExitNode(owner: ControlFlowGraph, override val fir: FirBlock, level: Int) : CFGNode<FirBlock>(owner, level)
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
class WhenEnterNode(owner: ControlFlowGraph, override val fir: FirWhenExpression, level: Int) : CFGNode<FirWhenExpression>(owner, level)
|
||||
class WhenExitNode(owner: ControlFlowGraph, override val fir: FirWhenExpression, level: Int) : CFGNode<FirWhenExpression>(owner, level)
|
||||
class WhenBranchConditionEnterNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int) : CFGNode<FirWhenBranch>(owner, level)
|
||||
class WhenBranchConditionExitNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int) : CFGNode<FirWhenBranch>(owner, level)
|
||||
class WhenBranchResultExitNode(owner: ControlFlowGraph, override val fir: FirWhenBranch, level: Int) : CFGNode<FirWhenBranch>(owner, level)
|
||||
|
||||
// ----------------------------------- Loop -----------------------------------
|
||||
|
||||
class LoopEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode<FirLoop>(owner, level)
|
||||
class LoopBlockEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode<FirLoop>(owner, level)
|
||||
class LoopBlockExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode<FirLoop>(owner, level)
|
||||
class LoopConditionEnterNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode<FirLoop>(owner, level)
|
||||
class LoopConditionExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode<FirLoop>(owner, level)
|
||||
class LoopExitNode(owner: ControlFlowGraph, override val fir: FirLoop, level: Int) : CFGNode<FirLoop>(owner, level)
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
class TryExpressionEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class TryMainBlockEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class TryMainBlockExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class CatchClauseEnterNode(owner: ControlFlowGraph, override val fir: FirCatch, level: Int) : CFGNode<FirCatch>(owner, level)
|
||||
class CatchClauseExitNode(owner: ControlFlowGraph, override val fir: FirCatch, level: Int) : CFGNode<FirCatch>(owner, level)
|
||||
class FinallyBlockEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class FinallyBlockExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class FinallyProxyEnterNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class FinallyProxyExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
class TryExpressionExitNode(owner: ControlFlowGraph, override val fir: FirTryExpression, level: Int) : CFGNode<FirTryExpression>(owner, level)
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
abstract class AbstractBinaryExitNode<T : FirElement>(owner: ControlFlowGraph, level: Int) : CFGNode<T>(owner, level) {
|
||||
val leftOperandNode: CFGNode<*> get() = previousNodes[0]
|
||||
val rightOperandNode: CFGNode<*> get() = previousNodes[1]
|
||||
}
|
||||
|
||||
class BinaryAndEnterNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int) : CFGNode<FirBinaryLogicExpression>(owner, level)
|
||||
class BinaryAndExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int) : AbstractBinaryExitNode<FirBinaryLogicExpression>(owner, level)
|
||||
class BinaryOrEnterNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int) : CFGNode<FirBinaryLogicExpression>(owner, level)
|
||||
|
||||
// Exit left node pass after left argument in case if that argument is `false`
|
||||
class BinaryOrExitLeftOperandNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int) : CFGNode<FirBinaryLogicExpression>(owner, level)
|
||||
class BinaryOrExitNode(owner: ControlFlowGraph, override val fir: FirBinaryLogicExpression, level: Int) : AbstractBinaryExitNode<FirBinaryLogicExpression>(owner, level)
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
class TypeOperatorCallNode(owner: ControlFlowGraph, override val fir: FirTypeOperatorCall, level: Int) : CFGNode<FirTypeOperatorCall>(owner, level)
|
||||
class OperatorCallNode(owner: ControlFlowGraph, override val fir: FirOperatorCall, level: Int) : AbstractBinaryExitNode<FirOperatorCall>(owner, level)
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
class JumpNode(owner: ControlFlowGraph, override val fir: FirJump<*>, level: Int) : CFGNode<FirJump<*>>(owner, level)
|
||||
class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExpression<*>, level: Int) : CFGNode<FirConstExpression<*>>(owner, level)
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
class QualifiedAccessNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirQualifiedAccessExpression,
|
||||
override val returnsNothing: Boolean,
|
||||
level: Int
|
||||
) : CFGNode<FirQualifiedAccessExpression>(owner, level), ReturnableNothingNode
|
||||
|
||||
class FunctionCallNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirFunctionCall,
|
||||
override val returnsNothing: Boolean,
|
||||
level: Int
|
||||
) : CFGNode<FirFunctionCall>(owner, level), ReturnableNothingNode
|
||||
|
||||
class ThrowExceptionNode(
|
||||
owner: ControlFlowGraph,
|
||||
override val fir: FirThrowExpression,
|
||||
level: Int
|
||||
) : CFGNode<FirThrowExpression>(owner, level), ReturnableNothingNode {
|
||||
override val returnsNothing: Boolean get() = true
|
||||
}
|
||||
|
||||
class StubNode(owner: ControlFlowGraph, level: Int) : CFGNode<FirStub>(owner, level) {
|
||||
init {
|
||||
isDead = true
|
||||
}
|
||||
|
||||
override val fir: FirStub get() = FirStub
|
||||
}
|
||||
|
||||
class VariableDeclarationNode(owner: ControlFlowGraph, override val fir: FirVariable<*>, level: Int) : CFGNode<FirVariable<*>>(owner, level)
|
||||
class VariableAssignmentNode(owner: ControlFlowGraph, override val fir: FirVariableAssignment, level: Int) : CFGNode<FirVariableAssignment>(owner, level)
|
||||
|
||||
// ----------------------------------- Other -----------------------------------
|
||||
|
||||
class AnnotationEnterNode(owner: ControlFlowGraph, override val fir: FirAnnotationCall, level: Int) : CFGNode<FirAnnotationCall>(owner, level)
|
||||
class AnnotationExitNode(owner: ControlFlowGraph, override val fir: FirAnnotationCall, level: Int) : CFGNode<FirAnnotationCall>(owner, level)
|
||||
|
||||
// ----------------------------------- Stub -----------------------------------
|
||||
|
||||
object FirStub : FirElement {
|
||||
override val psi: PsiElement? get() = null
|
||||
}
|
||||
+507
@@ -0,0 +1,507 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirElement
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirAbstractPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.*
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.resultType
|
||||
import org.jetbrains.kotlin.fir.types.isNothing
|
||||
|
||||
class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
|
||||
private val graphs: Stack<ControlFlowGraph> = stackOf(ControlFlowGraph("<DUMP_GRAPH_FOR_ENUMS>"))
|
||||
override val graph: ControlFlowGraph get() = graphs.top()
|
||||
|
||||
private val lexicalScopes: Stack<Stack<CFGNode<*>>> = stackOf(stackOf())
|
||||
private val lastNodes: Stack<CFGNode<*>> get() = lexicalScopes.top()
|
||||
val lastNode: CFGNode<*> get() = lastNodes.top()
|
||||
|
||||
private val exitNodes: Stack<CFGNode<*>> = stackOf()
|
||||
|
||||
private val functionExitNodes: NodeStorage<FirFunction<*>, FunctionExitNode> = NodeStorage(
|
||||
pushCallback = { exitNodes.push(it) },
|
||||
popCallback = { exitNodes.pop() }
|
||||
)
|
||||
|
||||
private val whenExitNodes: NodeStorage<FirWhenExpression, WhenExitNode> = NodeStorage()
|
||||
|
||||
private val loopEnterNodes: NodeStorage<FirElement, CFGNode<FirElement>> = NodeStorage()
|
||||
private val loopExitNodes: NodeStorage<FirLoop, LoopExitNode> = NodeStorage()
|
||||
|
||||
private val tryExitNodes: NodeStorage<FirTryExpression, TryExpressionExitNode> = NodeStorage()
|
||||
private val catchNodeStorages: Stack<NodeStorage<FirCatch, CatchClauseEnterNode>> = stackOf()
|
||||
private val catchNodeStorage: NodeStorage<FirCatch, CatchClauseEnterNode> get() = catchNodeStorages.top()
|
||||
|
||||
private val binaryAndExitNodes: Stack<BinaryAndExitNode> = stackOf()
|
||||
private val binaryOrExitNodes: Stack<BinaryOrExitNode> = stackOf()
|
||||
|
||||
private val topLevelVariableExitNodes: Stack<PropertyExitNode> = stackWithCallbacks(
|
||||
pushCallback = { exitNodes.push(it) },
|
||||
popCallback = { exitNodes.pop() }
|
||||
)
|
||||
|
||||
private val initBlockExitNodes: Stack<InitBlockExitNode> = stackWithCallbacks(
|
||||
pushCallback = { exitNodes.push(it) },
|
||||
popCallback = { exitNodes.pop() }
|
||||
)
|
||||
|
||||
override var levelCounter: Int = 0
|
||||
|
||||
fun isTopLevel(): Boolean = graphs.size == 1
|
||||
|
||||
// ----------------------------------- Named function -----------------------------------
|
||||
|
||||
fun enterFunction(function: FirFunction<*>): FunctionEnterNode {
|
||||
val name = when (function) {
|
||||
is FirNamedFunction -> function.name.asString()
|
||||
is FirAbstractPropertyAccessor -> if (function.isGetter) "<getter>" else "<setter>"
|
||||
is FirAnonymousFunction -> "<anonymous>" // TODO: add check to lambda or fun
|
||||
is FirConstructor -> function.name.asString()
|
||||
else -> throw IllegalArgumentException("Unknown function: ${function.render()}")
|
||||
}
|
||||
graphs.push(ControlFlowGraph(name))
|
||||
functionExitNodes.push(createFunctionExitNode(function))
|
||||
lexicalScopes.push(stackOf())
|
||||
return createFunctionEnterNode(function).also { lastNodes.push(it) }.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitFunction(function: FirFunction<*>): Pair<FunctionExitNode, ControlFlowGraph> {
|
||||
levelCounter--
|
||||
val exitNode = functionExitNodes.pop()
|
||||
addEdge(lastNodes.pop(), exitNode)
|
||||
lexicalScopes.pop()
|
||||
return exitNode to graphs.pop()
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
fun enterBlock(block: FirBlock): BlockEnterNode {
|
||||
return createBlockEnterNode(block).also { addNewSimpleNode(it) }.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitBlock(block: FirBlock): BlockExitNode {
|
||||
levelCounter--
|
||||
return createBlockExitNode(block).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
fun enterProperty(property: FirProperty): PropertyEnterNode {
|
||||
graphs.push(ControlFlowGraph("val ${property.name}"))
|
||||
val enterNode = createPropertyEnterNode(property)
|
||||
val exitNode = createPropertyExitNode(property)
|
||||
topLevelVariableExitNodes.push(exitNode)
|
||||
lexicalScopes.push(stackOf(enterNode))
|
||||
graph.enterNode = enterNode
|
||||
graph.exitNode = exitNode
|
||||
levelCounter++
|
||||
return enterNode
|
||||
}
|
||||
|
||||
fun exitProperty(property: FirProperty): Pair<PropertyExitNode, ControlFlowGraph> {
|
||||
val topLevelVariableExitNode = topLevelVariableExitNodes.pop()
|
||||
addNewSimpleNode(topLevelVariableExitNode)
|
||||
levelCounter--
|
||||
lexicalScopes.pop()
|
||||
return topLevelVariableExitNode to graphs.pop()
|
||||
}
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall): TypeOperatorCallNode {
|
||||
return createTypeOperatorCallNode(typeOperatorCall).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
fun exitOperatorCall(operatorCall: FirOperatorCall): OperatorCallNode {
|
||||
return createOperatorCallNode(operatorCall).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
fun exitJump(jump: FirJump<*>): JumpNode {
|
||||
val node = createJumpNode(jump)
|
||||
val nextNode = when (jump) {
|
||||
is FirReturnExpression -> functionExitNodes[jump.target.labeledElement]
|
||||
is FirContinueExpression -> loopEnterNodes[jump.target.labeledElement]
|
||||
is FirBreakExpression -> loopExitNodes[jump.target.labeledElement]
|
||||
else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}")
|
||||
}
|
||||
|
||||
addNodeWithJump(node, nextNode)
|
||||
return node
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
fun enterWhenExpression(whenExpression: FirWhenExpression): WhenEnterNode {
|
||||
val node = createWhenEnterNode(whenExpression)
|
||||
addNewSimpleNode(node)
|
||||
whenExitNodes.push(createWhenExitNode(whenExpression))
|
||||
levelCounter++
|
||||
return node
|
||||
}
|
||||
|
||||
fun enterWhenBranchCondition(whenBranch: FirWhenBranch): WhenBranchConditionEnterNode {
|
||||
return createWhenBranchConditionEnterNode(whenBranch).also { addNewSimpleNode(it) }.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitWhenBranchCondition(whenBranch: FirWhenBranch): WhenBranchConditionExitNode {
|
||||
levelCounter--
|
||||
return createWhenBranchConditionExitNode(whenBranch).also {
|
||||
addNewSimpleNode(it)
|
||||
// put exit branch condition node twice so we can refer it after exit from when expression
|
||||
lastNodes.push(it)
|
||||
}.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitWhenBranchResult(whenBranch: FirWhenBranch): WhenBranchResultExitNode {
|
||||
levelCounter--
|
||||
val node = createWhenBranchResultExitNode(whenBranch)
|
||||
addEdge(lastNodes.pop(), node)
|
||||
val whenExitNode = whenExitNodes.top()
|
||||
addEdge(node, whenExitNode, propagateDeadness = false)
|
||||
return node
|
||||
}
|
||||
|
||||
fun exitWhenExpression(whenExpression: FirWhenExpression): WhenExitNode {
|
||||
levelCounter--
|
||||
// exit from last condition node still on stack
|
||||
// we should remove it
|
||||
require(lastNodes.pop() is WhenBranchConditionExitNode)
|
||||
val whenExitNode = whenExitNodes.pop()
|
||||
lastNodes.push(whenExitNode)
|
||||
return whenExitNode
|
||||
}
|
||||
|
||||
// ----------------------------------- While Loop -----------------------------------
|
||||
|
||||
fun enterWhileLoop(loop: FirLoop): LoopConditionEnterNode {
|
||||
addNewSimpleNode(createLoopEnterNode(loop))
|
||||
loopExitNodes.push(createLoopExitNode(loop))
|
||||
levelCounter++
|
||||
val node = createLoopConditionEnterNode(loop)
|
||||
levelCounter++
|
||||
addNewSimpleNode(node)
|
||||
// put conditional node twice so we can refer it after exit from loop block
|
||||
lastNodes.push(node)
|
||||
loopEnterNodes.push(node)
|
||||
return node
|
||||
}
|
||||
|
||||
fun exitWhileLoopCondition(loop: FirLoop): LoopConditionExitNode {
|
||||
levelCounter--
|
||||
val conditionExitNode = createLoopConditionExitNode(loop)
|
||||
addNewSimpleNode(conditionExitNode)
|
||||
// TODO: here we can check that condition is always true
|
||||
addEdge(conditionExitNode, loopExitNodes.top())
|
||||
addNewSimpleNode(createLoopBlockEnterNode(loop))
|
||||
levelCounter++
|
||||
return conditionExitNode
|
||||
}
|
||||
|
||||
fun exitWhileLoop(loop: FirLoop): Pair<LoopBlockExitNode, LoopExitNode> {
|
||||
loopEnterNodes.pop()
|
||||
levelCounter--
|
||||
val loopBlockExitNode = createLoopBlockExitNode(loop)
|
||||
addEdge(lastNodes.pop(), loopBlockExitNode)
|
||||
if (lastNodes.isNotEmpty) {
|
||||
val conditionEnterNode = lastNodes.pop()
|
||||
require(conditionEnterNode is LoopConditionEnterNode) { loop.render() }
|
||||
addEdge(loopBlockExitNode, conditionEnterNode, propagateDeadness = false)
|
||||
}
|
||||
val loopExitNode = loopExitNodes.pop()
|
||||
lastNodes.push(loopExitNode)
|
||||
levelCounter--
|
||||
return loopBlockExitNode to loopExitNode
|
||||
}
|
||||
|
||||
// ----------------------------------- Do while Loop -----------------------------------
|
||||
|
||||
fun enterDoWhileLoop(loop: FirLoop): LoopBlockEnterNode {
|
||||
addNewSimpleNode(createLoopEnterNode(loop))
|
||||
loopExitNodes.push(createLoopExitNode(loop))
|
||||
levelCounter++
|
||||
val blockEnterNode = createLoopBlockEnterNode(loop)
|
||||
addNewSimpleNode(blockEnterNode)
|
||||
// put block enter node twice so we can refer it after exit from loop condition
|
||||
lastNodes.push(blockEnterNode)
|
||||
loopEnterNodes.push(blockEnterNode)
|
||||
levelCounter++
|
||||
return blockEnterNode
|
||||
}
|
||||
|
||||
fun enterDoWhileLoopCondition(loop: FirLoop): Pair<LoopBlockExitNode, LoopConditionEnterNode> {
|
||||
levelCounter--
|
||||
val blockExitNode = createLoopBlockExitNode(loop).also { addNewSimpleNode(it) }
|
||||
val conditionEnterNode = createLoopConditionEnterNode(loop).also { addNewSimpleNode(it) }
|
||||
levelCounter++
|
||||
return blockExitNode to conditionEnterNode
|
||||
}
|
||||
|
||||
fun exitDoWhileLoop(loop: FirLoop): LoopExitNode {
|
||||
loopEnterNodes.pop()
|
||||
levelCounter--
|
||||
val conditionExitNode = createLoopConditionExitNode(loop)
|
||||
// TODO: here we can check that condition is always false
|
||||
addEdge(lastNodes.pop(), conditionExitNode)
|
||||
val blockEnterNode = lastNodes.pop()
|
||||
require(blockEnterNode is LoopBlockEnterNode)
|
||||
addEdge(conditionExitNode, blockEnterNode, propagateDeadness = false)
|
||||
val loopExit = loopExitNodes.pop()
|
||||
addEdge(conditionExitNode, loopExit)
|
||||
lastNodes.push(loopExit)
|
||||
levelCounter--
|
||||
return loopExit
|
||||
}
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression): BinaryAndEnterNode {
|
||||
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.AND)
|
||||
binaryAndExitNodes.push(createBinaryAndExitNode(binaryLogicExpression))
|
||||
return createBinaryAndEnterNode(binaryLogicExpression).also { addNewSimpleNode(it) }.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.AND)
|
||||
addEdge(lastNode, binaryAndExitNodes.top())
|
||||
}
|
||||
|
||||
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression): BinaryAndExitNode {
|
||||
levelCounter--
|
||||
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.AND)
|
||||
return binaryAndExitNodes.pop().also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression): BinaryOrEnterNode {
|
||||
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.OR)
|
||||
binaryOrExitNodes.push(createBinaryOrExitNode(binaryLogicExpression))
|
||||
return createBinaryOrEnterNode(binaryLogicExpression).also {
|
||||
addNewSimpleNode(it)
|
||||
// put or enter node twice so we can refer it after exit from left argument
|
||||
lastNodes.push(it)
|
||||
}.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression): BinaryOrExitLeftOperandNode {
|
||||
levelCounter--
|
||||
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.OR)
|
||||
val previousNode = lastNodes.pop()
|
||||
addEdge(previousNode, binaryOrExitNodes.top())
|
||||
return createBinaryOrExitLeftOperandNode(binaryLogicExpression).also {
|
||||
addEdge(previousNode, it)
|
||||
lastNodes.push(it)
|
||||
levelCounter++
|
||||
}
|
||||
}
|
||||
|
||||
fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression): BinaryOrExitNode {
|
||||
assert(binaryLogicExpression.kind == FirBinaryLogicExpression.OperationKind.OR)
|
||||
levelCounter--
|
||||
return binaryOrExitNodes.pop().also {
|
||||
addEdge(lastNodes.pop(), it)
|
||||
addEdge(lastNodes.pop(), it)
|
||||
lastNodes.push(it)
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
private val finallyEnterNodes: Stack<FinallyBlockEnterNode> = stackOf()
|
||||
|
||||
fun enterTryExpression(tryExpression: FirTryExpression): TryMainBlockEnterNode {
|
||||
catchNodeStorages.push(NodeStorage())
|
||||
addNewSimpleNode(createTryExpressionEnterNode(tryExpression))
|
||||
tryExitNodes.push(createTryExpressionExitNode(tryExpression))
|
||||
levelCounter++
|
||||
val tryNode = createTryMainBlockEnterNode(tryExpression)
|
||||
addNewSimpleNode(tryNode)
|
||||
addEdge(tryNode, exitNodes.top())
|
||||
|
||||
for (catch in tryExpression.catches) {
|
||||
val catchNode = createCatchClauseEnterNode(catch)
|
||||
catchNodeStorage.push(catchNode)
|
||||
addEdge(tryNode, catchNode)
|
||||
addEdge(catchNode, exitNodes.top())
|
||||
}
|
||||
levelCounter++
|
||||
|
||||
if (tryExpression.finallyBlock != null) {
|
||||
val finallyEnterNode = createFinallyBlockEnterNode(tryExpression)
|
||||
addEdge(tryNode, finallyEnterNode)
|
||||
finallyEnterNodes.push(finallyEnterNode)
|
||||
}
|
||||
|
||||
return tryNode
|
||||
}
|
||||
|
||||
fun exitTryMainBlock(tryExpression: FirTryExpression): TryMainBlockExitNode {
|
||||
levelCounter--
|
||||
val node = createTryMainBlockExitNode(tryExpression)
|
||||
addEdge(lastNodes.pop(), node)
|
||||
addEdge(node, tryExitNodes.top())
|
||||
return node
|
||||
}
|
||||
|
||||
fun enterCatchClause(catch: FirCatch): CatchClauseEnterNode {
|
||||
return catchNodeStorage[catch]!!.also { lastNodes.push(it) }.also { levelCounter++ }
|
||||
}
|
||||
|
||||
fun exitCatchClause(catch: FirCatch): CatchClauseExitNode {
|
||||
levelCounter--
|
||||
return createCatchClauseExitNode(catch).also {
|
||||
addEdge(lastNodes.pop(), it)
|
||||
addEdge(it, tryExitNodes.top(), propagateDeadness = false)
|
||||
}
|
||||
}
|
||||
|
||||
fun enterFinallyBlock(tryExpression: FirTryExpression): FinallyBlockEnterNode {
|
||||
val enterNode = finallyEnterNodes.pop()
|
||||
lastNodes.push(enterNode)
|
||||
return enterNode
|
||||
}
|
||||
|
||||
fun exitFinallyBlock(tryExpression: FirTryExpression): FinallyBlockExitNode {
|
||||
return createFinallyBlockExitNode(tryExpression).also {
|
||||
addEdge(lastNodes.pop(), it)
|
||||
addEdge(it, tryExitNodes.top())
|
||||
}
|
||||
}
|
||||
|
||||
fun exitTryExpression(tryExpression: FirTryExpression): TryExpressionExitNode {
|
||||
levelCounter--
|
||||
catchNodeStorages.pop()
|
||||
val node = tryExitNodes.pop()
|
||||
node.markAsDeadIfNecessary()
|
||||
lastNodes.push(node)
|
||||
return node
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode {
|
||||
val returnsNothing = qualifiedAccessExpression.resultType.isNothing
|
||||
val node = createQualifiedAccessNode(qualifiedAccessExpression, returnsNothing)
|
||||
if (returnsNothing) {
|
||||
addNodeThatReturnsNothing(node)
|
||||
} else {
|
||||
addNewSimpleNode(node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
fun exitFunctionCall(functionCall: FirFunctionCall): FunctionCallNode {
|
||||
val returnsNothing = functionCall.resultType.isNothing
|
||||
val node = createFunctionCallNode(functionCall, returnsNothing)
|
||||
if (returnsNothing) {
|
||||
addNodeThatReturnsNothing(node)
|
||||
} else {
|
||||
addNewSimpleNode(node)
|
||||
}
|
||||
return node
|
||||
}
|
||||
|
||||
fun exitConstExpresion(constExpression: FirConstExpression<*>): ConstExpressionNode {
|
||||
return createConstExpressionNode(constExpression).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
fun exitVariableDeclaration(variable: FirVariable<*>): VariableDeclarationNode {
|
||||
return createVariableDeclarationNode(variable).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
fun exitVariableAssignment(assignment: FirVariableAssignment): VariableAssignmentNode {
|
||||
return createVariableAssignmentNode(assignment).also { addNewSimpleNode(it) }
|
||||
}
|
||||
|
||||
fun exitThrowExceptionNode(throwExpression: FirThrowExpression): ThrowExceptionNode {
|
||||
return createThrowExceptionNode(throwExpression).also { addNodeThatReturnsNothing(it) }
|
||||
}
|
||||
|
||||
// ----------------------------------- Annotations -----------------------------------
|
||||
|
||||
fun enterAnnotationCall(annotationCall: FirAnnotationCall): AnnotationEnterNode {
|
||||
return createAnnotationEnterNode(annotationCall).also {
|
||||
if (graphs.size > 1) {
|
||||
addNewSimpleNode(it)
|
||||
} else {
|
||||
lastNodes.push(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun exitAnnotationCall(annotationCall: FirAnnotationCall): AnnotationExitNode {
|
||||
return createAnnotationExitNode(annotationCall).also {
|
||||
if (graphs.size > 1) {
|
||||
addNewSimpleNode(it)
|
||||
} else {
|
||||
lastNodes.pop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
fun enterInitBlock(initBlock: FirAnonymousInitializer): InitBlockEnterNode {
|
||||
val enterNode = createInitBlockEnterNode(initBlock).also {
|
||||
lexicalScopes.push(stackOf(it))
|
||||
}
|
||||
val exitNode = createInitBlockExitNode(initBlock)
|
||||
initBlockExitNodes.push(exitNode)
|
||||
levelCounter++
|
||||
return enterNode
|
||||
}
|
||||
|
||||
fun exitInitBlock(initBlock: FirAnonymousInitializer): InitBlockExitNode {
|
||||
levelCounter--
|
||||
return initBlockExitNodes.pop().also {
|
||||
addNewSimpleNode(it)
|
||||
lexicalScopes.pop()
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
private fun CFGNode<*>.markAsDeadIfNecessary() {
|
||||
isDead = previousNodes.all { it.isDead }
|
||||
}
|
||||
|
||||
private fun addNodeThatReturnsNothing(node: CFGNode<*>) {
|
||||
/*
|
||||
* `return` is temporary solution that is needed for init block
|
||||
* it will be replaced after correct implementation of CFG for class initialization
|
||||
*/
|
||||
val exitNode: CFGNode<*> = exitNodes.top()
|
||||
addNodeWithJump(node, exitNode)
|
||||
}
|
||||
|
||||
private fun addNodeWithJump(node: CFGNode<*>, targetNode: CFGNode<*>?) {
|
||||
addEdge(lastNodes.pop(), node)
|
||||
if (targetNode != null) {
|
||||
addEdge(node, targetNode)
|
||||
}
|
||||
val stub = createStubNode()
|
||||
addEdge(node, stub)
|
||||
lastNodes.push(stub)
|
||||
}
|
||||
|
||||
private fun addNewSimpleNode(newNode: CFGNode<*>): CFGNode<*> {
|
||||
val oldNode = lastNodes.pop()
|
||||
addEdge(oldNode, newNode)
|
||||
lastNodes.push(newNode)
|
||||
return oldNode
|
||||
}
|
||||
|
||||
private fun addEdge(from: CFGNode<*>, to: CFGNode<*>, propagateDeadness: Boolean = true) {
|
||||
if (propagateDeadness && from.isDead) {
|
||||
to.isDead = true
|
||||
}
|
||||
from.followingNodes += to
|
||||
to.previousNodes += from
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirProperty
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
|
||||
abstract class ControlFlowGraphNodeBuilder {
|
||||
protected abstract val graph: ControlFlowGraph
|
||||
protected abstract var levelCounter: Int
|
||||
|
||||
protected fun createStubNode(): StubNode = StubNode(
|
||||
graph,
|
||||
levelCounter
|
||||
)
|
||||
|
||||
protected fun createLoopExitNode(fir: FirLoop): LoopExitNode = LoopExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createLoopEnterNode(fir: FirLoop): LoopEnterNode = LoopEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createInitBlockExitNode(fir: FirAnonymousInitializer): InitBlockExitNode =
|
||||
InitBlockExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createInitBlockEnterNode(fir: FirAnonymousInitializer): InitBlockEnterNode =
|
||||
InitBlockEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createTypeOperatorCallNode(fir: FirTypeOperatorCall): TypeOperatorCallNode =
|
||||
TypeOperatorCallNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createOperatorCallNode(fir: FirOperatorCall): OperatorCallNode =
|
||||
OperatorCallNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createWhenBranchConditionExitNode(fir: FirWhenBranch): WhenBranchConditionExitNode =
|
||||
WhenBranchConditionExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createJumpNode(fir: FirJump<*>): JumpNode =
|
||||
JumpNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createQualifiedAccessNode(
|
||||
fir: FirQualifiedAccessExpression,
|
||||
returnsNothing: Boolean
|
||||
): QualifiedAccessNode = QualifiedAccessNode(graph, fir, returnsNothing, levelCounter)
|
||||
|
||||
protected fun createBlockEnterNode(fir: FirBlock): BlockEnterNode = BlockEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createBlockExitNode(fir: FirBlock): BlockExitNode = BlockExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createPropertyExitNode(fir: FirProperty): PropertyExitNode = PropertyExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createPropertyEnterNode(fir: FirProperty): PropertyEnterNode = PropertyEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createFunctionEnterNode(fir: FirFunction<*>): FunctionEnterNode =
|
||||
FunctionEnterNode(graph, fir, levelCounter).also {
|
||||
graph.enterNode = it
|
||||
}
|
||||
|
||||
protected fun createFunctionExitNode(fir: FirFunction<*>): FunctionExitNode = FunctionExitNode(graph, fir, levelCounter).also {
|
||||
graph.exitNode = it
|
||||
}
|
||||
|
||||
protected fun createBinaryOrEnterNode(fir: FirBinaryLogicExpression): BinaryOrEnterNode =
|
||||
BinaryOrEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createBinaryOrExitLeftOperandNode(fir: FirBinaryLogicExpression): BinaryOrExitLeftOperandNode =
|
||||
BinaryOrExitLeftOperandNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createBinaryOrExitNode(fir: FirBinaryLogicExpression): BinaryOrExitNode =
|
||||
BinaryOrExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createBinaryAndExitNode(fir: FirBinaryLogicExpression): BinaryAndExitNode =
|
||||
BinaryAndExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createBinaryAndEnterNode(fir: FirBinaryLogicExpression): BinaryAndEnterNode =
|
||||
BinaryAndEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createWhenBranchConditionEnterNode(fir: FirWhenBranch): WhenBranchConditionEnterNode =
|
||||
WhenBranchConditionEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createWhenEnterNode(fir: FirWhenExpression): WhenEnterNode =
|
||||
WhenEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createWhenExitNode(fir: FirWhenExpression): WhenExitNode =
|
||||
WhenExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createWhenBranchResultExitNode(fir: FirWhenBranch): WhenBranchResultExitNode =
|
||||
WhenBranchResultExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createLoopConditionExitNode(fir: FirLoop): LoopConditionExitNode =
|
||||
LoopConditionExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createLoopConditionEnterNode(fir: FirLoop): LoopConditionEnterNode =
|
||||
LoopConditionEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createLoopBlockEnterNode(fir: FirLoop): LoopBlockEnterNode =
|
||||
LoopBlockEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createLoopBlockExitNode(fir: FirLoop): LoopBlockExitNode =
|
||||
LoopBlockExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createFunctionCallNode(fir: FirFunctionCall, returnsNothing: Boolean): FunctionCallNode =
|
||||
FunctionCallNode(graph, fir, returnsNothing, levelCounter)
|
||||
|
||||
protected fun createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode =
|
||||
VariableAssignmentNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createAnnotationExitNode(fir: FirAnnotationCall): AnnotationExitNode =
|
||||
AnnotationExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createAnnotationEnterNode(fir: FirAnnotationCall): AnnotationEnterNode =
|
||||
AnnotationEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createVariableDeclarationNode(fir: FirVariable<*>): VariableDeclarationNode =
|
||||
VariableDeclarationNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createConstExpressionNode(fir: FirConstExpression<*>): ConstExpressionNode =
|
||||
ConstExpressionNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createThrowExceptionNode(fir: FirThrowExpression): ThrowExceptionNode =
|
||||
ThrowExceptionNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createFinallyProxyExitNode(fir: FirTryExpression): FinallyProxyExitNode =
|
||||
FinallyProxyExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createFinallyProxyEnterNode(fir: FirTryExpression): FinallyProxyEnterNode =
|
||||
FinallyProxyEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createFinallyBlockExitNode(fir: FirTryExpression): FinallyBlockExitNode =
|
||||
FinallyBlockExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createFinallyBlockEnterNode(fir: FirTryExpression): FinallyBlockEnterNode =
|
||||
FinallyBlockEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createCatchClauseExitNode(fir: FirCatch): CatchClauseExitNode =
|
||||
CatchClauseExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createTryMainBlockExitNode(fir: FirTryExpression): TryMainBlockExitNode =
|
||||
TryMainBlockExitNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createTryMainBlockEnterNode(fir: FirTryExpression): TryMainBlockEnterNode =
|
||||
TryMainBlockEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createCatchClauseEnterNode(fir: FirCatch): CatchClauseEnterNode =
|
||||
CatchClauseEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createTryExpressionEnterNode(fir: FirTryExpression): TryExpressionEnterNode =
|
||||
TryExpressionEnterNode(graph, fir, levelCounter)
|
||||
|
||||
protected fun createTryExpressionExitNode(fir: FirTryExpression): TryExpressionExitNode =
|
||||
TryExpressionExitNode(graph, fir, levelCounter)
|
||||
|
||||
}
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.fir.resolve.dfa.cfg
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirRenderer
|
||||
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.FirNamedFunction
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirAbstractPropertyAccessor
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirErrorFunction
|
||||
import org.jetbrains.kotlin.fir.expressions.FirDoWhileLoop
|
||||
import org.jetbrains.kotlin.fir.expressions.FirLoop
|
||||
import org.jetbrains.kotlin.fir.expressions.FirWhileLoop
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirElseIfTrueCondition
|
||||
import org.jetbrains.kotlin.fir.render
|
||||
import org.jetbrains.kotlin.utils.DFS
|
||||
|
||||
private const val INDENT = " "
|
||||
private const val DEAD = "[DEAD]"
|
||||
|
||||
fun ControlFlowGraph.renderToStringBuilder(builder: StringBuilder) {
|
||||
val sortedNodes: List<CFGNode<*>> = DFS.topologicalOrder(
|
||||
nodes
|
||||
) {
|
||||
val result = if (it !is WhenBranchConditionExitNode || it.followingNodes.size < 2) {
|
||||
it.followingNodes
|
||||
} else {
|
||||
it.followingNodes.sortedBy { node -> if (node is BlockEnterNode) 1 else 0 }
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
|
||||
val indices = sortedNodes.mapIndexed { i, node -> node to i }.toMap()
|
||||
val notVisited = sortedNodes.toMutableSet()
|
||||
val maxLineNumberSize = sortedNodes.size.toString().length
|
||||
|
||||
fun List<CFGNode<*>>.renderEdges(nodeIsDead: Boolean): String = map {
|
||||
indices.getValue(it) to it.isDead
|
||||
}.sortedBy { it.first }.joinToString(", ") { (index, isDead) ->
|
||||
index.toString() + if (isDead && !nodeIsDead) DEAD else ""
|
||||
}
|
||||
|
||||
fun StringBuilder.renderNode(node: CFGNode<*>, index: Int) {
|
||||
append(index.toString().padStart(maxLineNumberSize))
|
||||
append(": ")
|
||||
append(INDENT.repeat(node.level))
|
||||
append(node.render())
|
||||
append(" -> ")
|
||||
append(node.followingNodes.renderEdges(node.isDead))
|
||||
if (node.previousNodes.isNotEmpty()) {
|
||||
append(" | <- ")
|
||||
append(node.previousNodes.renderEdges(node.isDead))
|
||||
}
|
||||
appendln()
|
||||
}
|
||||
|
||||
with(builder) {
|
||||
sortedNodes.forEachIndexed { i, node ->
|
||||
notVisited.remove(node)
|
||||
renderNode(node, i)
|
||||
}
|
||||
|
||||
if (notVisited.isNotEmpty()) {
|
||||
appendln("Not visited nodes:")
|
||||
notVisited.forEach { node ->
|
||||
renderNode(node, indices.getValue(node))
|
||||
}
|
||||
}
|
||||
|
||||
appendln()
|
||||
}
|
||||
}
|
||||
|
||||
fun ControlFlowGraph.render(): String = buildString { renderToStringBuilder(this) }
|
||||
|
||||
fun CFGNode<*>.render(): String =
|
||||
buildString {
|
||||
append(
|
||||
when (this@render) {
|
||||
is FunctionEnterNode -> "Enter function \"${fir.name()}\""
|
||||
is FunctionExitNode -> "Exit function \"${fir.name()}\""
|
||||
|
||||
is BlockEnterNode -> "Enter block"
|
||||
is BlockExitNode -> "Exit block"
|
||||
|
||||
is WhenEnterNode -> "Enter when"
|
||||
is WhenBranchConditionEnterNode -> "Enter when branch condition ${if (fir.condition is FirElseIfTrueCondition) "\"else\"" else ""}"
|
||||
is WhenBranchConditionExitNode -> "Exit when branch condition"
|
||||
is WhenBranchResultExitNode -> "Exit when branch result"
|
||||
is WhenExitNode -> "Exit when"
|
||||
|
||||
is LoopEnterNode -> "Enter ${fir.type()} loop"
|
||||
is LoopBlockEnterNode -> "Enter loop block"
|
||||
is LoopBlockExitNode -> "Exit loop block"
|
||||
is LoopConditionEnterNode -> "Enter loop condition"
|
||||
is LoopConditionExitNode -> "Exit loop condition"
|
||||
is LoopExitNode -> "Exit ${fir.type()}loop"
|
||||
|
||||
is QualifiedAccessNode -> "Access variable ${fir.calleeReference.render()}"
|
||||
is OperatorCallNode -> "Operator ${fir.operation.operator}"
|
||||
is TypeOperatorCallNode -> "Type operator: \"${fir.psi?.text?.toString() ?: fir.render()}\""
|
||||
is JumpNode -> "Jump: ${fir.render()}"
|
||||
is StubNode -> "Stub"
|
||||
|
||||
is ConstExpressionNode -> "Const: ${fir.render()}"
|
||||
is VariableDeclarationNode ->
|
||||
"Variable declaration: ${buildString { FirRenderer(this).visitCallableDeclaration(fir)} }"
|
||||
|
||||
is VariableAssignmentNode -> "Assignmenet: ${fir.lValue.render()}"
|
||||
is FunctionCallNode -> "Function call: ${fir.render()}"
|
||||
is ThrowExceptionNode -> "Throw: ${fir.render()}"
|
||||
|
||||
is TryExpressionEnterNode -> "Try expression enter"
|
||||
is TryMainBlockEnterNode -> "Try main block enter"
|
||||
is TryMainBlockExitNode -> "Try main block exit"
|
||||
is CatchClauseEnterNode -> "Catch enter"
|
||||
is CatchClauseExitNode -> "Catch exit"
|
||||
is FinallyBlockEnterNode -> "Enter finally"
|
||||
is FinallyBlockExitNode -> "Exit finally"
|
||||
is FinallyProxyEnterNode -> TODO()
|
||||
is FinallyProxyExitNode -> TODO()
|
||||
is TryExpressionExitNode -> "Try expression exit"
|
||||
|
||||
is BinaryAndEnterNode -> "Enter &&"
|
||||
is BinaryAndExitNode -> "Exit &&"
|
||||
is BinaryOrEnterNode -> "Enter ||"
|
||||
is BinaryOrExitLeftOperandNode -> "Exit left part of ||"
|
||||
is BinaryOrExitNode -> "Exit ||"
|
||||
|
||||
is PropertyEnterNode -> "Enter property"
|
||||
is PropertyExitNode -> "Exit property"
|
||||
is InitBlockEnterNode -> "Enter init block"
|
||||
is InitBlockExitNode -> "Exit init block"
|
||||
is AnnotationEnterNode -> "Enter annotation"
|
||||
is AnnotationExitNode -> "Exit annotation"
|
||||
|
||||
else -> TODO(this@render.toString())
|
||||
}
|
||||
)
|
||||
if (isDead) {
|
||||
append(DEAD)
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirFunction<*>.name(): String = when (this) {
|
||||
is FirNamedFunction -> name.asString()
|
||||
is FirAnonymousFunction -> "anonymousFunction"
|
||||
is FirConstructor -> name.asString()
|
||||
is FirAbstractPropertyAccessor -> if (isGetter) "getter" else "setter"
|
||||
is FirErrorFunction -> "errorFunction"
|
||||
else -> TODO(toString())
|
||||
}
|
||||
|
||||
private fun FirLoop.type(): String = when (this) {
|
||||
is FirWhileLoop -> "while"
|
||||
is FirDoWhileLoop -> "do-while"
|
||||
else -> throw IllegalArgumentException()
|
||||
}
|
||||
Reference in New Issue
Block a user