[FIR] Remove DummyFirDataFlowAnalyzer

This commit is contained in:
Dmitriy Novozhilov
2019-08-30 15:33:07 +03:00
parent 92acd30bd6
commit 286a322b55
4 changed files with 617 additions and 829 deletions
@@ -1,111 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
import org.jetbrains.kotlin.fir.types.ConeKotlinType
class DummyFirDataFlowAnalyzer : FirDataFlowAnalyzer() {
companion object {
private const val DUMMY = "<DUMMY>"
}
override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>? {
return null
}
override fun enterFunction(function: FirFunction<*>) {}
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? = null
override fun enterBlock(block: FirBlock) {}
override fun exitBlock(block: FirBlock) {}
override fun enterProperty(property: FirProperty) {}
override fun exitProperty(property: FirProperty) = ControlFlowGraph(DUMMY)
override fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {}
override fun exitOperatorCall(operatorCall: FirOperatorCall) {}
override fun exitJump(jump: FirJump<*>) {}
override fun enterWhenExpression(whenExpression: FirWhenExpression) {}
override fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {}
override fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {}
override fun exitWhenBranchResult(whenBranch: FirWhenBranch) {}
override fun exitWhenExpression(whenExpression: FirWhenExpression) {}
override fun enterWhileLoop(loop: FirLoop) {}
override fun exitWhileLoopCondition(loop: FirLoop) {}
override fun exitWhileLoop(loop: FirLoop) {}
override fun enterDoWhileLoop(loop: FirLoop) {}
override fun enterDoWhileLoopCondition(loop: FirLoop) {}
override fun exitDoWhileLoop(loop: FirLoop) {}
override fun enterTryExpression(tryExpression: FirTryExpression) {}
override fun exitTryMainBlock(tryExpression: FirTryExpression) {}
override fun enterCatchClause(catch: FirCatch) {}
override fun exitCatchClause(catch: FirCatch) {}
override fun enterFinallyBlock(tryExpression: FirTryExpression) {}
override fun exitFinallyBlock(tryExpression: FirTryExpression) {}
override fun exitTryExpression(tryExpression: FirTryExpression) {}
override fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {}
override fun enterFunctionCall(functionCall: FirFunctionCall) {}
override fun exitFunctionCall(functionCall: FirFunctionCall) {}
override fun exitConstExpresion(constExpression: FirConstExpression<*>) {}
override fun exitVariableDeclaration(variable: FirVariable<*>) {}
override fun exitVariableAssignment(assignment: FirVariableAssignment) {}
override fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {}
override fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {}
override fun enterAnnotationCall(annotationCall: FirAnnotationCall) {}
override fun exitAnnotationCall(annotationCall: FirAnnotationCall) {}
override fun enterInitBlock(initBlock: FirAnonymousInitializer) {}
override fun exitInitBlock(initBlock: FirAnonymousInitializer) {}
}
@@ -5,97 +5,664 @@
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirResolvedCallableReference
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraph
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.dfa.Condition.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.transformers.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
abstract class FirDataFlowAnalyzer {
abstract fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>?
class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveComponents by transformer {
companion object {
private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not"))
}
private val context: DataFlowInferenceContext get() = inferenceComponents.ctx as DataFlowInferenceContext
private val graphBuilder = ControlFlowGraphBuilder()
private val logicSystem = LogicSystem(context)
private val variableStorage = DataFlowVariableStorage()
private val edges = mutableMapOf<CFGNode<*>, Flow>().withDefault { Flow.EMPTY }
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>? {
val symbol: FirBasedSymbol<*> = qualifiedAccessExpression.resolvedSymbol ?: return null
val variable = variableStorage[symbol]?.real ?: return null
return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null
}
// ----------------------------------- Named function -----------------------------------
abstract fun enterFunction(function: FirFunction<*>)
abstract fun exitFunction(function: FirFunction<*>): ControlFlowGraph?
fun enterFunction(function: FirFunction<*>) {
graphBuilder.enterFunction(function).passFlow()
for (valueParameter in function.valueParameters) {
getRealVariable(valueParameter.symbol)
}
}
fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
val (node, graph) = graphBuilder.exitFunction(function)
node.passFlow()
for (valueParameter in function.valueParameters) {
variableStorage.removeRealVariable(valueParameter.symbol)
}
if (graphBuilder.isTopLevel()) {
edges.clear()
variableStorage.reset()
}
return graph
}
// ----------------------------------- Property -----------------------------------
abstract fun enterProperty(property: FirProperty)
abstract fun exitProperty(property: FirProperty): ControlFlowGraph
fun enterProperty(property: FirProperty) {
graphBuilder.enterProperty(property).passFlow()
}
fun exitProperty(property: FirProperty): ControlFlowGraph {
val (node, graph) = graphBuilder.exitProperty(property)
node.passFlow()
return graph
}
// ----------------------------------- Block -----------------------------------
abstract fun enterBlock(block: FirBlock)
abstract fun exitBlock(block: FirBlock)
fun enterBlock(block: FirBlock) {
val node = graphBuilder.enterBlock(block).passFlow(false)
val previousNode = node.usefulPreviousNodes.singleOrNull() as? WhenBranchConditionExitNode
if (previousNode != null) {
node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqTrue, node.flow)
}
node.flow.freeze()
}
fun exitBlock(block: FirBlock) {
graphBuilder.exitBlock(block).passFlow()
}
// ----------------------------------- Operator call -----------------------------------
abstract fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall)
abstract fun exitOperatorCall(operatorCall: FirOperatorCall)
private fun FirExpression.getResolvedSymbol(): FirCallableSymbol<*>? {
val expression = (this as? FirWhenSubjectExpression)?.whenSubject?.whenExpression?.let {
it.subjectVariable?.symbol?.let { return it }
it.subject
} ?: this
return expression.toResolvedCallableSymbol() as? FirCallableSymbol<*>
}
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false)
try {
if (typeOperatorCall.operation !in FirOperation.TYPES) return
val symbol: FirCallableSymbol<*> = typeOperatorCall.argument.getResolvedSymbol() ?: return
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
val varVariable = getRealVariable(symbol)
var flow = node.flow
when (typeOperatorCall.operation) {
FirOperation.IS, FirOperation.NOT_IS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall)
val trueInfo = FirDataFlowInfo(setOf(type), emptySet())
val falseInfo = FirDataFlowInfo(emptySet(), setOf(type))
fun chooseInfo(trueBranch: Boolean) =
if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqTrue, varVariable, chooseInfo(true)
)
)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqFalse, varVariable, chooseInfo(false)
)
)
}
FirOperation.AS -> {
flow = flow.addApprovedFact(varVariable, FirDataFlowInfo(setOf(type), emptySet()))
}
FirOperation.SAFE_AS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet())
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type))
)
)
}
else -> throw IllegalStateException()
}
node.flow = flow
} finally {
node.flow.freeze()
}
}
fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false)
try {
when (val operation = operatorCall.operation) {
FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> {
val leftOperand = operatorCall.arguments[0]
val rightOperand = operatorCall.arguments[1]
val leftConst = leftOperand as? FirConstExpression<*>
val rightConst = rightOperand as? FirConstExpression<*>
when {
leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation)
rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation)
leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation)
rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation)
operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return
else -> processEq(node, leftOperand, rightOperand, operation)
}
}
}
} finally {
node.flow.freeze()
}
}
private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) {
val isEq = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> true
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false
else -> return
}
val expressionVariable = getVariable(node.fir)
var flow = node.flow
// not null for comparisons with constants
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
isEq.toEqBoolean(),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
}
}
// propagating facts for (... == true) and (... == false)
variableStorage[operand]?.let { operandVariable ->
if (const.kind != IrConstKind.Boolean) return@let
val constValue = (const.value as Boolean)
val shouldInvert = isEq xor constValue
flow.notApprovedFacts[operandVariable].forEach { info ->
flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it })
}
}
node.flow = flow
}
private fun processEq(node: OperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation) {
val leftType = leftOperand.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
val rightType = rightOperand.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
when {
leftType.isMarkedNullable && rightType.isMarkedNullable -> return
leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ)
rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ)
}
// TODO: process EQUALITY
}
private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) {
var flow = node.flow
val expressionVariable = getVariable(node.fir)
variableStorage[operand]?.let { operandVariable ->
val condition = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> EqNull
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> NotEqNull
else -> throw IllegalArgumentException()
}
val facts = logicSystem.approveFact(operandVariable, condition, flow)
facts.forEach { (variable, info) ->
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqTrue, variable, info
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqFalse, variable, info.invert()
)
)
}
node.flow = flow
return
}
val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return
val condition = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> EqFalse
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> EqTrue
else -> throw IllegalArgumentException()
}
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
condition,
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
// TODO: design do we need casts to Nothing?
/*
flow = addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
eq(conditionValue.invert()!!),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet())
)
)
*/
}
node.flow = flow
}
// ----------------------------------- Jump -----------------------------------
abstract fun exitJump(jump: FirJump<*>)
fun exitJump(jump: FirJump<*>) {
graphBuilder.exitJump(jump).passFlow()
}
// ----------------------------------- When -----------------------------------
abstract fun enterWhenExpression(whenExpression: FirWhenExpression)
abstract fun enterWhenBranchCondition(whenBranch: FirWhenBranch)
abstract fun exitWhenBranchCondition(whenBranch: FirWhenBranch)
abstract fun exitWhenBranchResult(whenBranch: FirWhenBranch)
abstract fun exitWhenExpression(whenExpression: FirWhenExpression)
fun enterWhenExpression(whenExpression: FirWhenExpression) {
graphBuilder.enterWhenExpression(whenExpression).passFlow()
}
fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.enterWhenBranchCondition(whenBranch).passFlow(false)
val previousNode = node.previousNodes.single()
if (previousNode is WhenBranchConditionExitNode) {
node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqFalse, node.flow)
}
node.flow.freeze()
}
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow()
val conditionVariable = getVariable(whenBranch.condition)
node.variable = conditionVariable
}
fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false)
val conditionVariable = getVariable(whenBranch.condition)
node.flow = node.flow.removeSyntheticVariable(conditionVariable)
}
fun exitWhenExpression(whenExpression: FirWhenExpression) {
val node = graphBuilder.exitWhenExpression(whenExpression)
var flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow })
val subjectSymbol = whenExpression.subjectVariable?.symbol
if (subjectSymbol != null) {
variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) }
}
node.flow = flow
}
// ----------------------------------- While Loop -----------------------------------
abstract fun enterWhileLoop(loop: FirLoop)
abstract fun exitWhileLoopCondition(loop: FirLoop)
abstract fun exitWhileLoop(loop: FirLoop)
fun enterWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.passFlow()
loopConditionEnterNode.passFlow()
}
fun exitWhileLoopCondition(loop: FirLoop) {
graphBuilder.exitWhileLoopCondition(loop).passFlow()
}
fun exitWhileLoop(loop: FirLoop) {
graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) ->
blockExitNode.passFlow()
exitNode.passFlow()
}
}
// ----------------------------------- Do while Loop -----------------------------------
abstract fun enterDoWhileLoop(loop: FirLoop)
abstract fun enterDoWhileLoopCondition(loop: FirLoop)
abstract fun exitDoWhileLoop(loop: FirLoop)
fun enterDoWhileLoop(loop: FirLoop) {
graphBuilder.enterDoWhileLoop(loop).passFlow()
}
fun enterDoWhileLoopCondition(loop: FirLoop) {
val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop)
loopBlockExitNode.passFlow()
loopConditionEnterNode.passFlow()
}
fun exitDoWhileLoop(loop: FirLoop) {
graphBuilder.exitDoWhileLoop(loop).passFlow()
}
// ----------------------------------- Try-catch-finally -----------------------------------
abstract fun enterTryExpression(tryExpression: FirTryExpression)
abstract fun exitTryMainBlock(tryExpression: FirTryExpression)
abstract fun enterCatchClause(catch: FirCatch)
abstract fun exitCatchClause(catch: FirCatch)
abstract fun enterFinallyBlock(tryExpression: FirTryExpression)
abstract fun exitFinallyBlock(tryExpression: FirTryExpression)
abstract fun exitTryExpression(tryExpression: FirTryExpression)
fun enterTryExpression(tryExpression: FirTryExpression) {
graphBuilder.enterTryExpression(tryExpression).passFlow()
}
fun exitTryMainBlock(tryExpression: FirTryExpression) {
graphBuilder.exitTryMainBlock(tryExpression).passFlow()
}
fun enterCatchClause(catch: FirCatch) {
graphBuilder.enterCatchClause(catch).passFlow()
}
fun exitCatchClause(catch: FirCatch) {
graphBuilder.exitCatchClause(catch).passFlow()
}
fun enterFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.enterFinallyBlock(tryExpression).passFlow()
}
fun exitFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitFinallyBlock(tryExpression).passFlow()
}
fun exitTryExpression(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitTryExpression(tryExpression).passFlow()
}
// ----------------------------------- Resolvable call -----------------------------------
abstract fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression)
abstract fun enterFunctionCall(functionCall: FirFunctionCall)
abstract fun exitFunctionCall(functionCall: FirFunctionCall)
abstract fun exitConstExpresion(constExpression: FirConstExpression<*>)
abstract fun exitVariableDeclaration(variable: FirVariable<*>)
abstract fun exitVariableAssignment(assignment: FirVariableAssignment)
abstract fun exitThrowExceptionNode(throwExpression: FirThrowExpression)
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).passFlow()
}
fun enterFunctionCall(functionCall: FirFunctionCall) {
// TODO: add processing in-place lambdas
}
fun exitFunctionCall(functionCall: FirFunctionCall) {
val node = graphBuilder.exitFunctionCall(functionCall).passFlow(false)
if (functionCall.isBooleanNot()) {
exitBooleanNot(functionCall, node)
return
}
}
private val FirElement.resolvedSymbol: FirBasedSymbol<*>? get() = this.safeAs<FirResolvable>()?.calleeReference.safeAs<FirResolvedCallableReference>()?.coneSymbol as? FirBasedSymbol<*>
private fun FirFunctionCall.isBooleanNot(): Boolean {
val symbol = calleeReference.safeAs<FirResolvedCallableReference>()?.coneSymbol as? FirNamedFunctionSymbol ?: return false
return symbol.callableId == KOTLIN_BOOLEAN_NOT
}
fun exitConstExpresion(constExpression: FirConstExpression<*>) {
graphBuilder.exitConstExpresion(constExpression).passFlow()
}
fun exitVariableDeclaration(variable: FirVariable<*>) {
val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false)
try {
val initializer = variable.initializer ?: return
/*
* That part is needed for cases like that:
*
* val b = x is String
* ...
* if (b) {
* x.length
* }
*/
variableStorage[initializer]?.let { initializerVariable ->
assert(initializerVariable.isSynthetic)
val realVariable = getRealVariable(variable.symbol)
node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable)
}
initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> ->
val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return
variableStorage.createAliasVariable(variable.symbol, rhsVariable)
}
} finally {
node.flow.freeze()
}
}
fun exitVariableAssignment(assignment: FirVariableAssignment) {
graphBuilder.exitVariableAssignment(assignment).passFlow()
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()
}
// ----------------------------------- Boolean operators -----------------------------------
abstract fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression)
abstract fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression)
fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryAnd(binaryLogicExpression).passFlow()
}
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
leftNode.passFlow()
rightNode.passFlow(false)
val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqTrue, rightNode.flow).also { it.freeze() }
}
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).passFlow(false)
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow
val flowFromRight = node.rightOperandNode.flow
val flow = node.flow
val andVariable = getVariable(binaryLogicExpression)
val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromRight)
val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromRight)
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
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))
}
}
logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
}
}
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() }
}
fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryOr(binaryLogicExpression)
}
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
leftNode.passFlow()
rightNode.passFlow(false)
val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqFalse, rightNode.flow).also { it.freeze() }
}
fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryOr(binaryLogicExpression).passFlow(false)
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow
val flowFromRight = node.rightOperandNode.flow
val flow = node.flow
val orVariable = getVariable(binaryLogicExpression)
val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromLeft)
val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromLeft)
val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight)
val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight)
logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info))
}
}
logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
}
}
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() }
}
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = getVariable(node.previousNodes.first().fir)
val variable = getVariable(functionCall)
node.flow = node.flow.copyNotApprovedFacts(booleanExpressionVariable, variable) { it.invert() }.also { it.freeze() }
}
// ----------------------------------- Annotations -----------------------------------
abstract fun enterAnnotationCall(annotationCall: FirAnnotationCall)
abstract fun exitAnnotationCall(annotationCall: FirAnnotationCall)
fun enterAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.enterAnnotationCall(annotationCall).passFlow()
}
fun exitAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.exitAnnotationCall(annotationCall).passFlow()
}
// ----------------------------------- Init block -----------------------------------
abstract fun enterInitBlock(initBlock: FirAnonymousInitializer)
abstract fun exitInitBlock(initBlock: FirAnonymousInitializer)
}
fun enterInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.enterInitBlock(initBlock)
}
fun exitInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.exitInitBlock(initBlock).passFlow()
}
// -------------------------------------------------------------------------------------------------------------------------
private fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap<DataFlowVariable, FirDataFlowInfo>? =
logicSystem.approveFact(variable, condition, flow)
private fun FirBinaryLogicExpression.getVariables(): Pair<DataFlowVariable, DataFlowVariable> =
getVariable(leftOperand) to getVariable(rightOperand)
private var CFGNode<*>.flow: Flow
get() = edges.getValue(this)
set(value) {
edges[this] = value
}
private fun <T : CFGNode<*>> T.passFlow(shouldFreeze: Boolean = true): T = this.also { node ->
node.flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }).also {
if (shouldFreeze) it.freeze()
}
}
private fun getSyntheticVariable(fir: FirElement): DataFlowVariable = variableStorage.getOrCreateNewSyntheticVariable(fir)
private fun getRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = variableStorage.getOrCreateNewRealVariable(symbol).real
private fun getVariable(fir: FirElement): DataFlowVariable {
val symbol = fir.resolvedSymbol
return if (symbol == null)
getSyntheticVariable(fir)
else
getRealVariable(symbol)
}
private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection<DataFlowVariable> {
val result = mutableListOf<DataFlowVariable>()
fun collect(call: FirExpression) {
when (call) {
is FirQualifiedAccess -> {
if (call.safe) {
val explicitReceiver = call.explicitReceiver
require(explicitReceiver != null)
collect(explicitReceiver)
}
((call.calleeReference as? FirResolvedCallableReference)?.coneSymbol)?.let { symbol ->
if (symbol is FirVariableSymbol<*> || symbol is FirPropertySymbol) {
result += getRealVariable(symbol as FirBasedSymbol<*>)
}
}
}
is FirWhenSubjectExpression -> {
call.whenSubject.whenExpression.subjectVariable?.let { result += getRealVariable(it.symbol) }
call.whenSubject.whenExpression.subject?.let { collect(it) }
}
}
}
collect(call)
return result
}
private fun Flow.removeVariable(variable: DataFlowVariable): Flow {
variableStorage.removeVariable(variable)
return removeVariableFromFlow(variable)
}
private fun Flow.removeSyntheticVariable(variable: DataFlowVariable): Flow {
if (!variable.isSynthetic) return this
return removeVariable(variable)
}
}
@@ -1,667 +0,0 @@
/*
* Copyright 2010-2019 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirResolvedCallableReference
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.dfa.Condition.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.transformers.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.*
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class FirDataFlowAnalyzerImpl(transformer: FirBodyResolveTransformer) : FirDataFlowAnalyzer(), BodyResolveComponents by transformer {
companion object {
private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not"))
}
private val context: DataFlowInferenceContext get() = inferenceComponents.ctx as DataFlowInferenceContext
private val graphBuilder = ControlFlowGraphBuilder()
private val logicSystem = LogicSystem(context)
private val variableStorage = DataFlowVariableStorage()
private val edges = mutableMapOf<CFGNode<*>, Flow>().withDefault { Flow.EMPTY }
override fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection<ConeKotlinType>? {
val symbol: FirBasedSymbol<*> = qualifiedAccessExpression.resolvedSymbol ?: return null
val variable = variableStorage[symbol]?.real ?: return null
return graphBuilder.lastNode.flow.approvedFacts(variable)?.exactType ?: return null
}
// ----------------------------------- Named function -----------------------------------
override fun enterFunction(function: FirFunction<*>) {
graphBuilder.enterFunction(function).passFlow()
for (valueParameter in function.valueParameters) {
getRealVariable(valueParameter.symbol)
}
}
override fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
val (node, graph) = graphBuilder.exitFunction(function)
node.passFlow()
for (valueParameter in function.valueParameters) {
variableStorage.removeRealVariable(valueParameter.symbol)
}
if (graphBuilder.isTopLevel()) {
edges.clear()
variableStorage.reset()
}
return graph
}
// ----------------------------------- Property -----------------------------------
override fun enterProperty(property: FirProperty) {
graphBuilder.enterProperty(property).passFlow()
}
override fun exitProperty(property: FirProperty): ControlFlowGraph {
val (node, graph) = graphBuilder.exitProperty(property)
node.passFlow()
return graph
}
// ----------------------------------- Block -----------------------------------
override fun enterBlock(block: FirBlock) {
val node = graphBuilder.enterBlock(block).passFlow(false)
val previousNode = node.usefulPreviousNodes.singleOrNull() as? WhenBranchConditionExitNode
if (previousNode != null) {
node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqTrue, node.flow)
}
node.flow.freeze()
}
override fun exitBlock(block: FirBlock) {
graphBuilder.exitBlock(block).passFlow()
}
// ----------------------------------- Operator call -----------------------------------
private fun FirExpression.getResolvedSymbol(): FirCallableSymbol<*>? {
val expression = (this as? FirWhenSubjectExpression)?.whenSubject?.whenExpression?.let {
it.subjectVariable?.symbol?.let { return it }
it.subject
} ?: this
return expression.toResolvedCallableSymbol() as? FirCallableSymbol<*>
}
override fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).passFlow(false)
try {
if (typeOperatorCall.operation !in FirOperation.TYPES) return
val symbol: FirCallableSymbol<*> = typeOperatorCall.argument.getResolvedSymbol() ?: return
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
val varVariable = getRealVariable(symbol)
var flow = node.flow
when (typeOperatorCall.operation) {
FirOperation.IS, FirOperation.NOT_IS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall)
val trueInfo = FirDataFlowInfo(setOf(type), emptySet())
val falseInfo = FirDataFlowInfo(emptySet(), setOf(type))
fun chooseInfo(trueBranch: Boolean) = if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqTrue, varVariable, chooseInfo(true)
)
)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqFalse, varVariable, chooseInfo(false)
)
)
}
FirOperation.AS -> {
flow = flow.addApprovedFact(varVariable, FirDataFlowInfo(setOf(type), emptySet()))
}
FirOperation.SAFE_AS -> {
val expressionVariable = getSyntheticVariable(typeOperatorCall)
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
NotEqNull, varVariable, FirDataFlowInfo(setOf(type), emptySet())
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqNull, varVariable, FirDataFlowInfo(emptySet(), setOf(type))
)
)
}
else -> throw IllegalStateException()
}
node.flow = flow
} finally {
node.flow.freeze()
}
}
override fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).passFlow(false)
try {
when (val operation = operatorCall.operation) {
FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> {
val leftOperand = operatorCall.arguments[0]
val rightOperand = operatorCall.arguments[1]
val leftConst = leftOperand as? FirConstExpression<*>
val rightConst = rightOperand as? FirConstExpression<*>
when {
leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation)
rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation)
leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation)
rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation)
operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return
else -> processEq(node, leftOperand, rightOperand, operation)
}
}
}
} finally {
node.flow.freeze()
}
}
private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) {
val isEq = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> true
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false
else -> return
}
val expressionVariable = getVariable(node.fir)
var flow = node.flow
// not null for comparisons with constants
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
isEq.toEqBoolean(),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
}
}
// propagating facts for (... == true) and (... == false)
variableStorage[operand]?.let { operandVariable ->
if (const.kind != IrConstKind.Boolean) return@let
val constValue = (const.value as Boolean)
val shouldInvert = isEq xor constValue
flow.notApprovedFacts[operandVariable].forEach { info ->
flow = flow.addNotApprovedFact(expressionVariable, info.let { if (shouldInvert) it.invert() else it })
}
}
node.flow = flow
}
private fun processEq(node: OperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation) {
val leftType = leftOperand.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
val rightType = rightOperand.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
when {
leftType.isMarkedNullable && rightType.isMarkedNullable -> return
leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ)
rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ)
}
// TODO: process EQUALITY
}
private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) {
var flow = node.flow
val expressionVariable = getVariable(node.fir)
variableStorage[operand]?.let { operandVariable ->
val condition = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> EqNull
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> NotEqNull
else -> throw IllegalArgumentException()
}
val facts = logicSystem.approveFact(operandVariable, condition, flow)
facts.forEach { (variable, info) ->
flow = flow.addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqTrue, variable, info
)
).addNotApprovedFact(
expressionVariable,
UnapprovedFirDataFlowInfo(
EqFalse, variable, info.invert()
)
)
}
node.flow = flow
return
}
val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return
val condition = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> EqFalse
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> EqTrue
else -> throw IllegalArgumentException()
}
operandVariables.forEach { operandVariable ->
flow = flow.addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
condition,
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
// TODO: design do we need casts to Nothing?
/*
flow = addNotApprovedFact(
expressionVariable, UnapprovedFirDataFlowInfo(
eq(conditionValue.invert()!!),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet())
)
)
*/
}
node.flow = flow
}
// ----------------------------------- Jump -----------------------------------
override fun exitJump(jump: FirJump<*>) {
graphBuilder.exitJump(jump).passFlow()
}
// ----------------------------------- When -----------------------------------
override fun enterWhenExpression(whenExpression: FirWhenExpression) {
graphBuilder.enterWhenExpression(whenExpression).passFlow()
}
override fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.enterWhenBranchCondition(whenBranch).passFlow(false)
val previousNode = node.previousNodes.single()
if (previousNode is WhenBranchConditionExitNode) {
node.flow = logicSystem.approveFactsInsideFlow(previousNode.variable, EqFalse, node.flow)
}
node.flow.freeze()
}
override fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchCondition(whenBranch).passFlow()
val conditionVariable = getVariable(whenBranch.condition)
node.variable = conditionVariable
}
override fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
val node = graphBuilder.exitWhenBranchResult(whenBranch).passFlow(false)
val conditionVariable = getVariable(whenBranch.condition)
node.flow = node.flow.removeSyntheticVariable(conditionVariable)
}
override fun exitWhenExpression(whenExpression: FirWhenExpression) {
val node = graphBuilder.exitWhenExpression(whenExpression)
var flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow })
val subjectSymbol = whenExpression.subjectVariable?.symbol
if (subjectSymbol != null) {
variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) }
}
node.flow = flow
}
// ----------------------------------- While Loop -----------------------------------
override fun enterWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.passFlow()
loopConditionEnterNode.passFlow()
}
override fun exitWhileLoopCondition(loop: FirLoop) {
graphBuilder.exitWhileLoopCondition(loop).passFlow()
}
override fun exitWhileLoop(loop: FirLoop) {
graphBuilder.exitWhileLoop(loop).also { (blockExitNode, exitNode) ->
blockExitNode.passFlow()
exitNode.passFlow()
}
}
// ----------------------------------- Do while Loop -----------------------------------
override fun enterDoWhileLoop(loop: FirLoop) {
graphBuilder.enterDoWhileLoop(loop).passFlow()
}
override fun enterDoWhileLoopCondition(loop: FirLoop) {
val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop)
loopBlockExitNode.passFlow()
loopConditionEnterNode.passFlow()
}
override fun exitDoWhileLoop(loop: FirLoop) {
graphBuilder.exitDoWhileLoop(loop).passFlow()
}
// ----------------------------------- Try-catch-finally -----------------------------------
override fun enterTryExpression(tryExpression: FirTryExpression) {
graphBuilder.enterTryExpression(tryExpression).passFlow()
}
override fun exitTryMainBlock(tryExpression: FirTryExpression) {
graphBuilder.exitTryMainBlock(tryExpression).passFlow()
}
override fun enterCatchClause(catch: FirCatch) {
graphBuilder.enterCatchClause(catch).passFlow()
}
override fun exitCatchClause(catch: FirCatch) {
graphBuilder.exitCatchClause(catch).passFlow()
}
override fun enterFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.enterFinallyBlock(tryExpression).passFlow()
}
override fun exitFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitFinallyBlock(tryExpression).passFlow()
}
override fun exitTryExpression(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitTryExpression(tryExpression).passFlow()
}
// ----------------------------------- Resolvable call -----------------------------------
override fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).passFlow()
}
override fun enterFunctionCall(functionCall: FirFunctionCall) {
// TODO: add processing in-place lambdas
}
override fun exitFunctionCall(functionCall: FirFunctionCall) {
val node = graphBuilder.exitFunctionCall(functionCall).passFlow(false)
if (functionCall.isBooleanNot()) {
exitBooleanNot(functionCall, node)
return
}
}
private val FirElement.resolvedSymbol: FirBasedSymbol<*>? get() = this.safeAs<FirResolvable>()?.calleeReference.safeAs<FirResolvedCallableReference>()?.coneSymbol as? FirBasedSymbol<*>
private fun FirFunctionCall.isBooleanNot(): Boolean {
val symbol = calleeReference.safeAs<FirResolvedCallableReference>()?.coneSymbol as? FirNamedFunctionSymbol ?: return false
return symbol.callableId == KOTLIN_BOOLEAN_NOT
}
override fun exitConstExpresion(constExpression: FirConstExpression<*>) {
graphBuilder.exitConstExpresion(constExpression).passFlow()
}
override fun exitVariableDeclaration(variable: FirVariable<*>) {
val node = graphBuilder.exitVariableDeclaration(variable).passFlow(false)
try {
val initializer = variable.initializer ?: return
/*
* That part is needed for cases like that:
*
* val b = x is String
* ...
* if (b) {
* x.length
* }
*/
variableStorage[initializer]?.let { initializerVariable ->
assert(initializerVariable.isSynthetic)
val realVariable = getRealVariable(variable.symbol)
node.flow = node.flow.copyNotApprovedFacts(initializerVariable, realVariable)
}
initializer.resolvedSymbol?.let { initializerSymbol: FirBasedSymbol<*> ->
val rhsVariable = variableStorage[initializerSymbol]?.takeIf { !it.isSynthetic } ?: return
variableStorage.createAliasVariable(variable.symbol, rhsVariable)
}
} finally {
node.flow.freeze()
}
}
override fun exitVariableAssignment(assignment: FirVariableAssignment) {
graphBuilder.exitVariableAssignment(assignment).passFlow()
val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return
val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic } ?: return
variableStorage.rebindAliasVariable(lhsVariable, rhsVariable)
}
override fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
graphBuilder.exitThrowExceptionNode(throwExpression).passFlow()
}
// ----------------------------------- Boolean operators -----------------------------------
override fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryAnd(binaryLogicExpression).passFlow()
}
override fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
leftNode.passFlow()
rightNode.passFlow(false)
val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqTrue, rightNode.flow).also { it.freeze() }
}
override fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression).passFlow(false)
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow
val flowFromRight = node.rightOperandNode.flow
val flow = node.flow
val andVariable = getVariable(binaryLogicExpression)
val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromRight)
val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromRight)
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
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))
}
}
logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(andVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
}
}
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() }
}
override fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryOr(binaryLogicExpression)
}
override fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
leftNode.passFlow()
rightNode.passFlow(false)
val leftOperandVariable = getVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(leftOperandVariable, EqFalse, rightNode.flow).also { it.freeze() }
}
override fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryOr(binaryLogicExpression).passFlow(false)
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val flowFromLeft = node.leftOperandNode.flow
val flowFromRight = node.rightOperandNode.flow
val flow = node.flow
val orVariable = getVariable(binaryLogicExpression)
val leftIsTrue = approveFact(leftVariable, EqTrue, flowFromLeft)
val leftIsFalse = approveFact(leftVariable, EqFalse, flowFromLeft)
val rightIsTrue = approveFact(rightVariable, EqTrue, flowFromRight)
val rightIsFalse = approveFact(rightVariable, EqFalse, flowFromRight)
logicSystem.orForVerifiedFacts(leftIsTrue, rightIsTrue)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqTrue, variable, info))
}
}
logicSystem.andForVerifiedFacts(leftIsFalse, rightIsFalse)?.let {
for ((variable, info) in it) {
flow.addNotApprovedFact(orVariable, UnapprovedFirDataFlowInfo(EqFalse, variable, info))
}
}
node.flow = flow.removeSyntheticVariable(leftVariable).removeSyntheticVariable(rightVariable).also { it.freeze() }
}
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = getVariable(node.previousNodes.first().fir)
val variable = getVariable(functionCall)
node.flow = node.flow.copyNotApprovedFacts(booleanExpressionVariable, variable) { it.invert() }.also { it.freeze() }
}
// ----------------------------------- Annotations -----------------------------------
override fun enterAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.enterAnnotationCall(annotationCall).passFlow()
}
override fun exitAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.exitAnnotationCall(annotationCall).passFlow()
}
// ----------------------------------- Init block -----------------------------------
override fun enterInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.enterInitBlock(initBlock)
}
override fun exitInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.exitInitBlock(initBlock).passFlow()
}
// -------------------------------------------------------------------------------------------------------------------------
private fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableMap<DataFlowVariable, FirDataFlowInfo>? =
logicSystem.approveFact(variable, condition, flow)
private fun FirBinaryLogicExpression.getVariables(): Pair<DataFlowVariable, DataFlowVariable> =
getVariable(leftOperand) to getVariable(rightOperand)
private var CFGNode<*>.flow: Flow
get() = edges.getValue(this)
set(value) {
edges[this] = value
}
private fun <T : CFGNode<*>> T.passFlow(shouldFreeze: Boolean = true): T = this.also { node ->
node.flow = logicSystem.or(node.usefulPreviousNodes.map { it.flow }).also {
if (shouldFreeze) it.freeze()
}
}
private fun getSyntheticVariable(fir: FirElement): DataFlowVariable = variableStorage.getOrCreateNewSyntheticVariable(fir)
private fun getRealVariable(symbol: FirBasedSymbol<*>): DataFlowVariable = variableStorage.getOrCreateNewRealVariable(symbol).real
private fun getVariable(fir: FirElement): DataFlowVariable {
val symbol = fir.resolvedSymbol
return if (symbol == null)
getSyntheticVariable(fir)
else
getRealVariable(symbol)
}
private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection<DataFlowVariable> {
val result = mutableListOf<DataFlowVariable>()
fun collect(call: FirExpression) {
when (call) {
is FirQualifiedAccess -> {
if (call.safe) {
val explicitReceiver = call.explicitReceiver
require(explicitReceiver != null)
collect(explicitReceiver)
}
((call.calleeReference as? FirResolvedCallableReference)?.coneSymbol)?.let { symbol ->
if (symbol is FirVariableSymbol<*> || symbol is FirPropertySymbol) {
result += getRealVariable(symbol as FirBasedSymbol<*>)
}
}
}
is FirWhenSubjectExpression -> {
call.whenSubject.whenExpression.subjectVariable?.let { result += getRealVariable(it.symbol) }
call.whenSubject.whenExpression.subject?.let { collect(it) }
}
}
}
collect(call)
return result
}
private fun Flow.removeVariable(variable: DataFlowVariable): Flow {
variableStorage.removeVariable(variable)
return removeVariableFromFlow(variable)
}
private fun Flow.removeSyntheticVariable(variable: DataFlowVariable): Flow {
if (!variable.isSynthetic) return this
return removeVariable(variable)
}
}
@@ -18,7 +18,6 @@ import org.jetbrains.kotlin.fir.resolve.*
import org.jetbrains.kotlin.fir.resolve.calls.*
import org.jetbrains.kotlin.fir.resolve.dfa.DataFlowInferenceContext
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer
import org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzerImpl
import org.jetbrains.kotlin.fir.resolve.inference.FirCallCompleter
import org.jetbrains.kotlin.fir.scopes.FirScope
import org.jetbrains.kotlin.fir.scopes.addImportingScopes
@@ -86,7 +85,7 @@ open class FirBodyResolveTransformer(
)
private val syntheticCallGenerator: FirSyntheticCallGenerator = FirSyntheticCallGenerator(this)
private val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzerImpl(this)
private val dataFlowAnalyzer: FirDataFlowAnalyzer = FirDataFlowAnalyzer(this)
override val <D> AbstractFirBasedSymbol<D>.phasedFir: D where D : FirDeclaration, D : FirSymbolOwner<D>
get() {