[FIR] Some minor fixes in DFA after review

This commit is contained in:
Dmitriy Novozhilov
2019-09-04 18:07:26 +03:00
parent 6b7aa21b8f
commit f9d45d2f2a
10 changed files with 168 additions and 183 deletions
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.resolve.calls.NewCommonSuperTypeCalculator
import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext import org.jetbrains.kotlin.types.model.TypeSystemCommonSuperTypesContext
interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInferenceContext { interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInferenceContext {
fun myCommonSuperType(types: List<ConeKotlinType>): ConeKotlinType? { fun commonSuperTypeOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) { return when (types.size) {
0 -> null 0 -> null
1 -> types.first() 1 -> types.first()
@@ -22,7 +22,7 @@ interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInfe
} }
} }
fun myIntersectTypes(types: List<ConeKotlinType>): ConeKotlinType? { fun intersectTypesOrNull(types: List<ConeKotlinType>): ConeKotlinType? {
return when (types.size) { return when (types.size) {
0 -> null 0 -> null
1 -> types.first() 1 -> types.first()
@@ -43,7 +43,7 @@ interface DataFlowInferenceContext : TypeSystemCommonSuperTypesContext, ConeInfe
val commonTypes = allTypes.toMutableSet() val commonTypes = allTypes.toMutableSet()
types.forEach { commonTypes.retainAll(it) } types.forEach { commonTypes.retainAll(it) }
val differentTypes = allTypes - commonTypes val differentTypes = allTypes - commonTypes
myCommonSuperType(differentTypes.toList())?.let { commonTypes += it } commonSuperTypeOrNull(differentTypes.toList())?.let { commonTypes += it }
return commonTypes return commonTypes
} }
@@ -6,43 +6,32 @@
package org.jetbrains.kotlin.fir.resolve.dfa package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirThisReference
import org.jetbrains.kotlin.fir.expressions.impl.FirThisReceiverExpressionImpl
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
/* /*
* isSynthetic = false for variables that represents actual variables in fir * isSynthetic = false for variables that represents actual variables in fir
* isSynthetic = true for complex expressions (like when expression) * isSynthetic = true for complex expressions (like when expression)
*/ */
sealed class DataFlowVariable(val index: Int, val fir: FirElement) { sealed class DataFlowVariable(val variableIndexForDebug: Int, val fir: FirElement) {
abstract val isSynthetic: Boolean abstract val isSynthetic: Boolean
abstract val real: DataFlowVariable abstract val aliasedVariable: DataFlowVariable
abstract val isThisReference: Boolean abstract val isThisReference: Boolean
final override fun hashCode(): Int {
return index
}
final override fun equals(other: Any?): Boolean {
if (other !is DataFlowVariable) return false
return index == other.index
}
final override fun toString(): String { final override fun toString(): String {
return "d$index" return "d$variableIndexForDebug"
} }
} }
private class RealDataFlowVariable(index: Int, fir: FirElement, override val isThisReference: Boolean) : DataFlowVariable(index, fir) { private class RealDataFlowVariable(index: Int, fir: FirElement, override val isThisReference: Boolean) : DataFlowVariable(index, fir) {
override val isSynthetic: Boolean get() = false override val isSynthetic: Boolean get() = false
override val real: DataFlowVariable get() = this override val aliasedVariable: DataFlowVariable get() = this
} }
private class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowVariable(index, fir) { private class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowVariable(index, fir) {
override val isSynthetic: Boolean get() = true override val isSynthetic: Boolean get() = true
override val real: DataFlowVariable get() = this override val aliasedVariable: DataFlowVariable get() = this
override val isThisReference: Boolean get() = false override val isThisReference: Boolean get() = false
} }
@@ -50,7 +39,7 @@ private class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowV
private class AliasedDataFlowVariable(index: Int, fir: FirElement, var delegate: DataFlowVariable) : DataFlowVariable(index, fir) { private class AliasedDataFlowVariable(index: Int, fir: FirElement, var delegate: DataFlowVariable) : DataFlowVariable(index, fir) {
override val isSynthetic: Boolean get() = delegate.isSynthetic override val isSynthetic: Boolean get() = delegate.isSynthetic
override val real: DataFlowVariable get() = delegate.real override val aliasedVariable: DataFlowVariable get() = delegate.aliasedVariable
override val isThisReference: Boolean get() = false override val isThisReference: Boolean get() = false
} }
@@ -58,7 +47,7 @@ private class AliasedDataFlowVariable(index: Int, fir: FirElement, var delegate:
class DataFlowVariableStorage { class DataFlowVariableStorage {
private val fir2DfiMap: MutableMap<FirElement, DataFlowVariable> = mutableMapOf() private val fir2DfiMap: MutableMap<FirElement, DataFlowVariable> = mutableMapOf()
private var counter: Int = 1 private var debugIndexCounter: Int = 1
fun getOrCreateNewRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable { fun getOrCreateNewRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable {
return getOrCreateNewRealVariableImpl(symbol, false) return getOrCreateNewRealVariableImpl(symbol, false)
@@ -71,12 +60,12 @@ class DataFlowVariableStorage {
private fun getOrCreateNewRealVariableImpl(symbol: FirBasedSymbol<*>, isThisReference: Boolean): DataFlowVariable { private fun getOrCreateNewRealVariableImpl(symbol: FirBasedSymbol<*>, isThisReference: Boolean): DataFlowVariable {
val fir = symbol.fir val fir = symbol.fir
get(fir)?.let { return it } get(fir)?.let { return it }
return RealDataFlowVariable(counter++, fir, isThisReference).also { storeVariable(it, fir) } return RealDataFlowVariable(debugIndexCounter++, fir, isThisReference).also { storeVariable(it, fir) }
} }
fun getOrCreateNewSyntheticVariable(fir: FirElement): DataFlowVariable { fun getOrCreateNewSyntheticVariable(fir: FirElement): DataFlowVariable {
get(fir)?.let { return it } get(fir)?.let { return it }
return SyntheticDataFlowVariable(counter++, fir).also { storeVariable(it, fir) } return SyntheticDataFlowVariable(debugIndexCounter++, fir).also { storeVariable(it, fir) }
} }
fun createAliasVariable(symbol: FirBasedSymbol<*>, variable: DataFlowVariable) { fun createAliasVariable(symbol: FirBasedSymbol<*>, variable: DataFlowVariable) {
@@ -84,7 +73,7 @@ class DataFlowVariableStorage {
} }
private fun createAliasVariable(fir: FirElement, variable: DataFlowVariable) { private fun createAliasVariable(fir: FirElement, variable: DataFlowVariable) {
AliasedDataFlowVariable(counter++, fir, variable).also { storeVariable(it, fir) } AliasedDataFlowVariable(debugIndexCounter++, fir, variable).also { storeVariable(it, fir) }
} }
fun rebindAliasVariable(aliasVariable: DataFlowVariable, newVariable: DataFlowVariable) { fun rebindAliasVariable(aliasVariable: DataFlowVariable, newVariable: DataFlowVariable) {
@@ -122,7 +111,7 @@ class DataFlowVariableStorage {
fun reset() { fun reset() {
fir2DfiMap.clear() fir2DfiMap.clear()
counter = 1 debugIndexCounter = 1
} }
private fun storeVariable(variable: DataFlowVariable, fir: FirElement) { private fun storeVariable(variable: DataFlowVariable, fir: FirElement) {
@@ -40,30 +40,36 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
private val graphBuilder = ControlFlowGraphBuilder() private val graphBuilder = ControlFlowGraphBuilder()
private val logicSystem = LogicSystem(context) private val logicSystem = LogicSystem(context)
private val variableStorage = DataFlowVariableStorage() private val variableStorage = DataFlowVariableStorage()
private val edges = mutableMapOf<CFGNode<*>, Flow>() private val flowOnNodes = mutableMapOf<CFGNode<*>, Flow>()
/* /*
* If there is no types from smartcasts function returns null * If there is no types from smartcasts function returns null
*
* Note that return value based on state of DataFlowAnalyzer (despite of stateless model of old frontend)
*/ */
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>? { fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>? {
val variable = qualifiedAccessExpression.variable?.real ?: return null /*
* DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have)
* If there is no useful information there is no data flow variable also
*/
val variable = qualifiedAccessExpression.variable?.aliasedVariable ?: return null
return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null
} }
// ----------------------------------- Named function ----------------------------------- // ----------------------------------- Named function -----------------------------------
fun enterFunction(function: FirFunction<*>) { fun enterFunction(function: FirFunction<*>) {
graphBuilder.enterFunction(function).passFlow() graphBuilder.enterFunction(function).mergeIncomingFlow()
} }
fun exitFunction(function: FirFunction<*>): ControlFlowGraph? { fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
val (node, graph) = graphBuilder.exitFunction(function) val (node, graph) = graphBuilder.exitFunction(function)
node.passFlow() node.mergeIncomingFlow()
for (valueParameter in function.valueParameters) { for (valueParameter in function.valueParameters) {
variableStorage.removeRealVariable(valueParameter.symbol) variableStorage.removeRealVariable(valueParameter.symbol)
} }
if (graphBuilder.isTopLevel()) { if (graphBuilder.isTopLevel()) {
edges.clear() flowOnNodes.clear()
variableStorage.reset() variableStorage.reset()
} }
return graph return graph
@@ -72,38 +78,38 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Property ----------------------------------- // ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty) { fun enterProperty(property: FirProperty) {
graphBuilder.enterProperty(property).passFlow() graphBuilder.enterProperty(property).mergeIncomingFlow()
} }
fun exitProperty(property: FirProperty): ControlFlowGraph { fun exitProperty(property: FirProperty): ControlFlowGraph {
val (node, graph) = graphBuilder.exitProperty(property) val (node, graph) = graphBuilder.exitProperty(property)
node.passFlow() node.mergeIncomingFlow()
return graph return graph
} }
// ----------------------------------- Block ----------------------------------- // ----------------------------------- Block -----------------------------------
fun enterBlock(block: FirBlock) { fun enterBlock(block: FirBlock) {
val node = graphBuilder.enterBlock(block).passFlow(false) val node = graphBuilder.enterBlock(block).mergeIncomingFlow()
val previousNode = node.usefulPreviousNodes.singleOrNull() as? WhenBranchConditionExitNode val previousNode = node.alivePreviousNodes.singleOrNull() as? WhenBranchConditionExitNode
if (previousNode != null) { if (previousNode != null) {
node.flow = approveFactsAndUpdateImplicitReceivers(previousNode.variable, EqTrue, node.flow) node.flow = approveFactsAndUpdateImplicitReceivers(previousNode.variable, EqTrue, node.flow)
} }
} }
fun exitBlock(block: FirBlock) { fun exitBlock(block: FirBlock) {
graphBuilder.exitBlock(block).passFlow() graphBuilder.exitBlock(block).mergeIncomingFlow()
} }
// ----------------------------------- Operator call ----------------------------------- // ----------------------------------- Operator call -----------------------------------
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) { fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false) val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
if (typeOperatorCall.operation !in FirOperation.TYPES) return if (typeOperatorCall.operation !in FirOperation.TYPES) return
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
val varVariable = getOrCreateRealVariable(typeOperatorCall.argument)?.real ?: return val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument)?.aliasedVariable ?: return
var flow = node.flow var flow = node.flow
when (typeOperatorCall.operation) { when (typeOperatorCall.operation) {
@@ -118,34 +124,34 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( ConditionalFirDataFlowInfo(
EqTrue, varVariable, chooseInfo(true) EqTrue, operandVariable, chooseInfo(true)
) )
) )
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( ConditionalFirDataFlowInfo(
EqFalse, varVariable, chooseInfo(false) EqFalse, operandVariable, chooseInfo(false)
) )
) )
} }
FirOperation.AS -> { FirOperation.AS -> {
flow = addApprovedFact(flow, varVariable, FirDataFlowInfo(setOf(type), emptySet())) flow = addApprovedFact(flow, operandVariable, FirDataFlowInfo(setOf(type), emptySet()))
} }
FirOperation.SAFE_AS -> { FirOperation.SAFE_AS -> {
val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall) val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall)
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( ConditionalFirDataFlowInfo(
NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet()) NotEqNull, operandVariable, FirDataFlowInfo(setOf(type), emptySet())
) )
).addNotApprovedFact( ).addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( ConditionalFirDataFlowInfo(
EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type)) EqNull, operandVariable, FirDataFlowInfo(emptySet(), setOf(type))
) )
) )
} }
@@ -157,7 +163,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitOperatorCall(operatorCall: FirOperatorCall) { fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false) val node = graphBuilder.exitOperatorCall(operatorCall).mergeIncomingFlow()
when (val operation = operatorCall.operation) { when (val operation = operatorCall.operation) {
FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> { FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> {
val leftOperand = operatorCall.arguments[0] val leftOperand = operatorCall.arguments[0]
@@ -192,7 +198,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables -> getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
operandVariables.forEach { operandVariable -> operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo( expressionVariable, ConditionalFirDataFlowInfo(
isEq.toEqBoolean(), isEq.toEqBoolean(),
operandVariable, operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
@@ -208,7 +214,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val constValue = (const.value as Boolean) val constValue = (const.value as Boolean)
val shouldInvert = isEq xor constValue val shouldInvert = isEq xor constValue
flow.notApprovedFacts[operandVariable].forEach { info -> flow.conditionalInfos[operandVariable].forEach { info ->
flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it }) flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it })
} }
} }
@@ -241,12 +247,12 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
facts.forEach { (variable, info) -> facts.forEach { (variable, info) ->
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( ConditionalFirDataFlowInfo(
EqTrue, variable, info EqTrue, variable, info
) )
).addNotApprovedFact( ).addNotApprovedFact(
expressionVariable, expressionVariable,
UnapprovedFirDataFlowInfo( ConditionalFirDataFlowInfo(
EqFalse, variable, info.invert() EqFalse, variable, info.invert()
) )
) )
@@ -265,7 +271,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
operandVariables.forEach { operandVariable -> operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact( flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo( expressionVariable, ConditionalFirDataFlowInfo(
condition, condition,
operandVariable, operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet()) FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
@@ -289,17 +295,17 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Jump ----------------------------------- // ----------------------------------- Jump -----------------------------------
fun exitJump(jump: FirJump<*>) { fun exitJump(jump: FirJump<*>) {
graphBuilder.exitJump(jump).passFlow() graphBuilder.exitJump(jump).mergeIncomingFlow()
} }
// ----------------------------------- When ----------------------------------- // ----------------------------------- When -----------------------------------
fun enterWhenExpression(whenExpression: FirWhenExpression) { fun enterWhenExpression(whenExpression: FirWhenExpression) {
graphBuilder.enterWhenExpression(whenExpression).passFlow() graphBuilder.enterWhenExpression(whenExpression).mergeIncomingFlow()
} }
fun enterWhenBranchCondition(whenBranch: FirWhenBranch) { fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.enterWhenBranchCondition(whenBranch).passFlow(false) val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow()
val previousNode = node.previousNodes.single() val previousNode = node.previousNodes.single()
if (previousNode is WhenBranchConditionExitNode) { if (previousNode is WhenBranchConditionExitNode) {
node.flow = approveFactsAndUpdateImplicitReceivers(previousNode.variable, EqFalse, node.flow) node.flow = approveFactsAndUpdateImplicitReceivers(previousNode.variable, EqFalse, node.flow)
@@ -308,20 +314,20 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) { fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow(true) val node = graphBuilder.exitWhenBranchCondition(whenBranch).mergeIncomingFlow()
node.flow.freeze()
val conditionVariable = getOrCreateVariable(whenBranch.condition) val conditionVariable = getOrCreateVariable(whenBranch.condition)
node.variable = conditionVariable node.variable = conditionVariable
} }
fun exitWhenBranchResult(whenBranch: FirWhenBranch) { fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false) val node = graphBuilder.exitWhenBranchResult(whenBranch).mergeIncomingFlow()
val conditionVariable = getOrCreateVariable(whenBranch.condition) val conditionVariable = getOrCreateVariable(whenBranch.condition)
node.flow = node.flow.removeSyntheticVariable(conditionVariable).apply { freeze() } node.flow = node.flow.removeSyntheticVariable(conditionVariable).apply { freeze() }
} }
fun exitWhenExpression(whenExpression: FirWhenExpression) { fun exitWhenExpression(whenExpression: FirWhenExpression) {
val node = graphBuilder.exitWhenExpression(whenExpression).passFlow() val node = graphBuilder.exitWhenExpression(whenExpression).mergeIncomingFlow()
var flow = node.flow var flow = node.flow
val subjectSymbol = whenExpression.subjectVariable?.symbol val subjectSymbol = whenExpression.subjectVariable?.symbol
if (subjectSymbol != null) { if (subjectSymbol != null) {
@@ -334,74 +340,74 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun enterWhileLoop(loop: FirLoop) { fun enterWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop) val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.passFlow() loopEnterNode.mergeIncomingFlow()
loopConditionEnterNode.passFlow() loopConditionEnterNode.mergeIncomingFlow()
} }
fun exitWhileLoopCondition(loop: FirLoop) { fun exitWhileLoopCondition(loop: FirLoop) {
graphBuilder.exitWhileLoopCondition(loop).passFlow() graphBuilder.exitWhileLoopCondition(loop).mergeIncomingFlow()
} }
fun exitWhileLoop(loop: FirLoop) { fun exitWhileLoop(loop: FirLoop) {
graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) -> graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) ->
blockExitNode.passFlow() blockExitNode.mergeIncomingFlow()
exitNode.passFlow() exitNode.mergeIncomingFlow()
} }
} }
// ----------------------------------- Do while Loop ----------------------------------- // ----------------------------------- Do while Loop -----------------------------------
fun enterDoWhileLoop(loop: FirLoop) { fun enterDoWhileLoop(loop: FirLoop) {
graphBuilder.enterDoWhileLoop(loop).passFlow() graphBuilder.enterDoWhileLoop(loop).mergeIncomingFlow()
} }
fun enterDoWhileLoopCondition(loop: FirLoop) { fun enterDoWhileLoopCondition(loop: FirLoop) {
val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop) val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop)
loopBlockExitNode.passFlow() loopBlockExitNode.mergeIncomingFlow()
loopConditionEnterNode.passFlow() loopConditionEnterNode.mergeIncomingFlow()
} }
fun exitDoWhileLoop(loop: FirLoop) { fun exitDoWhileLoop(loop: FirLoop) {
graphBuilder.exitDoWhileLoop(loop).passFlow() graphBuilder.exitDoWhileLoop(loop).mergeIncomingFlow()
} }
// ----------------------------------- Try-catch-finally ----------------------------------- // ----------------------------------- Try-catch-finally -----------------------------------
fun enterTryExpression(tryExpression: FirTryExpression) { fun enterTryExpression(tryExpression: FirTryExpression) {
graphBuilder.enterTryExpression(tryExpression).passFlow() graphBuilder.enterTryExpression(tryExpression).mergeIncomingFlow()
} }
fun exitTryMainBlock(tryExpression: FirTryExpression) { fun exitTryMainBlock(tryExpression: FirTryExpression) {
graphBuilder.exitTryMainBlock(tryExpression).passFlow() graphBuilder.exitTryMainBlock(tryExpression).mergeIncomingFlow()
} }
fun enterCatchClause(catch: FirCatch) { fun enterCatchClause(catch: FirCatch) {
graphBuilder.enterCatchClause(catch).passFlow() graphBuilder.enterCatchClause(catch).mergeIncomingFlow()
} }
fun exitCatchClause(catch: FirCatch) { fun exitCatchClause(catch: FirCatch) {
graphBuilder.exitCatchClause(catch).passFlow() graphBuilder.exitCatchClause(catch).mergeIncomingFlow()
} }
fun enterFinallyBlock(tryExpression: FirTryExpression) { fun enterFinallyBlock(tryExpression: FirTryExpression) {
// TODO // TODO
graphBuilder.enterFinallyBlock(tryExpression).passFlow() graphBuilder.enterFinallyBlock(tryExpression).mergeIncomingFlow()
} }
fun exitFinallyBlock(tryExpression: FirTryExpression) { fun exitFinallyBlock(tryExpression: FirTryExpression) {
// TODO // TODO
graphBuilder.exitFinallyBlock(tryExpression).passFlow() graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow()
} }
fun exitTryExpression(tryExpression: FirTryExpression) { fun exitTryExpression(tryExpression: FirTryExpression) {
// TODO // TODO
graphBuilder.exitTryExpression(tryExpression).passFlow() graphBuilder.exitTryExpression(tryExpression).mergeIncomingFlow()
} }
// ----------------------------------- Resolvable call ----------------------------------- // ----------------------------------- Resolvable call -----------------------------------
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) { fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).passFlow() graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).mergeIncomingFlow()
} }
fun enterFunctionCall(functionCall: FirFunctionCall) { fun enterFunctionCall(functionCall: FirFunctionCall) {
@@ -409,7 +415,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitFunctionCall(functionCall: FirFunctionCall) { fun exitFunctionCall(functionCall: FirFunctionCall) {
val node = graphBuilder.exitFunctionCall(functionCall).passFlow(false) val node = graphBuilder.exitFunctionCall(functionCall).mergeIncomingFlow()
if (functionCall.isBooleanNot()) { if (functionCall.isBooleanNot()) {
exitBooleanNot(functionCall, node) exitBooleanNot(functionCall, node)
return return
@@ -434,11 +440,11 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitConstExpresion(constExpression: FirConstExpression<*>) { fun exitConstExpresion(constExpression: FirConstExpression<*>) {
graphBuilder.exitConstExpresion(constExpression).passFlow() graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow()
} }
fun exitVariableDeclaration(variable: FirVariable<*>) { fun exitVariableDeclaration(variable: FirVariable<*>) {
val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false) val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
val initializer = variable.initializer ?: return val initializer = variable.initializer ?: return
/* /*
@@ -463,33 +469,34 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitVariableAssignment(assignment: FirVariableAssignment) { fun exitVariableAssignment(assignment: FirVariableAssignment) {
graphBuilder.exitVariableAssignment(assignment).passFlow() graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return
val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return
variableStorage.rebindAliasVariable(lhsVariable, rhsVariable) variableStorage.rebindAliasVariable(lhsVariable, rhsVariable)
} }
fun exitThrowExceptionNode(throwExpression: FirThrowExpression) { fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
graphBuilder.exitThrowExceptionNode(throwExpression).passFlow() graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow()
} }
// ----------------------------------- Boolean operators ----------------------------------- // ----------------------------------- Boolean operators -----------------------------------
fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryAnd(binaryLogicExpression).passFlow() graphBuilder.enterBinaryAnd(binaryLogicExpression).mergeIncomingFlow()
} }
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) { fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression) val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
leftNode.passFlow(true) leftNode.mergeIncomingFlow()
rightNode.passFlow(false) leftNode.flow.freeze()
rightNode.mergeIncomingFlow()
val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir) val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir)
val flow = approveFactsAndUpdateImplicitReceivers(leftOperandVariable, EqTrue, rightNode.flow) val flow = approveFactsAndUpdateImplicitReceivers(leftOperandVariable, EqTrue, rightNode.flow)
rightNode.flow = flow.also { it.freeze() } rightNode.flow = flow.also { it.freeze() }
} }
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) { fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).passFlow(false) val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).mergeIncomingFlow()
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow val flowFromLeft = node.leftOperandNode.flow
@@ -503,19 +510,19 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) ?: mutableMapOf() val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight) ?: mutableMapOf()
val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight) val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight)
flowFromRight.approvedFacts.forEach { (variable, info) -> flowFromRight.approvedInfos.forEach { (variable, info) ->
val actualInfo = flowFromLeft.approvedFacts[variable]?.let { info - it } ?: info val actualInfo = flowFromLeft.approvedInfos[variable]?.let { info - it } ?: info
if (actualInfo.isNotEmpty) rightIsTrue.compute(variable) { _, existingInfo -> info + existingInfo } if (actualInfo.isNotEmpty) rightIsTrue.compute(variable) { _, existingInfo -> info + existingInfo }
} }
logicSystem.andForVerifiedFacts(leftIsTrue, rightIsTrue)?.let { logicSystem.andForVerifiedFacts(leftIsTrue, rightIsTrue)?.let {
for ((variable, info) in it) { for ((variable, info) in it) {
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info)) flow.addNotApprovedFact(andVariable, ConditionalFirDataFlowInfo(EqTrue, variable, info))
} }
} }
logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let { logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) { for ((variable, info) in it) {
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) flow.addNotApprovedFact(andVariable, ConditionalFirDataFlowInfo(EqFalse, variable, info))
} }
} }
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable) node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable)
@@ -527,15 +534,16 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) { fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression) val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
leftNode.passFlow(true) leftNode.mergeIncomingFlow()
rightNode.passFlow(false) leftNode.flow.freeze()
rightNode.mergeIncomingFlow()
val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir) val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir)
val flow = approveFactsAndUpdateImplicitReceivers(leftOperandVariable, EqFalse, rightNode.flow) val flow = approveFactsAndUpdateImplicitReceivers(leftOperandVariable, EqFalse, rightNode.flow)
rightNode.flow = flow.also { it.freeze() } rightNode.flow = flow.also { it.freeze() }
} }
fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) { fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryOr(binaryLogicExpression).passFlow(false) val node = graphBuilder.exitBinaryOr(binaryLogicExpression).mergeIncomingFlow()
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables() val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow val flowFromLeft = node.leftOperandNode.flow
@@ -551,12 +559,12 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let { logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let {
for ((variable, info) in it) { for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info)) flow.addNotApprovedFact(orVariable, ConditionalFirDataFlowInfo(EqTrue, variable, info))
} }
} }
logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let { logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) { for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info)) flow.addNotApprovedFact(orVariable, ConditionalFirDataFlowInfo(EqFalse, variable, info))
} }
} }
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable) node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable)
@@ -571,11 +579,11 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
// ----------------------------------- Annotations ----------------------------------- // ----------------------------------- Annotations -----------------------------------
fun enterAnnotationCall(annotationCall: FirAnnotationCall) { fun enterAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.enterAnnotationCall(annotationCall).passFlow() graphBuilder.enterAnnotationCall(annotationCall).mergeIncomingFlow()
} }
fun exitAnnotationCall(annotationCall: FirAnnotationCall) { fun exitAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.exitAnnotationCall(annotationCall).passFlow() graphBuilder.exitAnnotationCall(annotationCall).mergeIncomingFlow()
} }
// ----------------------------------- Init block ----------------------------------- // ----------------------------------- Init block -----------------------------------
@@ -585,7 +593,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
fun exitInitBlock(initBlock: FirAnonymousInitializer) { fun exitInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.exitInitBlock(initBlock).passFlow() graphBuilder.exitInitBlock(initBlock).mergeIncomingFlow()
} }
// ------------------------------------------------------------------------------------------------------------------------- // -------------------------------------------------------------------------------------------------------------------------
@@ -597,16 +605,14 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
getOrCreateVariable(leftOperand) to getOrCreateVariable(rightOperand) getOrCreateVariable(leftOperand) to getOrCreateVariable(rightOperand)
private var CFGNode<*>.flow: Flow private var CFGNode<*>.flow: Flow
get() = edges[this] ?: Flow.EMPTY get() = flowOnNodes[this] ?: Flow.EMPTY
set(value) { set(value) {
edges[this] = value flowOnNodes[this] = value
} }
private fun <T : CFGNode<*>> T.passFlow(shouldFreeze: Boolean = false): T = this.also { node -> private fun <T : CFGNode<*>> T.mergeIncomingFlow(): T = this.also { node ->
val previousFlows = node.usefulPreviousNodes.map { it.flow } val previousFlows = node.alivePreviousNodes.map { it.flow }
val flow = logicSystem.or(previousFlows).also { val flow = logicSystem.or(previousFlows)
if (shouldFreeze) it.freeze()
}
if (previousFlows.size > 1) { if (previousFlows.size > 1) {
receiverStack.forEachIndexed { index, receiver -> receiverStack.forEachIndexed { index, receiver ->
val variable = variableStorage[receiver.boundSymbol] ?: return@forEachIndexed val variable = variableStorage[receiver.boundSymbol] ?: return@forEachIndexed
@@ -633,7 +639,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
} }
private fun getOrCreateRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = private fun getOrCreateRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable =
variableStorage.getOrCreateNewRealVariable(symbol).real variableStorage.getOrCreateNewRealVariable(symbol).aliasedVariable
private fun getOrCreateVariable(fir: FirElement): DataFlowVariable { private fun getOrCreateVariable(fir: FirElement): DataFlowVariable {
val symbol = fir.resolvedSymbol val symbol = fir.resolvedSymbol
@@ -695,7 +701,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
private fun updateReceiverType(flow: Flow, variable: DataFlowVariable) { private fun updateReceiverType(flow: Flow, variable: DataFlowVariable) {
val symbol = (variable.fir as? FirSymbolOwner<*>)?.symbol ?: return val symbol = (variable.fir as? FirSymbolOwner<*>)?.symbol ?: return
val index = receiverStack.getReceiverIndex(symbol) ?: return val index = receiverStack.getReceiverIndex(symbol) ?: return
val info = flow.approvedFacts[variable] ?: return val info = flow.approvedInfos[variable] ?: return
updateReceiverType(index, info) updateReceiverType(index, info)
} }
@@ -703,7 +709,7 @@ class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveC
val types = info.exactType.toMutableList().also { val types = info.exactType.toMutableList().also {
it += receiverStack.getOriginalType(index) it += receiverStack.getOriginalType(index)
} }
receiverStack.replaceReceiverType(index, context.myIntersectTypes(types)!!) receiverStack.replaceReceiverType(index, context.intersectTypesOrNull(types)!!)
} }
@@ -8,13 +8,13 @@ package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.types.ConeKotlinType import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.render import org.jetbrains.kotlin.fir.types.render
data class UnapprovedFirDataFlowInfo( data class ConditionalFirDataFlowInfo(
val condition: Condition, val condition: Condition,
val variable: DataFlowVariable, val variable: DataFlowVariable,
val info: FirDataFlowInfo val info: FirDataFlowInfo
) { ) {
fun invert(): UnapprovedFirDataFlowInfo { fun invert(): ConditionalFirDataFlowInfo {
return UnapprovedFirDataFlowInfo(condition.invert(), variable, info) return ConditionalFirDataFlowInfo(condition.invert(), variable, info)
} }
override fun toString(): String { override fun toString(): String {
@@ -8,8 +8,8 @@ package org.jetbrains.kotlin.fir.resolve.dfa
import com.google.common.collect.HashMultimap import com.google.common.collect.HashMultimap
class Flow( class Flow(
val approvedFacts: MutableMap<DataFlowVariable, FirDataFlowInfo> = mutableMapOf(), val approvedInfos: MutableMap<DataFlowVariable, FirDataFlowInfo> = mutableMapOf(),
val notApprovedFacts: HashMultimap<DataFlowVariable, UnapprovedFirDataFlowInfo> = HashMultimap.create(), val conditionalInfos: HashMultimap<DataFlowVariable, ConditionalFirDataFlowInfo> = HashMultimap.create(),
private var state: State = State.Building private var state: State = State.Building
) { ) {
private val isFrozen: Boolean get() = state == State.Frozen private val isFrozen: Boolean get() = state == State.Frozen
@@ -20,46 +20,46 @@ class Flow(
fun addApprovedFact(variable: DataFlowVariable, info: FirDataFlowInfo): Flow { fun addApprovedFact(variable: DataFlowVariable, info: FirDataFlowInfo): Flow {
if (isFrozen) return copyForBuilding().addApprovedFact(variable, info) if (isFrozen) return copyForBuilding().addApprovedFact(variable, info)
approvedFacts.compute(variable) { _, existingInfo -> approvedInfos.compute(variable) { _, existingInfo ->
if (existingInfo == null) info if (existingInfo == null) info
else existingInfo + info else existingInfo + info
} }
return this return this
} }
fun addNotApprovedFact(variable: DataFlowVariable, info: UnapprovedFirDataFlowInfo): Flow { fun addNotApprovedFact(variable: DataFlowVariable, info: ConditionalFirDataFlowInfo): Flow {
if (isFrozen) return copyForBuilding().addNotApprovedFact(variable, info) if (isFrozen) return copyForBuilding().addNotApprovedFact(variable, info)
notApprovedFacts.put(variable, info) conditionalInfos.put(variable, info)
return this return this
} }
fun copyNotApprovedFacts( fun copyNotApprovedFacts(
from: DataFlowVariable, from: DataFlowVariable,
to: DataFlowVariable, to: DataFlowVariable,
transform: ((UnapprovedFirDataFlowInfo) -> UnapprovedFirDataFlowInfo)? = null transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo)? = null
): Flow { ): Flow {
if (isFrozen) if (isFrozen)
return copyForBuilding().copyNotApprovedFacts(from, to, transform) return copyForBuilding().copyNotApprovedFacts(from, to, transform)
var facts = if (from.isSynthetic) { var facts = if (from.isSynthetic) {
notApprovedFacts.removeAll(from) conditionalInfos.removeAll(from)
} else { } else {
notApprovedFacts[from] conditionalInfos[from]
} }
if (transform != null) { if (transform != null) {
facts = facts.mapTo(mutableSetOf(), transform) facts = facts.mapTo(mutableSetOf(), transform)
} }
notApprovedFacts.putAll(to, facts) conditionalInfos.putAll(to, facts)
return this return this
} }
fun approvedFacts(variable: DataFlowVariable): FirDataFlowInfo? { fun approvedFacts(variable: DataFlowVariable): FirDataFlowInfo? {
return approvedFacts[variable] return approvedInfos[variable]
} }
fun removeVariableFromFlow(variable: DataFlowVariable): Flow { fun removeVariableFromFlow(variable: DataFlowVariable): Flow {
if (isFrozen) return copyForBuilding().removeVariableFromFlow(variable) if (isFrozen) return copyForBuilding().removeVariableFromFlow(variable)
notApprovedFacts.removeAll(variable) conditionalInfos.removeAll(variable)
approvedFacts.remove(variable) approvedInfos.remove(variable)
return this return this
} }
@@ -79,7 +79,7 @@ class Flow(
} }
fun copyForBuilding(): Flow { fun copyForBuilding(): Flow {
return Flow(approvedFacts.toMutableMap(), notApprovedFacts.copy(), State.Building) return Flow(approvedInfos.toMutableMap(), conditionalInfos.copy(), State.Building)
} }
} }
@@ -14,28 +14,27 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
storages.singleOrNull()?.let { storages.singleOrNull()?.let {
return it return it
} }
val approvedFacts = mutableMapOf<DataFlowVariable, FirDataFlowInfo>().apply { val approvedFacts = mutableMapOf<DataFlowVariable, FirDataFlowInfo>()
storages.map { it.approvedFacts.keys } storages.map { it.approvedInfos.keys }
.intersectSets() .intersectSets()
.forEach { variable -> .forEach { variable ->
val infos = storages.map { it.approvedFacts[variable]!! } val infos = storages.map { it.approvedInfos[variable]!! }
if (infos.isNotEmpty()) { if (infos.isNotEmpty()) {
this[variable] = context.or(infos) approvedFacts[variable] = context.or(infos)
}
} }
}
val notApprovedFacts = HashMultimap.create<DataFlowVariable, UnapprovedFirDataFlowInfo>()
.apply {
storages.map { it.notApprovedFacts.keySet() }
.intersectSets()
.forEach { variable ->
val infos = storages.map { it.notApprovedFacts[variable] }.intersectSets()
if (infos.isNotEmpty()) {
this.putAll(variable, infos)
}
}
} }
val notApprovedFacts = HashMultimap.create<DataFlowVariable, ConditionalFirDataFlowInfo>()
storages.map { it.conditionalInfos.keySet() }
.intersectSets()
.forEach { variable ->
val infos = storages.map { it.conditionalInfos[variable] }.intersectSets()
if (infos.isNotEmpty()) {
notApprovedFacts.putAll(variable, infos)
}
}
return Flow(approvedFacts, notApprovedFacts) return Flow(approvedFacts, notApprovedFacts)
} }
@@ -70,7 +69,7 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
} }
fun approveFactsInsideFlow(variable: DataFlowVariable, condition: Condition, flow: Flow): Pair<Flow, Collection<DataFlowVariable>> { fun approveFactsInsideFlow(variable: DataFlowVariable, condition: Condition, flow: Flow): Pair<Flow, Collection<DataFlowVariable>> {
val notApprovedFacts: Set<UnapprovedFirDataFlowInfo> = flow.notApprovedFacts[variable] val notApprovedFacts: Set<ConditionalFirDataFlowInfo> = flow.conditionalInfos[variable]
if (notApprovedFacts.isEmpty()) { if (notApprovedFacts.isEmpty()) {
return flow to emptyList() return flow to emptyList()
} }
@@ -87,10 +86,10 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
newFacts.asMap().forEach { (variable, infos) -> newFacts.asMap().forEach { (variable, infos) ->
@Suppress("NAME_SHADOWING") @Suppress("NAME_SHADOWING")
val infos = ArrayList(infos) val infos = ArrayList(infos)
flow.approvedFacts[variable]?.let { flow.approvedInfos[variable]?.let {
infos.add(it) infos.add(it)
} }
flow.approvedFacts[variable] = context.and(infos) flow.approvedInfos[variable] = context.and(infos)
if (variable.isThisReference) { if (variable.isThisReference) {
updatedReceivers += variable updatedReceivers += variable
} }
@@ -99,7 +98,7 @@ class LogicSystem(private val context: DataFlowInferenceContext) {
} }
fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap<DataFlowVariable, FirDataFlowInfo> { fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap<DataFlowVariable, FirDataFlowInfo> {
val notApprovedFacts: Set<UnapprovedFirDataFlowInfo> = flow.notApprovedFacts[variable] val notApprovedFacts: Set<ConditionalFirDataFlowInfo> = flow.conditionalInfos[variable]
if (notApprovedFacts.isEmpty()) { if (notApprovedFacts.isEmpty()) {
return mutableMapOf() return mutableMapOf()
} }
@@ -31,12 +31,8 @@ sealed class CFGNode<out E : FirElement>(val owner: ControlFlowGraph, val level:
var isDead: Boolean = false var isDead: Boolean = false
} }
val CFGNode<*>.usefulFollowingNodes: List<CFGNode<*>> get() = if (isDead) followingNodes else followingNodes.filterNot { it.isDead } val CFGNode<*>.aliveFollowingNodes: 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 } val CFGNode<*>.alivePreviousNodes: List<CFGNode<*>> get() = if (isDead) previousNodes else previousNodes.filterNot { it.isDead }
interface ReturnableNothingNode {
val returnsNothing: Boolean
}
interface EnterNode interface EnterNode
interface ExitNode interface ExitNode
@@ -48,8 +44,8 @@ class FunctionExitNode(owner: ControlFlowGraph, override val fir: FirFunction<*>
// ----------------------------------- Property ----------------------------------- // ----------------------------------- Property -----------------------------------
class PropertyEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), EnterNode class PropertyInitializerEnterNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), EnterNode
class PropertyExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), ExitNode class PropertyInitializerExitNode(owner: ControlFlowGraph, override val fir: FirProperty, level: Int) : CFGNode<FirProperty>(owner, level), ExitNode
// ----------------------------------- Init ----------------------------------- // ----------------------------------- Init -----------------------------------
@@ -125,24 +121,20 @@ class ConstExpressionNode(owner: ControlFlowGraph, override val fir: FirConstExp
class QualifiedAccessNode( class QualifiedAccessNode(
owner: ControlFlowGraph, owner: ControlFlowGraph,
override val fir: FirQualifiedAccessExpression, override val fir: FirQualifiedAccessExpression,
override val returnsNothing: Boolean,
level: Int level: Int
) : CFGNode<FirQualifiedAccessExpression>(owner, level), ReturnableNothingNode ) : CFGNode<FirQualifiedAccessExpression>(owner, level)
class FunctionCallNode( class FunctionCallNode(
owner: ControlFlowGraph, owner: ControlFlowGraph,
override val fir: FirFunctionCall, override val fir: FirFunctionCall,
override val returnsNothing: Boolean,
level: Int level: Int
) : CFGNode<FirFunctionCall>(owner, level), ReturnableNothingNode ) : CFGNode<FirFunctionCall>(owner, level)
class ThrowExceptionNode( class ThrowExceptionNode(
owner: ControlFlowGraph, owner: ControlFlowGraph,
override val fir: FirThrowExpression, override val fir: FirThrowExpression,
level: Int level: Int
) : CFGNode<FirThrowExpression>(owner, level), ReturnableNothingNode { ) : CFGNode<FirThrowExpression>(owner, level)
override val returnsNothing: Boolean get() = true
}
class StubNode(owner: ControlFlowGraph, level: Int) : CFGNode<FirStub>(owner, level) { class StubNode(owner: ControlFlowGraph, level: Int) : CFGNode<FirStub>(owner, level) {
init { init {
@@ -39,7 +39,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
private val binaryAndExitNodes: Stack<BinaryAndExitNode> = stackOf() private val binaryAndExitNodes: Stack<BinaryAndExitNode> = stackOf()
private val binaryOrExitNodes: Stack<BinaryOrExitNode> = stackOf() private val binaryOrExitNodes: Stack<BinaryOrExitNode> = stackOf()
private val topLevelVariableExitNodes: Stack<PropertyExitNode> = stackWithCallbacks( private val topLevelVariableInitializerExitNodes: Stack<PropertyInitializerExitNode> = stackWithCallbacks(
pushCallback = { exitNodes.push(it) }, pushCallback = { exitNodes.push(it) },
popCallback = { exitNodes.pop() } popCallback = { exitNodes.pop() }
) )
@@ -127,11 +127,11 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
// ----------------------------------- Property ----------------------------------- // ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty): PropertyEnterNode { fun enterProperty(property: FirProperty): PropertyInitializerEnterNode {
graphs.push(ControlFlowGraph("val ${property.name}")) graphs.push(ControlFlowGraph("val ${property.name}"))
val enterNode = createPropertyEnterNode(property) val enterNode = createPropertyInitializerEnterNode(property)
val exitNode = createPropertyExitNode(property) val exitNode = createPropertyInitializerExitNode(property)
topLevelVariableExitNodes.push(exitNode) topLevelVariableInitializerExitNodes.push(exitNode)
lexicalScopes.push(stackOf(enterNode)) lexicalScopes.push(stackOf(enterNode))
graph.enterNode = enterNode graph.enterNode = enterNode
graph.exitNode = exitNode graph.exitNode = exitNode
@@ -139,8 +139,8 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
return enterNode return enterNode
} }
fun exitProperty(property: FirProperty): Pair<PropertyExitNode, ControlFlowGraph> { fun exitProperty(property: FirProperty): Pair<PropertyInitializerExitNode, ControlFlowGraph> {
val topLevelVariableExitNode = topLevelVariableExitNodes.pop().also { val topLevelVariableExitNode = topLevelVariableInitializerExitNodes.pop().also {
addNewSimpleNode(it) addNewSimpleNode(it)
it.markAsDeadIfNecessary() it.markAsDeadIfNecessary()
} }
@@ -169,7 +169,6 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
is FirBreakExpression -> loopExitNodes[jump.target.labeledElement] is FirBreakExpression -> loopExitNodes[jump.target.labeledElement]
else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}") else -> throw IllegalArgumentException("Unknown jump type: ${jump.render()}")
} }
addNodeWithJump(node, nextNode) addNodeWithJump(node, nextNode)
return node return node
} }
@@ -456,7 +455,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode { fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression): QualifiedAccessNode {
val returnsNothing = qualifiedAccessExpression.resultType.isNothing val returnsNothing = qualifiedAccessExpression.resultType.isNothing
val node = createQualifiedAccessNode(qualifiedAccessExpression, returnsNothing) val node = createQualifiedAccessNode(qualifiedAccessExpression)
if (returnsNothing) { if (returnsNothing) {
addNodeThatReturnsNothing(node) addNodeThatReturnsNothing(node)
} else { } else {
@@ -467,7 +466,7 @@ class ControlFlowGraphBuilder : ControlFlowGraphNodeBuilder() {
fun exitFunctionCall(functionCall: FirFunctionCall): FunctionCallNode { fun exitFunctionCall(functionCall: FirFunctionCall): FunctionCallNode {
val returnsNothing = functionCall.resultType.isNothing val returnsNothing = functionCall.resultType.isNothing
val node = createFunctionCallNode(functionCall, returnsNothing) val node = createFunctionCallNode(functionCall)
if (returnsNothing) { if (returnsNothing) {
addNodeThatReturnsNothing(node) addNodeThatReturnsNothing(node)
} else { } else {
@@ -41,18 +41,18 @@ abstract class ControlFlowGraphNodeBuilder {
protected fun createJumpNode(fir: FirJump<*>): JumpNode = protected fun createJumpNode(fir: FirJump<*>): JumpNode =
JumpNode(graph, fir, levelCounter) JumpNode(graph, fir, levelCounter)
protected fun createQualifiedAccessNode( protected fun createQualifiedAccessNode(fir: FirQualifiedAccessExpression): QualifiedAccessNode =
fir: FirQualifiedAccessExpression, QualifiedAccessNode(graph, fir, levelCounter)
returnsNothing: Boolean
): QualifiedAccessNode = QualifiedAccessNode(graph, fir, returnsNothing, levelCounter)
protected fun createBlockEnterNode(fir: FirBlock): BlockEnterNode = BlockEnterNode(graph, fir, levelCounter) protected fun createBlockEnterNode(fir: FirBlock): BlockEnterNode = BlockEnterNode(graph, fir, levelCounter)
protected fun createBlockExitNode(fir: FirBlock): BlockExitNode = BlockExitNode(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 createPropertyInitializerExitNode(fir: FirProperty): PropertyInitializerExitNode =
PropertyInitializerExitNode(graph, fir, levelCounter)
protected fun createPropertyEnterNode(fir: FirProperty): PropertyEnterNode = PropertyEnterNode(graph, fir, levelCounter) protected fun createPropertyInitializerEnterNode(fir: FirProperty): PropertyInitializerEnterNode =
PropertyInitializerEnterNode(graph, fir, levelCounter)
protected fun createFunctionEnterNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionEnterNode = protected fun createFunctionEnterNode(fir: FirFunction<*>, isInPlace: Boolean): FunctionEnterNode =
FunctionEnterNode(graph, fir, levelCounter).also { FunctionEnterNode(graph, fir, levelCounter).also {
@@ -66,7 +66,7 @@ abstract class ControlFlowGraphNodeBuilder {
if (!isInPlace) { if (!isInPlace) {
graph.exitNode = it graph.exitNode = it
} }
} }
protected fun createBinaryOrEnterNode(fir: FirBinaryLogicExpression): BinaryOrEnterNode = protected fun createBinaryOrEnterNode(fir: FirBinaryLogicExpression): BinaryOrEnterNode =
BinaryOrEnterNode(graph, fir, levelCounter) BinaryOrEnterNode(graph, fir, levelCounter)
@@ -107,8 +107,8 @@ abstract class ControlFlowGraphNodeBuilder {
protected fun createLoopBlockExitNode(fir: FirLoop): LoopBlockExitNode = protected fun createLoopBlockExitNode(fir: FirLoop): LoopBlockExitNode =
LoopBlockExitNode(graph, fir, levelCounter) LoopBlockExitNode(graph, fir, levelCounter)
protected fun createFunctionCallNode(fir: FirFunctionCall, returnsNothing: Boolean): FunctionCallNode = protected fun createFunctionCallNode(fir: FirFunctionCall): FunctionCallNode =
FunctionCallNode(graph, fir, returnsNothing, levelCounter) FunctionCallNode(graph, fir, levelCounter)
protected fun createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode = protected fun createVariableAssignmentNode(fir: FirVariableAssignment): VariableAssignmentNode =
VariableAssignmentNode(graph, fir, levelCounter) VariableAssignmentNode(graph, fir, levelCounter)
@@ -141,8 +141,8 @@ fun CFGNode<*>.render(): String =
is BinaryOrEnterRightOperandNode -> "Enter right part of ||" is BinaryOrEnterRightOperandNode -> "Enter right part of ||"
is BinaryOrExitNode -> "Exit ||" is BinaryOrExitNode -> "Exit ||"
is PropertyEnterNode -> "Enter property" is PropertyInitializerEnterNode -> "Enter property"
is PropertyExitNode -> "Exit property" is PropertyInitializerExitNode -> "Exit property"
is InitBlockEnterNode -> "Enter init block" is InitBlockEnterNode -> "Enter init block"
is InitBlockExitNode -> "Exit init block" is InitBlockExitNode -> "Exit init block"
is AnnotationEnterNode -> "Enter annotation" is AnnotationEnterNode -> "Enter annotation"