[FIR] Get rid of alivePreviousNodes in control flow graph

This commit is contained in:
Dmitriy Novozhilov
2020-01-28 16:20:06 +03:00
parent 1180ec54ef
commit 237198758d
3 changed files with 19 additions and 13 deletions
@@ -366,7 +366,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression) val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
if (syntheticElseNode != null) { if (syntheticElseNode != null) {
syntheticElseNode.mergeIncomingFlow() syntheticElseNode.mergeIncomingFlow()
val previousConditionExitNode = syntheticElseNode.previousNodes.single() as? WhenBranchConditionExitNode val previousConditionExitNode = syntheticElseNode.firstPreviousNode as? WhenBranchConditionExitNode
// previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches // previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches
// in that case there will be when enter or subject access node // in that case there will be when enter or subject access node
if (previousConditionExitNode != null) { if (previousConditionExitNode != null) {
@@ -474,11 +474,11 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun enterSafeCall(qualifiedAccess: FirQualifiedAccess) { private fun enterSafeCall(qualifiedAccess: FirQualifiedAccess) {
if (!qualifiedAccess.safe) return if (!qualifiedAccess.safe) return
val node = graphBuilder.enterSafeCall(qualifiedAccess).mergeIncomingFlow() val node = graphBuilder.enterSafeCall(qualifiedAccess).mergeIncomingFlow()
val previousNode = node.alivePreviousNodes.first() val previousNode = node.firstPreviousNode
val shouldFork: Boolean val shouldFork: Boolean
var flow = if (previousNode is ExitSafeCallNode) { var flow = if (previousNode is ExitSafeCallNode) {
shouldFork = false shouldFork = false
previousNode.alivePreviousNodes.getOrNull(1)?.flow ?: node.flow previousNode.secondPreviousNode?.flow ?: node.flow
} else { } else {
shouldFork = true shouldFork = true
node.flow node.flow
@@ -684,9 +684,9 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
} }
private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) { private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) {
val parentFlow = leftNode.alivePreviousNodes.first().flow val parentFlow = leftNode.firstPreviousNode.flow
leftNode.flow = logicSystem.forkFlow(parentFlow) leftNode.flow = logicSystem.forkFlow(parentFlow)
val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.previousNodes.first().fir) val leftOperandVariable = variableStorage.getOrCreateVariable(leftNode.firstPreviousNode.fir)
rightNode.flow = logicSystem.approveStatementsInsideFlow( rightNode.flow = logicSystem.approveStatementsInsideFlow(
parentFlow, parentFlow,
leftOperandVariable eq isAnd, leftOperandVariable eq isAnd,
@@ -751,7 +751,7 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) { private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.previousNodes.first().fir) val booleanExpressionVariable = variableStorage.getOrCreateVariable(node.firstPreviousNode.fir)
val variable = variableStorage.getOrCreateVariable(functionCall) val variable = variableStorage.getOrCreateVariable(functionCall)
logicSystem.replaceVariableFromConditionInStatements( logicSystem.replaceVariableFromConditionInStatements(
node.flow, node.flow,
@@ -789,10 +789,13 @@ abstract class FirDataFlowAnalyzer<FLOW : Flow>(
flowOnNodes[this.origin] = value flowOnNodes[this.origin] = value
} }
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) previousNodes.first() else this private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) firstPreviousNode else this
private fun <T : CFGNode<*>> T.mergeIncomingFlow(updateReceivers: Boolean = false): T = this.also { node -> private fun <T : CFGNode<*>> T.mergeIncomingFlow(updateReceivers: Boolean = false): T = this.also { node ->
val previousFlows = node.alivePreviousNodes.map { it.flow } val previousFlows = if (node.isDead)
node.previousNodes.map { it.flow }
else
node.previousNodes.mapNotNull { prev -> prev.takeIf { !it.isDead }?.flow }
val flow = logicSystem.joinFlow(previousFlows) val flow = logicSystem.joinFlow(previousFlows)
if (updateReceivers) { if (updateReceivers) {
logicSystem.updateAllReceivers(flow) logicSystem.updateAllReceivers(flow)
@@ -32,6 +32,8 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
val previousNodes = mutableListOf<CFGNode<*>>() val previousNodes = mutableListOf<CFGNode<*>>()
val followingNodes = mutableListOf<CFGNode<*>>() val followingNodes = mutableListOf<CFGNode<*>>()
val firstPreviousNode: CFGNode<*> get() = previousNodes.first()
abstract val fir: E abstract val fir: E
var isDead: Boolean = false var isDead: Boolean = false
@@ -45,8 +47,6 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
} }
} }
val CFGNode<*>.aliveFollowingNodes: List<CFGNode<*>> get() = if (isDead) followingNodes else followingNodes.filterNot { it.isDead }
val CFGNode<*>.alivePreviousNodes: List<CFGNode<*>> get() = if (isDead) previousNodes else previousNodes.filterNot { it.isDead }
interface EnterNode interface EnterNode
interface ExitNode interface ExitNode
@@ -173,7 +173,10 @@ class EnterContractNode(owner: ControlFlowGraph, override val fir: FirFunctionCa
class ExitContractNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int, id: Int) : CFGNode<FirFunctionCall>(owner, level, id), ExitNode class ExitContractNode(owner: ControlFlowGraph, override val fir: FirFunctionCall, level: Int, id: Int) : CFGNode<FirFunctionCall>(owner, level, id), ExitNode
class EnterSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode<FirQualifiedAccess>(owner, level, id) class EnterSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode<FirQualifiedAccess>(owner, level, id)
class ExitSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode<FirQualifiedAccess>(owner, level, id) class ExitSafeCallNode(owner: ControlFlowGraph, override val fir: FirQualifiedAccess, level: Int, id: Int) : CFGNode<FirQualifiedAccess>(owner, level, id) {
val lastPreviousNode: CFGNode<*> get() = previousNodes.last()
val secondPreviousNode: CFGNode<*>? get() = previousNodes.getOrNull(1)
}
// ----------------------------------- Other ----------------------------------- // ----------------------------------- Other -----------------------------------
@@ -148,8 +148,8 @@ class ControlFlowGraphBuilder {
fun CFGNode<*>.extractArgument(): FirElement? = when(this) { fun CFGNode<*>.extractArgument(): FirElement? = when(this) {
is FunctionEnterNode, is TryMainBlockEnterNode, is CatchClauseEnterNode -> null is FunctionEnterNode, is TryMainBlockEnterNode, is CatchClauseEnterNode -> null
is ExitSafeCallNode -> previousNodes.last().extractArgument() is ExitSafeCallNode -> lastPreviousNode.extractArgument()
is StubNode -> previousNodes.first().extractArgument() is StubNode -> firstPreviousNode.extractArgument()
else -> fir.extractArgument() else -> fir.extractArgument()
} }
return exitsOfAnonymousFunctions.getValue(function).previousNodes.mapNotNull { return exitsOfAnonymousFunctions.getValue(function).previousNodes.mapNotNull {