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