[FIR] Some minor fixes in DFA after review
This commit is contained in:
+3
-3
@@ -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) {
|
||||||
|
|||||||
+88
-82
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-15
@@ -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 {
|
||||||
|
|||||||
+9
-10
@@ -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 {
|
||||||
|
|||||||
+9
-9
@@ -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)
|
||||||
|
|||||||
+2
-2
@@ -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"
|
||||||
|
|||||||
Reference in New Issue
Block a user