[FIR] Remove old DFA
This commit is contained in:
@@ -1,141 +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.symbols.AbstractFirBasedSymbol
|
||||
import kotlin.contracts.ExperimentalContracts
|
||||
import kotlin.contracts.contract
|
||||
|
||||
/*
|
||||
* isSynthetic = false for variables that represents actual variables in fir
|
||||
* isSynthetic = true for complex expressions (like when expression)
|
||||
*/
|
||||
sealed class DataFlowVariable(private val variableIndexForDebug: Int, val fir: FirElement) {
|
||||
final override fun toString(): String {
|
||||
return "d$variableIndexForDebug"
|
||||
}
|
||||
}
|
||||
|
||||
open class RealDataFlowVariable(index: Int, fir: FirElement, val isThisReference: Boolean) : DataFlowVariable(index, fir)
|
||||
|
||||
class SyntheticDataFlowVariable(index: Int, fir: FirElement) : DataFlowVariable(index, fir)
|
||||
|
||||
class AliasedDataFlowVariable(
|
||||
index: Int,
|
||||
fir: FirElement,
|
||||
var delegate: RealDataFlowVariable
|
||||
) : RealDataFlowVariable(index, fir, delegate.isThisReference)
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
@UseExperimental(ExperimentalContracts::class)
|
||||
fun DataFlowVariable.isSynthetic(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isSynthetic is SyntheticDataFlowVariable)
|
||||
}
|
||||
return this is SyntheticDataFlowVariable
|
||||
}
|
||||
|
||||
@UseExperimental(ExperimentalContracts::class)
|
||||
fun DataFlowVariable.isAliasVariable(): Boolean {
|
||||
contract {
|
||||
returns(true) implies (this@isAliasVariable is AliasedDataFlowVariable)
|
||||
}
|
||||
return this is AliasedDataFlowVariable
|
||||
}
|
||||
|
||||
val RealDataFlowVariable.variableUnderAlias: RealDataFlowVariable
|
||||
get() {
|
||||
var variable = this
|
||||
while (variable.isAliasVariable()) {
|
||||
variable = variable.delegate
|
||||
}
|
||||
return variable
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
class DataFlowVariableStorage {
|
||||
private val fir2DfiMap: MutableMap<FirElement, DataFlowVariable> = mutableMapOf()
|
||||
private var debugIndexCounter: Int = 1
|
||||
|
||||
fun getOrCreateNewRealVariable(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable {
|
||||
return getOrCreateNewRealVariableImpl(symbol, false)
|
||||
}
|
||||
|
||||
fun getOrCreateNewThisRealVariable(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable {
|
||||
return getOrCreateNewRealVariableImpl(symbol, true)
|
||||
}
|
||||
|
||||
private fun getOrCreateNewRealVariableImpl(symbol: AbstractFirBasedSymbol<*>, isThisReference: Boolean): RealDataFlowVariable {
|
||||
val fir = symbol.fir
|
||||
get(fir)?.let { return it as RealDataFlowVariable }
|
||||
return RealDataFlowVariable(debugIndexCounter++, fir, isThisReference).also { storeVariable(it, fir) }
|
||||
}
|
||||
|
||||
fun getOrCreateNewSyntheticVariable(fir: FirElement): SyntheticDataFlowVariable {
|
||||
get(fir)?.let { return it as SyntheticDataFlowVariable }
|
||||
return SyntheticDataFlowVariable(debugIndexCounter++, fir).also { storeVariable(it, fir) }
|
||||
}
|
||||
|
||||
fun createAliasVariable(symbol: AbstractFirBasedSymbol<*>, variable: RealDataFlowVariable) {
|
||||
createAliasVariable(symbol.fir, variable)
|
||||
}
|
||||
|
||||
private fun createAliasVariable(fir: FirElement, variable: RealDataFlowVariable) {
|
||||
AliasedDataFlowVariable(debugIndexCounter++, fir, variable).also { storeVariable(it, fir) }
|
||||
}
|
||||
|
||||
fun rebindAliasVariable(aliasVariable: RealDataFlowVariable, newVariable: RealDataFlowVariable) {
|
||||
val fir = removeVariable(aliasVariable)
|
||||
requireNotNull(fir)
|
||||
createAliasVariable(fir, newVariable)
|
||||
}
|
||||
|
||||
fun removeRealVariable(symbol: AbstractFirBasedSymbol<*>) {
|
||||
removeSyntheticVariable(symbol.fir)
|
||||
}
|
||||
|
||||
fun removeSyntheticVariable(fir: FirElement) {
|
||||
val variable = fir2DfiMap[fir] ?: return
|
||||
removeVariable(variable)
|
||||
}
|
||||
|
||||
fun removeVariable(variable: DataFlowVariable): FirElement? {
|
||||
return variable.fir.also {
|
||||
fir2DfiMap.remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun removeVariableIfSynthetic(variable: DataFlowVariable): FirElement? {
|
||||
if (variable is SyntheticDataFlowVariable) {
|
||||
return removeVariable(variable)
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
operator fun get(variable: DataFlowVariable): FirElement? {
|
||||
return variable.fir
|
||||
}
|
||||
|
||||
operator fun get(firElement: FirElement): DataFlowVariable? {
|
||||
return fir2DfiMap[firElement]
|
||||
}
|
||||
|
||||
operator fun get(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable? {
|
||||
return fir2DfiMap[symbol.fir] as RealDataFlowVariable?
|
||||
}
|
||||
|
||||
fun reset() {
|
||||
fir2DfiMap.clear()
|
||||
debugIndexCounter = 1
|
||||
}
|
||||
|
||||
private fun storeVariable(variable: DataFlowVariable, fir: FirElement) {
|
||||
fir2DfiMap[fir] = variable
|
||||
}
|
||||
}
|
||||
-232
@@ -1,232 +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 com.google.common.collect.ArrayListMultimap
|
||||
import com.google.common.collect.Multimap
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
|
||||
class DelegatingFlow(
|
||||
val previousFlow: DelegatingFlow?,
|
||||
val approvedInfos: MutableApprovedInfos = mutableMapOf(),
|
||||
val conditionalInfos: ConditionalInfos = ArrayListMultimap.create()
|
||||
) : Flow {
|
||||
val level: Int = previousFlow?.level?.plus(1) ?: 0
|
||||
|
||||
override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? {
|
||||
val info = MutableFirDataFlowInfo(mutableSetOf(), mutableSetOf())
|
||||
collect { flow ->
|
||||
flow.approvedInfos[variable]?.let { info += it }
|
||||
true
|
||||
}
|
||||
return info.takeIf { it.isNotEmpty }
|
||||
}
|
||||
|
||||
override fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
|
||||
val result = mutableSetOf<ConditionalFirDataFlowInfo>()
|
||||
val isReal = !variable.isSynthetic()
|
||||
collect {
|
||||
val infos = it.conditionalInfos[variable]
|
||||
result += infos
|
||||
/*
|
||||
* Here we use invariant that if we found some conditional info about synthetic variable
|
||||
* on some level then there is definitely no another info on other levels of flow
|
||||
* It's correct because we can add condtional info about synthetic variable only
|
||||
* when we first met expression corresponding to that variable
|
||||
*/
|
||||
isReal || infos.isEmpty()
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable> {
|
||||
val result = mutableSetOf<RealDataFlowVariable>()
|
||||
collect {
|
||||
result += approvedInfos.keys
|
||||
true
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
override fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
|
||||
return conditionalInfos.removeAll(variable)
|
||||
}
|
||||
|
||||
private inline fun collect(block: (DelegatingFlow) -> Boolean) {
|
||||
var flow: DelegatingFlow? = this
|
||||
while (flow != null) {
|
||||
val shouldContinue = block(flow)
|
||||
if (!shouldContinue) break
|
||||
flow = flow.previousFlow
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun DelegatingFlow.approvedInfosFromTopFlow(): ApprovedInfos {
|
||||
return approvedInfos
|
||||
}
|
||||
|
||||
fun DelegatingFlow.conditionalInfosFromTopFlow(): ConditionalInfos {
|
||||
return ImmutableMultimap(conditionalInfos)
|
||||
}
|
||||
|
||||
private fun DelegatingFlow.approvedInfosUntilParent(parent: DelegatingFlow): ApprovedInfos {
|
||||
return when(level - parent.level) {
|
||||
0 -> emptyMap()
|
||||
1 -> approvedInfosFromTopFlow()
|
||||
else -> mutableMapOf<RealDataFlowVariable, FirDataFlowInfo>().apply {
|
||||
traverse(parent) {
|
||||
it.approvedInfosFromTopFlow().forEach { (variable, info) ->
|
||||
compute(variable) { _, existingInfo ->
|
||||
existingInfo?.plus(info) ?: info
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inline fun DelegatingFlow.traverse(untilFlow: DelegatingFlow, block: (DelegatingFlow) -> Unit) {
|
||||
var flow = this
|
||||
while (flow != untilFlow) {
|
||||
block(flow)
|
||||
flow = flow.previousFlow!!
|
||||
}
|
||||
}
|
||||
|
||||
private class ImmutableMultimap<K, V>(private val original: Multimap<K, V>) : Multimap<K, V> by original {
|
||||
override fun put(p0: K?, p1: V?): Boolean {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun remove(p0: Any?, p1: Any?): Boolean {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun putAll(p0: Multimap<out K, out V>): Boolean {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun putAll(p0: K?, p1: MutableIterable<V>): Boolean {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun removeAll(p0: Any?): MutableCollection<V> {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
|
||||
override fun replaceValues(p0: K?, p1: MutableIterable<V>): MutableCollection<V> {
|
||||
throw IllegalStateException()
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DelegatingLogicSystem(context: ConeInferenceContext) : LogicSystem(context) {
|
||||
override val Flow.approvedInfos: MutableApprovedInfos
|
||||
get() = (this as DelegatingFlow).approvedInfos
|
||||
|
||||
override val Flow.conditionalInfos: ConditionalInfos
|
||||
get() = (this as DelegatingFlow).conditionalInfos
|
||||
|
||||
override fun createEmptyFlow(): Flow {
|
||||
return DelegatingFlow(null)
|
||||
}
|
||||
|
||||
override fun forkFlow(flow: Flow): Flow {
|
||||
require(flow is DelegatingFlow)
|
||||
return DelegatingFlow(flow)
|
||||
}
|
||||
|
||||
override fun collectInfoForBooleanOperator(
|
||||
leftFlow: Flow,
|
||||
leftVariable: DataFlowVariable,
|
||||
rightFlow: Flow,
|
||||
rightVariable: DataFlowVariable
|
||||
): InfoForBooleanOperator {
|
||||
require(leftFlow is DelegatingFlow && rightFlow is DelegatingFlow)
|
||||
val parent = lowestCommonFlow(leftFlow, rightFlow)
|
||||
|
||||
return InfoForBooleanOperator(
|
||||
leftFlow.previousFlow!!.conditionalInfosFromTopFlow()[leftVariable],
|
||||
rightFlow.conditionalInfosFromTopFlow()[rightVariable],
|
||||
rightFlow.approvedInfosUntilParent(parent)
|
||||
)
|
||||
}
|
||||
|
||||
// ------------------------------- Flow operations -------------------------------
|
||||
|
||||
override fun joinFlow(flows: Collection<Flow>): Flow {
|
||||
if (flows.isEmpty()) return createEmptyFlow()
|
||||
flows.singleOrNull()?.let { return it }
|
||||
|
||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
val flows = flows as Collection<DelegatingFlow>
|
||||
val commonFlow = flows.reduce(this::lowestCommonFlow)
|
||||
|
||||
val approvedInfosFromAllFlows = mutableListOf<ApprovedInfos>()
|
||||
flows.forEach {
|
||||
approvedInfosFromAllFlows += it.collectInfos(commonFlow)
|
||||
}
|
||||
|
||||
// approved info
|
||||
val variablesFromApprovedInfos = approvedInfosFromAllFlows.map { it.keys }.intersectSets()
|
||||
for (variable in variablesFromApprovedInfos) {
|
||||
val infos = approvedInfosFromAllFlows.mapNotNull { it[variable] }
|
||||
if (infos.size != flows.size) continue
|
||||
val intersectedInfo = or(infos)
|
||||
if (intersectedInfo.isEmpty) continue
|
||||
val existingInfo = commonFlow.approvedInfos[variable]
|
||||
if (existingInfo == null) {
|
||||
commonFlow.approvedInfos[variable] = intersectedInfo
|
||||
} else {
|
||||
existingInfo += intersectedInfo
|
||||
}
|
||||
}
|
||||
|
||||
updateAllReceivers(commonFlow)
|
||||
|
||||
return commonFlow
|
||||
}
|
||||
|
||||
// ------------------------------- Util functions -------------------------------
|
||||
|
||||
private fun lowestCommonFlow(left: DelegatingFlow, right: DelegatingFlow): DelegatingFlow {
|
||||
val level = minOf(left.level, right.level)
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var left = left
|
||||
while (left.level > level) {
|
||||
left = left.previousFlow!!
|
||||
}
|
||||
@Suppress("NAME_SHADOWING")
|
||||
var right = right
|
||||
while (right.level > level) {
|
||||
right = right.previousFlow!!
|
||||
}
|
||||
while (left != right) {
|
||||
left = left.previousFlow!!
|
||||
right = right.previousFlow!!
|
||||
}
|
||||
return left
|
||||
}
|
||||
|
||||
private fun DelegatingFlow.collectInfos(untilFlow: DelegatingFlow): ApprovedInfos {
|
||||
val approvedInfos: MutableApprovedInfos = mutableMapOf()
|
||||
|
||||
// val conditionalInfos: ConditionalInfos = ArrayListMultimap.create()
|
||||
|
||||
var flow = this
|
||||
while (flow != untilFlow) {
|
||||
flow.approvedInfos.forEach { (variable, info) -> approvedInfos.addInfo(variable, info) }
|
||||
/*
|
||||
* we don't join conditional infos yet
|
||||
flow.conditionalInfos.asMap().forEach { (variable, infos) ->
|
||||
conditionalInfos.putAll(variable, infos)
|
||||
}
|
||||
*/
|
||||
flow = flow.previousFlow!!
|
||||
}
|
||||
return approvedInfos
|
||||
}
|
||||
}
|
||||
@@ -1,920 +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.*
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeBooleanConstantReference
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConditionalEffectDeclaration
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeConstantReference
|
||||
import org.jetbrains.kotlin.fir.contracts.description.ConeReturnsEffectDeclaration
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.expressions.impl.FirThisReceiverExpressionImpl
|
||||
import org.jetbrains.kotlin.fir.references.FirResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirExplicitThisReference
|
||||
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
|
||||
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl
|
||||
import org.jetbrains.kotlin.fir.resolve.ResolutionMode
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.FirNamedReferenceWithCandidate
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.Condition.*
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.buildContractFir
|
||||
import org.jetbrains.kotlin.fir.resolve.dfa.contracts.createArgumentsMapping
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.FirAbstractBodyResolveTransformer
|
||||
import org.jetbrains.kotlin.fir.resolve.transformers.body.resolve.resultType
|
||||
import org.jetbrains.kotlin.fir.resolve.withNullability
|
||||
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.CallableId
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.fir.visitors.transformSingle
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class FirDataFlowAnalyzer(private val components: FirAbstractBodyResolveTransformer.BodyResolveTransformerComponents) : BodyResolveComponents by components {
|
||||
companion object {
|
||||
private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not"))
|
||||
}
|
||||
|
||||
private val context: ConeInferenceContext get() = inferenceComponents.ctx
|
||||
private val receiverStack: ImplicitReceiverStackImpl = components.implicitReceiverStack as ImplicitReceiverStackImpl
|
||||
|
||||
private val graphBuilder = ControlFlowGraphBuilder()
|
||||
private val logicSystem: LogicSystem = LogicSystemImpl(context)
|
||||
private val variableStorage = DataFlowVariableStorage()
|
||||
private val flowOnNodes = mutableMapOf<CFGNode<*>, Flow>()
|
||||
|
||||
private val variablesForWhenConditions = mutableMapOf<WhenBranchConditionExitNode, DataFlowVariable>()
|
||||
|
||||
private var contractDescriptionVisitingMode = false
|
||||
|
||||
/*
|
||||
* 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>? {
|
||||
/*
|
||||
* 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.realVariable?.variableUnderAlias ?: return null
|
||||
return graphBuilder.lastNode.flow.getApprovedInfo(variable)?.exactType ?: return null
|
||||
}
|
||||
|
||||
// ----------------------------------- Named function -----------------------------------
|
||||
|
||||
fun enterFunction(function: FirFunction<*>) {
|
||||
val (functionEnterNode, previousNode) = graphBuilder.enterFunction(function)
|
||||
if (previousNode == null) {
|
||||
functionEnterNode.mergeIncomingFlow()
|
||||
} else {
|
||||
assert(functionEnterNode.previousNodes.isEmpty())
|
||||
functionEnterNode.flow = logicSystem.forkFlow(previousNode.flow)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
|
||||
val (node, graph) = graphBuilder.exitFunction(function)
|
||||
if (function.body == null) {
|
||||
node.mergeIncomingFlow()
|
||||
}
|
||||
for (valueParameter in function.valueParameters) {
|
||||
variableStorage.removeRealVariable(valueParameter.symbol)
|
||||
}
|
||||
if (graphBuilder.isTopLevel()) {
|
||||
flowOnNodes.clear()
|
||||
variableStorage.reset()
|
||||
graphBuilder.reset()
|
||||
}
|
||||
return graph
|
||||
}
|
||||
|
||||
// ----------------------------------- Property -----------------------------------
|
||||
|
||||
fun enterProperty(property: FirProperty) {
|
||||
graphBuilder.enterProperty(property).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitProperty(property: FirProperty): ControlFlowGraph {
|
||||
val (node, graph) = graphBuilder.exitProperty(property)
|
||||
node.mergeIncomingFlow()
|
||||
return graph
|
||||
}
|
||||
|
||||
// ----------------------------------- Block -----------------------------------
|
||||
|
||||
fun enterBlock(block: FirBlock) {
|
||||
graphBuilder.enterBlock(block)?.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitBlock(block: FirBlock) {
|
||||
graphBuilder.exitBlock(block).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
private fun FirElement.extractReturnType(target: FirFunction<*>?): ConeKotlinType? {
|
||||
return when (this) {
|
||||
is FirReturnExpression -> {
|
||||
if (this.target.labeledElement == target) {
|
||||
result.extractReturnType(target)
|
||||
} else {
|
||||
result.resultType.coneTypeSafe()
|
||||
}
|
||||
}
|
||||
is FirExpression -> {
|
||||
typeRef.coneTypeSafe()
|
||||
}
|
||||
else -> session.builtinTypes.unitType.type
|
||||
}
|
||||
}
|
||||
|
||||
fun returnExpressionsOfAnonymousFunction(function: FirAnonymousFunction): List<FirStatement> {
|
||||
return graphBuilder.returnExpressionsOfAnonymousFunction(function)
|
||||
}
|
||||
|
||||
// ----------------------------------- Operator call -----------------------------------
|
||||
|
||||
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
|
||||
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
|
||||
|
||||
if (typeOperatorCall.operation !in FirOperation.TYPES) return
|
||||
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe<ConeKotlinType>() ?: return
|
||||
val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument) ?: return
|
||||
|
||||
val flow = node.flow
|
||||
when (typeOperatorCall.operation) {
|
||||
FirOperation.IS, FirOperation.NOT_IS -> {
|
||||
val expressionVariable = getOrCreateSyntheticVariable(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
|
||||
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable,
|
||||
ConditionalFirDataFlowInfo(
|
||||
EqTrue, operandVariable, chooseInfo(true)
|
||||
)
|
||||
)
|
||||
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable,
|
||||
ConditionalFirDataFlowInfo(
|
||||
EqFalse, operandVariable, chooseInfo(false)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
FirOperation.AS -> {
|
||||
logicSystem.addApprovedInfo(flow, operandVariable, FirDataFlowInfo(setOf(type), emptySet()))
|
||||
}
|
||||
|
||||
FirOperation.SAFE_AS -> {
|
||||
val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall)
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable,
|
||||
ConditionalFirDataFlowInfo(
|
||||
NotEqNull, operandVariable, FirDataFlowInfo(setOf(type), emptySet())
|
||||
)
|
||||
)
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable,
|
||||
ConditionalFirDataFlowInfo(
|
||||
EqNull, operandVariable, FirDataFlowInfo(emptySet(), setOf(type))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> throw IllegalStateException()
|
||||
}
|
||||
|
||||
node.flow = flow
|
||||
}
|
||||
|
||||
fun exitOperatorCall(operatorCall: FirOperatorCall) {
|
||||
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]
|
||||
val rightOperand = operatorCall.arguments[1]
|
||||
|
||||
val leftConst = leftOperand as? FirConstExpression<*>
|
||||
val rightConst = rightOperand as? FirConstExpression<*>
|
||||
|
||||
when {
|
||||
leftConst?.kind == FirConstKind.Null -> processEqNull(node, rightOperand, operation)
|
||||
rightConst?.kind == FirConstKind.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 = getOrCreateVariable(node.fir)
|
||||
val flow = node.flow
|
||||
|
||||
// not null for comparisons with constants
|
||||
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
|
||||
operandVariables.forEach { operandVariable ->
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable, ConditionalFirDataFlowInfo(
|
||||
isEq.toEqBoolean(),
|
||||
operandVariable,
|
||||
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// propagating facts for (... == true) and (... == false)
|
||||
variableStorage[operand]?.let { operandVariable ->
|
||||
if (const.kind != FirConstKind.Boolean) return@let
|
||||
|
||||
val constValue = (const.value as Boolean)
|
||||
val shouldInvert = isEq xor constValue
|
||||
|
||||
flow.getConditionalInfos(operandVariable).forEach { info ->
|
||||
logicSystem.addConditionalInfo(flow, expressionVariable, info.let { if (shouldInvert) it.invert() else it })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
val flow = node.flow
|
||||
val expressionVariable = getOrCreateVariable(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) ->
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable,
|
||||
ConditionalFirDataFlowInfo(
|
||||
EqTrue, variable, info
|
||||
)
|
||||
)
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable,
|
||||
ConditionalFirDataFlowInfo(
|
||||
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 ->
|
||||
logicSystem.addConditionalInfo(
|
||||
flow,
|
||||
expressionVariable, ConditionalFirDataFlowInfo(
|
||||
condition,
|
||||
operandVariable,
|
||||
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
|
||||
)
|
||||
)
|
||||
// TODO: design do we need casts to Nothing?
|
||||
/*
|
||||
flow = addConditionalInfo(
|
||||
expressionVariable, UnapprovedFirDataFlowInfo(
|
||||
eq(conditionValue.invert()!!),
|
||||
operandVariable,
|
||||
FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet())
|
||||
)
|
||||
)
|
||||
*/
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------- Jump -----------------------------------
|
||||
|
||||
fun exitJump(jump: FirJump<*>) {
|
||||
graphBuilder.exitJump(jump).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// ----------------------------------- Check not null call -----------------------------------
|
||||
|
||||
fun exitCheckNotNullCall(checkNotNullCall: FirCheckNotNullCall) {
|
||||
// Add `Any` to the set of possible types; the intersection type `T? & Any` will be reduced to `T` after smartcast.
|
||||
val node = graphBuilder.exitCheckNotNullCall(checkNotNullCall).mergeIncomingFlow()
|
||||
val operandVariable = getOrCreateRealVariable(checkNotNullCall.argument) ?: return
|
||||
logicSystem.addApprovedInfo(
|
||||
node.flow,
|
||||
operandVariable,
|
||||
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
|
||||
)
|
||||
}
|
||||
|
||||
// ----------------------------------- When -----------------------------------
|
||||
|
||||
fun enterWhenExpression(whenExpression: FirWhenExpression) {
|
||||
graphBuilder.enterWhenExpression(whenExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
|
||||
val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow()
|
||||
val previousNode = node.previousNodes.single()
|
||||
if (previousNode is WhenBranchConditionExitNode) {
|
||||
node.flow = logicSystem.approveFactsInsideFlow(
|
||||
variablesForWhenConditions.remove(previousNode)!!,
|
||||
EqFalse,
|
||||
node.flow,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
|
||||
val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch)
|
||||
conditionExitNode.mergeIncomingFlow()
|
||||
|
||||
val conditionVariable = getOrCreateVariable(whenBranch.condition)
|
||||
variablesForWhenConditions[conditionExitNode] = conditionVariable
|
||||
branchEnterNode.flow = logicSystem.approveFactsInsideFlow(
|
||||
conditionVariable,
|
||||
EqTrue,
|
||||
conditionExitNode.flow,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
)
|
||||
}
|
||||
|
||||
fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
|
||||
graphBuilder.exitWhenBranchResult(whenBranch).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitWhenExpression(whenExpression: FirWhenExpression) {
|
||||
val (whenExitNode, syntheticElseNode) = graphBuilder.exitWhenExpression(whenExpression)
|
||||
if (syntheticElseNode != null) {
|
||||
syntheticElseNode.mergeIncomingFlow()
|
||||
val previousConditionExitNode = syntheticElseNode.previousNodes.single() as? WhenBranchConditionExitNode
|
||||
// previous node for syntheticElseNode can be not WhenBranchConditionExitNode in case of `when` without any branches
|
||||
// in that case there will be when enter or subject access node
|
||||
if (previousConditionExitNode != null) {
|
||||
syntheticElseNode.flow = logicSystem.approveFactsInsideFlow(
|
||||
variablesForWhenConditions.remove(previousConditionExitNode)!!,
|
||||
EqFalse,
|
||||
syntheticElseNode.flow,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
val previousFlows = whenExitNode.alivePreviousNodes.map { it.flow }
|
||||
val flow = logicSystem.joinFlow(previousFlows)
|
||||
whenExitNode.flow = flow
|
||||
// TODO
|
||||
// val subjectSymbol = whenExpression.subjectVariable?.symbol
|
||||
// if (subjectSymbol != null) {
|
||||
// variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) }
|
||||
// }
|
||||
// node.flow = flow
|
||||
}
|
||||
|
||||
// ----------------------------------- While Loop -----------------------------------
|
||||
|
||||
fun enterWhileLoop(loop: FirLoop) {
|
||||
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
|
||||
loopEnterNode.mergeIncomingFlow()
|
||||
loopConditionEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitWhileLoopCondition(loop: FirLoop) {
|
||||
val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop)
|
||||
loopConditionExitNode.mergeIncomingFlow()
|
||||
loopBlockEnterNode.mergeIncomingFlow()
|
||||
variableStorage[loop.condition]?.let { conditionVariable ->
|
||||
loopBlockEnterNode.flow = logicSystem.approveFactsInsideFlow(
|
||||
conditionVariable,
|
||||
EqTrue,
|
||||
loopBlockEnterNode.flow,
|
||||
shouldForkFlow = false,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitWhileLoop(loop: FirLoop) {
|
||||
val (blockExitNode, exitNode) = graphBuilder.exitWhileLoop(loop)
|
||||
blockExitNode.mergeIncomingFlow()
|
||||
exitNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// ----------------------------------- Do while Loop -----------------------------------
|
||||
|
||||
fun enterDoWhileLoop(loop: FirLoop) {
|
||||
val (loopEnterNode, loopBlockEnterNode) = graphBuilder.enterDoWhileLoop(loop)
|
||||
loopEnterNode.mergeIncomingFlow()
|
||||
loopBlockEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun enterDoWhileLoopCondition(loop: FirLoop) {
|
||||
val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop)
|
||||
loopBlockExitNode.mergeIncomingFlow()
|
||||
loopConditionEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitDoWhileLoop(loop: FirLoop) {
|
||||
val (loopConditionExitNode, loopExitNode) = graphBuilder.exitDoWhileLoop(loop)
|
||||
loopConditionExitNode.mergeIncomingFlow()
|
||||
loopExitNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// ----------------------------------- Try-catch-finally -----------------------------------
|
||||
|
||||
fun enterTryExpression(tryExpression: FirTryExpression) {
|
||||
val (tryExpressionEnterNode, tryMainBlockEnterNode) = graphBuilder.enterTryExpression(tryExpression)
|
||||
tryExpressionEnterNode.mergeIncomingFlow()
|
||||
tryMainBlockEnterNode.mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitTryMainBlock(tryExpression: FirTryExpression) {
|
||||
graphBuilder.exitTryMainBlock(tryExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun enterCatchClause(catch: FirCatch) {
|
||||
graphBuilder.enterCatchClause(catch).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitCatchClause(catch: FirCatch) {
|
||||
graphBuilder.exitCatchClause(catch).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun enterFinallyBlock(tryExpression: FirTryExpression) {
|
||||
// TODO
|
||||
graphBuilder.enterFinallyBlock(tryExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitFinallyBlock(tryExpression: FirTryExpression) {
|
||||
// TODO
|
||||
graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitTryExpression(tryExpression: FirTryExpression) {
|
||||
// TODO
|
||||
graphBuilder.exitTryExpression(tryExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// ----------------------------------- Resolvable call -----------------------------------
|
||||
|
||||
private fun enterSafeCall(qualifiedAccess: FirQualifiedAccess) {
|
||||
if (!qualifiedAccess.safe) return
|
||||
val node = graphBuilder.enterSafeCall(qualifiedAccess).mergeIncomingFlow()
|
||||
val previousNode = node.alivePreviousNodes.first()
|
||||
val shouldFork: Boolean
|
||||
var flow= if (previousNode is ExitSafeCallNode) {
|
||||
shouldFork = false
|
||||
previousNode.alivePreviousNodes.getOrNull(1)?.flow ?: node.flow
|
||||
} else {
|
||||
shouldFork = true
|
||||
node.flow
|
||||
}
|
||||
qualifiedAccess.explicitReceiver?.let {
|
||||
val type = it.typeRef.coneTypeSafe<ConeKotlinType>()
|
||||
?.takeIf { it.isMarkedNullable }
|
||||
?.withNullability(ConeNullability.NOT_NULL, session.inferenceContext)
|
||||
?: return@let
|
||||
|
||||
when (val variable = getOrCreateVariable(it)) {
|
||||
is RealDataFlowVariable -> {
|
||||
if (shouldFork) {
|
||||
flow = logicSystem.forkFlow(flow)
|
||||
}
|
||||
logicSystem.addApprovedInfo(flow, variable, FirDataFlowInfo(setOf(type), emptySet()))
|
||||
}
|
||||
is SyntheticDataFlowVariable -> {
|
||||
flow = logicSystem.approveFactsInsideFlow(variable, NotEqNull, flow, shouldFork, true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
node.flow = flow
|
||||
}
|
||||
|
||||
private fun exitSafeCall(qualifiedAccess: FirQualifiedAccess) {
|
||||
if (!qualifiedAccess.safe) return
|
||||
graphBuilder.exitSafeCall(qualifiedAccess).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun enterQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
|
||||
enterSafeCall(qualifiedAccessExpression)
|
||||
}
|
||||
|
||||
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
|
||||
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).mergeIncomingFlow()
|
||||
exitSafeCall(qualifiedAccessExpression)
|
||||
}
|
||||
|
||||
fun exitFunctionCall(functionCall: FirFunctionCall) {
|
||||
val node = graphBuilder.exitFunctionCall(functionCall).mergeIncomingFlow()
|
||||
if (functionCall.isBooleanNot()) {
|
||||
exitBooleanNot(functionCall, node)
|
||||
}
|
||||
processConditionalContract(functionCall)
|
||||
if (functionCall.safe) {
|
||||
exitSafeCall(functionCall)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processConditionalContract(functionCall: FirFunctionCall) {
|
||||
val contractDescription = (functionCall.resolvedSymbol?.fir as? FirSimpleFunction)?.contractDescription ?: return
|
||||
val conditionalEffects = contractDescription.effects.filterIsInstance<ConeConditionalEffectDeclaration>()
|
||||
if (conditionalEffects.isEmpty()) return
|
||||
val argumentsMapping = createArgumentsMapping(functionCall) ?: return
|
||||
contractDescriptionVisitingMode = true
|
||||
graphBuilder.enterContract(functionCall).mergeIncomingFlow()
|
||||
val functionCallVariable = getOrCreateVariable(functionCall)
|
||||
for (conditionalEffect in conditionalEffects) {
|
||||
val fir = conditionalEffect.buildContractFir(argumentsMapping) ?: continue
|
||||
val effect = conditionalEffect.effect as? ConeReturnsEffectDeclaration ?: continue
|
||||
fir.transformSingle(components.transformer, ResolutionMode.ContextDependent)
|
||||
val argumentVariable = getOrCreateVariable(fir)
|
||||
val lastNode = graphBuilder.lastNode
|
||||
when (val value = effect.value) {
|
||||
ConeConstantReference.WILDCARD -> {
|
||||
lastNode.flow = logicSystem.approveFactsInsideFlow(
|
||||
argumentVariable,
|
||||
EqTrue,
|
||||
lastNode.flow,
|
||||
shouldForkFlow = false,
|
||||
shouldRemoveSynthetics = true
|
||||
)
|
||||
}
|
||||
|
||||
ConeBooleanConstantReference.TRUE, ConeBooleanConstantReference.FALSE -> {
|
||||
logicSystem.changeVariableForConditionFlow(lastNode.flow, argumentVariable, functionCallVariable) {
|
||||
it.takeIf { it.condition == if (value == ConeBooleanConstantReference.TRUE) EqTrue else EqFalse }
|
||||
}
|
||||
}
|
||||
|
||||
ConeConstantReference.NOT_NULL, ConeConstantReference.NULL -> {
|
||||
logicSystem.changeVariableForConditionFlow(lastNode.flow, argumentVariable, functionCallVariable) {
|
||||
it.takeIf { it.condition == EqTrue }?.let {
|
||||
val condition = if (value == ConeConstantReference.NOT_NULL) Condition.NotEqNull else NotEqNull
|
||||
ConditionalFirDataFlowInfo(condition, it.variable, it.info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> throw IllegalArgumentException(value.toString())
|
||||
}
|
||||
}
|
||||
graphBuilder.exitContract(functionCall).mergeIncomingFlow()
|
||||
contractDescriptionVisitingMode = true
|
||||
}
|
||||
|
||||
|
||||
private val FirElement.resolvedSymbol: AbstractFirBasedSymbol<*>?
|
||||
get() {
|
||||
return when (this) {
|
||||
is FirResolvable -> resolvedSymbol
|
||||
is FirSymbolOwner<*> -> symbol
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private val FirResolvable.resolvedSymbol: AbstractFirBasedSymbol<*>?
|
||||
get() = calleeReference.let {
|
||||
when (it) {
|
||||
is FirExplicitThisReference -> it.boundSymbol
|
||||
is FirResolvedNamedReference -> it.resolvedSymbol
|
||||
is FirNamedReferenceWithCandidate -> it.candidateSymbol
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun FirFunctionCall.isBooleanNot(): Boolean {
|
||||
val symbol = calleeReference.safeAs<FirResolvedNamedReference>()?.resolvedSymbol as? FirNamedFunctionSymbol ?: return false
|
||||
return symbol.callableId == KOTLIN_BOOLEAN_NOT
|
||||
}
|
||||
|
||||
fun exitConstExpresion(constExpression: FirConstExpression<*>) {
|
||||
if (constExpression.resultType is FirResolvedTypeRef && !contractDescriptionVisitingMode) return
|
||||
graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitVariableDeclaration(variable: FirProperty) {
|
||||
val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
|
||||
val initializer = variable.initializer ?: return
|
||||
|
||||
/*
|
||||
* That part is needed for cases like that:
|
||||
*
|
||||
* val b = x is String
|
||||
* ...
|
||||
* if (b) {
|
||||
* x.length
|
||||
* }
|
||||
*/
|
||||
variableStorage[initializer]?.takeIf { it.isSynthetic() }?.let { initializerVariable ->
|
||||
val realVariable = getOrCreateRealVariable(variable)
|
||||
requireNotNull(realVariable)
|
||||
logicSystem.changeVariableForConditionFlow(node.flow, initializerVariable, realVariable)
|
||||
}
|
||||
|
||||
|
||||
getOrCreateRealVariable(initializer)?.let { rhsVariable ->
|
||||
variableStorage.createAliasVariable(variable.symbol, rhsVariable)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitVariableAssignment(assignment: FirVariableAssignment) {
|
||||
val node = graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
|
||||
val lhsSymbol: AbstractFirBasedSymbol<*> = (assignment.lValue as? FirResolvedNamedReference)?.resolvedSymbol ?: return
|
||||
val lhsVariable = getOrCreateRealVariable(lhsSymbol.fir) ?: return
|
||||
val rhsSymbol = assignment.rValue.resolvedSymbol
|
||||
val rhsVariable = rhsSymbol?.let { variableStorage[it]?.takeIf { !it.isSynthetic() } }
|
||||
if (rhsVariable == null) {
|
||||
val type = assignment.rValue.typeRef.coneTypeSafe<ConeKotlinType>() ?: return
|
||||
logicSystem.addApprovedInfo(
|
||||
node.flow,
|
||||
lhsVariable,
|
||||
FirDataFlowInfo(setOf(type), emptySet())
|
||||
)
|
||||
return
|
||||
} else {
|
||||
variableStorage.rebindAliasVariable(lhsVariable, rhsVariable)
|
||||
}
|
||||
}
|
||||
|
||||
fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
|
||||
graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// ----------------------------------- Boolean operators -----------------------------------
|
||||
|
||||
fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
graphBuilder.enterBinaryAnd(binaryLogicExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
|
||||
exitLeftArgumentOfBinaryBooleanOperator(leftNode, rightNode, isAnd = true)
|
||||
}
|
||||
|
||||
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression)
|
||||
exitBinaryBooleanOperator(binaryLogicExpression, node, isAnd = true)
|
||||
}
|
||||
|
||||
fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
graphBuilder.enterBinaryOr(binaryLogicExpression).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
|
||||
exitLeftArgumentOfBinaryBooleanOperator(leftNode, rightNode, isAnd = false)
|
||||
}
|
||||
|
||||
fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
|
||||
val node = graphBuilder.exitBinaryOr(binaryLogicExpression)
|
||||
exitBinaryBooleanOperator(binaryLogicExpression, node, isAnd = false)
|
||||
}
|
||||
|
||||
private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) {
|
||||
val parentFlow = leftNode.alivePreviousNodes.first().flow
|
||||
leftNode.flow = logicSystem.forkFlow(parentFlow)
|
||||
val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir)
|
||||
rightNode.flow = logicSystem.approveFactsInsideFlow(
|
||||
leftOperandVariable,
|
||||
if (isAnd) EqTrue else EqFalse,
|
||||
parentFlow,
|
||||
shouldForkFlow = true,
|
||||
shouldRemoveSynthetics = false
|
||||
)
|
||||
}
|
||||
|
||||
private fun exitBinaryBooleanOperator(
|
||||
binaryLogicExpression: FirBinaryLogicExpression,
|
||||
node: AbstractBinaryExitNode<*>,
|
||||
isAnd: Boolean
|
||||
) {
|
||||
val bothEvaluated = if (isAnd) EqTrue else EqFalse
|
||||
val onlyLeftEvaluated = bothEvaluated.invert()
|
||||
|
||||
// Naming for all variables was chosen in assumption that we processing && expression
|
||||
val flowFromLeft = node.leftOperandNode.flow
|
||||
val flowFromRight = node.rightOperandNode.flow
|
||||
|
||||
val flow = node.mergeIncomingFlow().flow
|
||||
|
||||
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
|
||||
val andVariable = getOrCreateVariable(binaryLogicExpression)
|
||||
|
||||
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator(
|
||||
flowFromLeft,
|
||||
leftVariable,
|
||||
flowFromRight,
|
||||
rightVariable
|
||||
)
|
||||
|
||||
// left && right == True
|
||||
// left || right == False
|
||||
val approvedIfTrue: MutableApprovedInfos = mutableMapOf()
|
||||
logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromLeft)
|
||||
logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromRight)
|
||||
approvedFromRight.forEach { (variable, info) ->
|
||||
approvedIfTrue.addInfo(variable, info)
|
||||
}
|
||||
approvedIfTrue.forEach { (variable, info) ->
|
||||
logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(bothEvaluated, variable))
|
||||
}
|
||||
|
||||
// left && right == False
|
||||
// left || right == True
|
||||
val approvedIfFalse: MutableApprovedInfos = mutableMapOf()
|
||||
val leftIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromLeft)
|
||||
val rightIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromRight)
|
||||
approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse))
|
||||
approvedIfFalse.forEach { (variable, info) ->
|
||||
logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(onlyLeftEvaluated, variable))
|
||||
}
|
||||
|
||||
node.flow = flow
|
||||
|
||||
variableStorage.removeVariableIfSynthetic(leftVariable)
|
||||
variableStorage.removeVariableIfSynthetic(rightVariable)
|
||||
}
|
||||
|
||||
|
||||
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
|
||||
val booleanExpressionVariable = getOrCreateVariable(node.previousNodes.first().fir)
|
||||
val variable = getOrCreateVariable(functionCall)
|
||||
logicSystem.changeVariableForConditionFlow(node.flow, booleanExpressionVariable, variable) { it.invert() }
|
||||
}
|
||||
|
||||
// ----------------------------------- Annotations -----------------------------------
|
||||
|
||||
fun enterAnnotationCall(annotationCall: FirAnnotationCall) {
|
||||
graphBuilder.enterAnnotationCall(annotationCall).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitAnnotationCall(annotationCall: FirAnnotationCall) {
|
||||
graphBuilder.exitAnnotationCall(annotationCall).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// ----------------------------------- Init block -----------------------------------
|
||||
|
||||
fun enterInitBlock(initBlock: FirAnonymousInitializer) {
|
||||
graphBuilder.enterInitBlock(initBlock).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
fun exitInitBlock(initBlock: FirAnonymousInitializer) {
|
||||
graphBuilder.exitInitBlock(initBlock).mergeIncomingFlow()
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
private fun FirBinaryLogicExpression.getVariables(): Pair<DataFlowVariable, DataFlowVariable> =
|
||||
getOrCreateVariable(leftOperand) to getOrCreateVariable(rightOperand)
|
||||
|
||||
private var CFGNode<*>.flow: Flow
|
||||
get() = flowOnNodes.getValue(this.origin)
|
||||
set(value) {
|
||||
flowOnNodes[this.origin] = value
|
||||
}
|
||||
|
||||
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) previousNodes.first() else this
|
||||
|
||||
private fun <T : CFGNode<*>> T.mergeIncomingFlow(): T = this.also { node ->
|
||||
val previousFlows = node.alivePreviousNodes.map { it.flow }
|
||||
node.flow = logicSystem.joinFlow(previousFlows)
|
||||
}
|
||||
|
||||
// -------------------------------- get or create variable --------------------------------
|
||||
|
||||
private fun getOrCreateSyntheticVariable(fir: FirElement): SyntheticDataFlowVariable =
|
||||
variableStorage.getOrCreateNewSyntheticVariable(fir)
|
||||
|
||||
private fun getOrCreateRealVariable(fir: FirElement): RealDataFlowVariable? {
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val fir = fir.unwrapWhenSubjectExpression().unwrapSmartcast()
|
||||
val symbol = fir.resolvedSymbol ?: return null
|
||||
return when {
|
||||
fir is FirThisReceiverExpressionImpl -> variableStorage.getOrCreateNewThisRealVariable(symbol)
|
||||
symbol is FirVariableSymbol<*> ->
|
||||
// TODO: Fix this for non-local properties.
|
||||
variableStorage.getOrCreateNewRealVariable(symbol).variableUnderAlias
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
private fun getOrCreateVariable(fir: FirElement): DataFlowVariable {
|
||||
return getOrCreateRealVariable(fir) ?: getOrCreateSyntheticVariable(fir)
|
||||
}
|
||||
|
||||
// -------------------------------- get variable --------------------------------
|
||||
|
||||
private val FirElement.realVariable: RealDataFlowVariable?
|
||||
get() {
|
||||
val symbol: AbstractFirBasedSymbol<*> = if (this is FirThisReceiverExpression) {
|
||||
calleeReference.boundSymbol
|
||||
} else {
|
||||
resolvedSymbol
|
||||
} ?: return null
|
||||
return variableStorage[symbol]
|
||||
}
|
||||
|
||||
// TODO: Fix this -- see broken test_3 in compiler/fir/resolve/testData/resolve/smartcasts/notBoundSmartcasts.kt
|
||||
// If we have multiple local variables (could be value parameter) of the same type, there should be separate DataFlowVariables for
|
||||
// accesses to a property for each distinct local variable, i.e., DataFlowVariables in storage for properties should be keyed by
|
||||
// the "chain" of variable/property accesses. We also need to check that the property is not mutable and has no custom getter.
|
||||
private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection<RealDataFlowVariable> {
|
||||
val result = mutableListOf<RealDataFlowVariable>()
|
||||
|
||||
fun collect(call: FirExpression) {
|
||||
when (call) {
|
||||
is FirQualifiedAccess -> {
|
||||
if (call.safe) {
|
||||
val explicitReceiver = call.explicitReceiver
|
||||
require(explicitReceiver != null)
|
||||
collect(explicitReceiver)
|
||||
}
|
||||
result.addIfNotNull(getOrCreateRealVariable(call))
|
||||
}
|
||||
is FirWhenSubjectExpression -> {
|
||||
// TODO: check
|
||||
call.whenSubject.whenExpression.subjectVariable?.let { result += getOrCreateRealVariable(it)!! }
|
||||
call.whenSubject.whenExpression.subject?.let { collect(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
collect(call)
|
||||
return result
|
||||
}
|
||||
|
||||
private inner class LogicSystemImpl(context: ConeInferenceContext) : DelegatingLogicSystem(context) {
|
||||
override fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable) {
|
||||
val symbol = (variable.fir as? FirSymbolOwner<*>)?.symbol ?: return
|
||||
|
||||
val index = receiverStack.getReceiverIndex(symbol) ?: return
|
||||
val info = flow.getApprovedInfo(variable)
|
||||
|
||||
if (info == null) {
|
||||
receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index))
|
||||
} else {
|
||||
val types = info.exactType.toMutableList().also {
|
||||
it += receiverStack.getOriginalType(index)
|
||||
}
|
||||
receiverStack.replaceReceiverType(index, context.intersectTypesOrNull(types)!!)
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateAllReceivers(flow: Flow) {
|
||||
receiverStack.mapNotNull { variableStorage[it.boundSymbol] }.forEach { processUpdatedReceiverVariable(flow, it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,89 +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 com.google.common.collect.Multimap
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.fir.types.render
|
||||
|
||||
data class ConditionalFirDataFlowInfo(
|
||||
val condition: Condition,
|
||||
val variable: RealDataFlowVariable,
|
||||
val info: FirDataFlowInfo
|
||||
) {
|
||||
fun invert(): ConditionalFirDataFlowInfo {
|
||||
return ConditionalFirDataFlowInfo(condition.invert(), variable, info)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "$condition -> $variable: ${info.exactType.render()}, ${info.exactNotType.render()}"
|
||||
}
|
||||
|
||||
private fun Set<ConeKotlinType>.render(): String = joinToString { it.render() }
|
||||
}
|
||||
|
||||
typealias ApprovedInfos = Map<RealDataFlowVariable, FirDataFlowInfo>
|
||||
typealias MutableApprovedInfos = MutableMap<RealDataFlowVariable, MutableFirDataFlowInfo>
|
||||
typealias ConditionalInfos = Multimap<DataFlowVariable, ConditionalFirDataFlowInfo>
|
||||
|
||||
interface FirDataFlowInfo {
|
||||
companion object {
|
||||
// TODO: temporary
|
||||
operator fun invoke(exactType: Set<ConeKotlinType>, exactNotType: Set<ConeKotlinType>): FirDataFlowInfo =
|
||||
MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet())
|
||||
}
|
||||
|
||||
val exactType: Set<ConeKotlinType>
|
||||
val exactNotType: Set<ConeKotlinType>
|
||||
operator fun plus(other: FirDataFlowInfo): FirDataFlowInfo
|
||||
operator fun minus(other: FirDataFlowInfo): FirDataFlowInfo
|
||||
val isNotEmpty: Boolean
|
||||
val isEmpty: Boolean get() = !isNotEmpty
|
||||
fun invert(): FirDataFlowInfo
|
||||
}
|
||||
|
||||
data class MutableFirDataFlowInfo(
|
||||
override val exactType: MutableSet<ConeKotlinType> = mutableSetOf(),
|
||||
override val exactNotType: MutableSet<ConeKotlinType> = mutableSetOf()
|
||||
) : FirDataFlowInfo {
|
||||
override operator fun plus(other: FirDataFlowInfo): MutableFirDataFlowInfo = MutableFirDataFlowInfo(
|
||||
HashSet(exactType).apply { addAll(other.exactType) },
|
||||
HashSet(exactNotType).apply { addAll(other.exactNotType) }
|
||||
)
|
||||
|
||||
override operator fun minus(other: FirDataFlowInfo): MutableFirDataFlowInfo = MutableFirDataFlowInfo(
|
||||
HashSet(exactType).apply { removeAll(other.exactType) },
|
||||
HashSet(exactNotType).apply { removeAll(other.exactNotType) }
|
||||
)
|
||||
|
||||
override val isNotEmpty: Boolean get() = exactType.isNotEmpty() || exactNotType.isNotEmpty()
|
||||
|
||||
override fun invert(): FirDataFlowInfo = MutableFirDataFlowInfo(exactNotType, exactType)
|
||||
|
||||
operator fun plusAssign(info: FirDataFlowInfo) {
|
||||
exactType += info.exactType
|
||||
exactNotType += info.exactNotType
|
||||
}
|
||||
|
||||
fun copy(): MutableFirDataFlowInfo = MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet())
|
||||
}
|
||||
|
||||
operator fun FirDataFlowInfo.plus(other: FirDataFlowInfo?): FirDataFlowInfo = other?.let { this + other } ?: this
|
||||
|
||||
fun FirDataFlowInfo.toConditional(condition: Condition, variable: RealDataFlowVariable): ConditionalFirDataFlowInfo =
|
||||
ConditionalFirDataFlowInfo(condition, variable, this)
|
||||
|
||||
fun MutableApprovedInfos.addInfo(variable: RealDataFlowVariable, info: FirDataFlowInfo) {
|
||||
merge(variable, info as MutableFirDataFlowInfo) { existingInfo, newInfo ->
|
||||
existingInfo.apply { this += newInfo }
|
||||
}
|
||||
}
|
||||
|
||||
fun MutableApprovedInfos.mergeInfo(other: Map<RealDataFlowVariable, FirDataFlowInfo>) {
|
||||
other.forEach { (variable, info) ->
|
||||
addInfo(variable, info)
|
||||
}
|
||||
}
|
||||
@@ -1,197 +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 com.google.common.collect.ArrayListMultimap
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
|
||||
interface Flow {
|
||||
fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo?
|
||||
fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo>
|
||||
|
||||
fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable>
|
||||
|
||||
fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo>
|
||||
}
|
||||
|
||||
abstract class LogicSystem(private val context: ConeInferenceContext) {
|
||||
|
||||
// ------------------------------- Flow operations -------------------------------
|
||||
|
||||
abstract fun createEmptyFlow(): Flow
|
||||
abstract fun forkFlow(flow: Flow): Flow
|
||||
abstract fun joinFlow(flows: Collection<Flow>): Flow
|
||||
|
||||
open fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) {
|
||||
assert(info is MutableFirDataFlowInfo)
|
||||
flow.approvedInfos.addInfo(variable, info)
|
||||
if (variable.isThisReference) {
|
||||
processUpdatedReceiverVariable(flow, variable)
|
||||
}
|
||||
}
|
||||
|
||||
open fun addConditionalInfo(flow: Flow, variable: DataFlowVariable, info: ConditionalFirDataFlowInfo) {
|
||||
flow.conditionalInfos.put(variable, info)
|
||||
}
|
||||
|
||||
/*
|
||||
* used for:
|
||||
* 1. val b = x is String
|
||||
* 2. b = x is String
|
||||
* 3. !b | b.not() for Booleans
|
||||
*/
|
||||
open fun changeVariableForConditionFlow(
|
||||
flow: Flow,
|
||||
sourceVariable: DataFlowVariable,
|
||||
newVariable: DataFlowVariable,
|
||||
transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo?)? = null
|
||||
) {
|
||||
var infos = flow.getConditionalInfos(sourceVariable)
|
||||
if (transform != null) {
|
||||
infos = infos.mapNotNull(transform)
|
||||
}
|
||||
flow.conditionalInfos.putAll(newVariable, infos)
|
||||
if (sourceVariable.isSynthetic()) {
|
||||
flow.conditionalInfos.removeAll(sourceVariable)
|
||||
}
|
||||
}
|
||||
|
||||
open fun approveFactsInsideFlow(
|
||||
variable: DataFlowVariable,
|
||||
condition: Condition,
|
||||
flow: Flow,
|
||||
shouldForkFlow: Boolean,
|
||||
shouldRemoveSynthetics: Boolean
|
||||
): Flow {
|
||||
val notApprovedFacts: Collection<ConditionalFirDataFlowInfo> = if (shouldRemoveSynthetics && variable.isSynthetic()) {
|
||||
flow.removeConditionalInfos(variable)
|
||||
} else {
|
||||
flow.getConditionalInfos(variable)
|
||||
}
|
||||
|
||||
val resultFlow = if (shouldForkFlow) forkFlow(flow) else flow
|
||||
if (notApprovedFacts.isEmpty()) {
|
||||
return resultFlow
|
||||
}
|
||||
|
||||
val newFacts = ArrayListMultimap.create<RealDataFlowVariable, FirDataFlowInfo>()
|
||||
notApprovedFacts.forEach {
|
||||
if (it.condition == condition) {
|
||||
newFacts.put(it.variable, it.info)
|
||||
}
|
||||
}
|
||||
|
||||
val updatedReceivers = mutableSetOf<RealDataFlowVariable>()
|
||||
|
||||
newFacts.asMap().forEach { (variable, infos) ->
|
||||
@Suppress("NAME_SHADOWING")
|
||||
val info = MutableFirDataFlowInfo()
|
||||
infos.forEach {
|
||||
info += it
|
||||
}
|
||||
if (variable.isThisReference) {
|
||||
updatedReceivers += variable
|
||||
}
|
||||
addApprovedInfo(resultFlow, variable, info)
|
||||
}
|
||||
updatedReceivers.forEach {
|
||||
processUpdatedReceiverVariable(resultFlow, it)
|
||||
}
|
||||
return resultFlow
|
||||
}
|
||||
|
||||
// ------------------------------- Callbacks for updating implicit receiver stack -------------------------------
|
||||
|
||||
abstract fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable)
|
||||
abstract fun updateAllReceivers(flow: Flow)
|
||||
|
||||
// ------------------------------- Accessors to flow implementation -------------------------------
|
||||
|
||||
protected abstract val Flow.approvedInfos: MutableApprovedInfos
|
||||
protected abstract val Flow.conditionalInfos: ConditionalInfos
|
||||
|
||||
// ------------------------------- Public DataFlowInfo util functions -------------------------------
|
||||
|
||||
data class InfoForBooleanOperator(
|
||||
val conditionalFromLeft: Collection<ConditionalFirDataFlowInfo>,
|
||||
val conditionalFromRight: Collection<ConditionalFirDataFlowInfo>,
|
||||
val approvedFromRight: ApprovedInfos
|
||||
)
|
||||
|
||||
abstract fun collectInfoForBooleanOperator(
|
||||
leftFlow: Flow,
|
||||
leftVariable: DataFlowVariable,
|
||||
rightFlow: Flow,
|
||||
rightVariable: DataFlowVariable
|
||||
): InfoForBooleanOperator
|
||||
|
||||
fun orForVerifiedFacts(
|
||||
left: ApprovedInfos,
|
||||
right: ApprovedInfos
|
||||
): MutableApprovedInfos {
|
||||
if (left.isNullOrEmpty() || right.isNullOrEmpty()) return mutableMapOf()
|
||||
val map = mutableMapOf<RealDataFlowVariable, MutableFirDataFlowInfo>()
|
||||
for (variable in left.keys.intersect(right.keys)) {
|
||||
val leftInfo = left.getValue(variable)
|
||||
val rightInfo = right.getValue(variable)
|
||||
map[variable] = or(listOf(leftInfo, rightInfo))
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
fun approveFactTo(destination: MutableApprovedInfos, variable: DataFlowVariable, condition: Condition, flow: Flow) {
|
||||
val notApprovedFacts: Collection<ConditionalFirDataFlowInfo> = flow.getConditionalInfos(variable)
|
||||
approveFactTo(destination, condition, notApprovedFacts)
|
||||
}
|
||||
|
||||
fun approveFactTo(destination: MutableApprovedInfos, condition: Condition, notApprovedFacts: Collection<ConditionalFirDataFlowInfo>) {
|
||||
if (notApprovedFacts.isEmpty()) return
|
||||
notApprovedFacts.forEach {
|
||||
if (it.condition == condition) {
|
||||
destination.addInfo(it.variable, it.info)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun approveFact(condition: Condition, notApprovedFacts: Collection<ConditionalFirDataFlowInfo>): MutableApprovedInfos {
|
||||
return mutableMapOf<RealDataFlowVariable, MutableFirDataFlowInfo>().apply { approveFactTo(this, condition, notApprovedFacts) }
|
||||
}
|
||||
|
||||
fun approveFact(variable: DataFlowVariable, condition: Condition, flow: Flow): MutableApprovedInfos {
|
||||
return mutableMapOf<RealDataFlowVariable, MutableFirDataFlowInfo>().apply { approveFactTo(this, variable, condition, flow) }
|
||||
}
|
||||
|
||||
// ------------------------------- Util functions -------------------------------
|
||||
|
||||
// TODO
|
||||
protected fun <E> Collection<Collection<E>>.intersectSets(): Set<E> {
|
||||
if (isEmpty()) return emptySet()
|
||||
val iterator = iterator()
|
||||
val result = HashSet<E>(iterator.next())
|
||||
while (iterator.hasNext()) {
|
||||
result.retainAll(iterator.next())
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
protected fun or(infos: Collection<FirDataFlowInfo>): MutableFirDataFlowInfo {
|
||||
infos.singleOrNull()?.let { return it as MutableFirDataFlowInfo }
|
||||
val exactType = orTypes(infos.map { it.exactType })
|
||||
val exactNotType = orTypes(infos.map { it.exactNotType })
|
||||
return MutableFirDataFlowInfo(exactType, exactNotType)
|
||||
}
|
||||
|
||||
private fun orTypes(types: Collection<Set<ConeKotlinType>>): MutableSet<ConeKotlinType> {
|
||||
if (types.any { it.isEmpty() }) return mutableSetOf()
|
||||
val allTypes = types.flatMapTo(mutableSetOf()) { it }
|
||||
val commonTypes = allTypes.toMutableSet()
|
||||
types.forEach { commonTypes.retainAll(it) }
|
||||
val differentTypes = allTypes - commonTypes
|
||||
context.commonSuperTypeOrNull(differentTypes.toList())?.let { commonTypes += it }
|
||||
return commonTypes
|
||||
}
|
||||
}
|
||||
-266
@@ -1,266 +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 kotlinx.collections.immutable.*
|
||||
//import kotlinx.collections.immutable.PersistentMap
|
||||
//import kotlinx.collections.immutable.PersistentSet
|
||||
//import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
//
|
||||
//
|
||||
//private data class PersistentFirDataFlowInfo(
|
||||
// override val exactType: PersistentSet<ConeKotlinType>,
|
||||
// override val exactNotType: PersistentSet<ConeKotlinType>
|
||||
//) : FirDataFlowInfo {
|
||||
//
|
||||
// override operator fun plus(other: FirDataFlowInfo): PersistentFirDataFlowInfo {
|
||||
// return PersistentFirDataFlowInfo(
|
||||
// exactType + other.exactType,
|
||||
// exactNotType + other.exactNotType
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override fun minus(other: FirDataFlowInfo): FirDataFlowInfo {
|
||||
// // TODO
|
||||
// throw IllegalStateException()
|
||||
// }
|
||||
//
|
||||
// override val isNotEmpty: Boolean
|
||||
// get() = exactType.isNotEmpty() || exactNotType.isNotEmpty()
|
||||
//
|
||||
// override fun invert(): PersistentFirDataFlowInfo {
|
||||
// return PersistentFirDataFlowInfo(exactNotType, exactType)
|
||||
// }
|
||||
//
|
||||
//// override fun toMutableInfo(): MutableFirDataFlowInfo {
|
||||
//// return MutableFirDataFlowInfo(exactType.toMutableSet(), exactNotType.toMutableSet())
|
||||
//// }
|
||||
//}
|
||||
//
|
||||
//private typealias PersistentApprovedInfos = PersistentMap<RealDataFlowVariable, PersistentFirDataFlowInfo>
|
||||
//private typealias PersistentConditionalInfos = PersistentMap<DataFlowVariable, PersistentList<ConditionalFirDataFlowInfo>>
|
||||
//
|
||||
//private fun FirDataFlowInfo.toPersistent(): PersistentFirDataFlowInfo = PersistentFirDataFlowInfo(
|
||||
// exactType.toPersistentSet(),
|
||||
// exactNotType.toPersistentSet()
|
||||
//)
|
||||
//
|
||||
//private fun PersistentApprovedInfos.addNewInfo(variable: RealDataFlowVariable, info: FirDataFlowInfo): PersistentApprovedInfos {
|
||||
// val existingInfo = this[variable]
|
||||
// return if (existingInfo == null) {
|
||||
// val persistentInfo = if (info is PersistentFirDataFlowInfo) info else info.toPersistent()
|
||||
// put(variable, persistentInfo)
|
||||
// } else {
|
||||
// put(variable, existingInfo + info)
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//private class PersistentFlow : Flow {
|
||||
// val previousFlow: PersistentFlow?
|
||||
// var approvedInfos: PersistentApprovedInfos
|
||||
// var conditionalInfos: PersistentConditionalInfos
|
||||
//
|
||||
// constructor(previousFlow: PersistentFlow) {
|
||||
// this.previousFlow = previousFlow
|
||||
// approvedInfos = previousFlow.approvedInfos
|
||||
// conditionalInfos = previousFlow.conditionalInfos
|
||||
// level = previousFlow.level + 1
|
||||
// }
|
||||
//
|
||||
// constructor() {
|
||||
// previousFlow = null
|
||||
// approvedInfos = persistentHashMapOf()
|
||||
// conditionalInfos = persistentHashMapOf()
|
||||
// level = 1
|
||||
// }
|
||||
//
|
||||
// val level: Int
|
||||
// var approvedInfosDiff: PersistentApprovedInfos = persistentHashMapOf()
|
||||
//
|
||||
// override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? {
|
||||
// return approvedInfos[variable]
|
||||
// }
|
||||
//
|
||||
// override fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
|
||||
// return conditionalInfos[variable] ?: emptyList()
|
||||
// }
|
||||
//
|
||||
// override fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable> {
|
||||
// return approvedInfos.keys
|
||||
// }
|
||||
//
|
||||
// override fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
|
||||
// val result = getConditionalInfos(variable)
|
||||
// if (result.isNotEmpty()) {
|
||||
// conditionalInfos = conditionalInfos.remove(variable)
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//abstract class PersistentLogicSystem(context: DataFlowInferenceContext) : LogicSystem(context) {
|
||||
// override fun createEmptyFlow(): Flow {
|
||||
// return PersistentFlow()
|
||||
// }
|
||||
//
|
||||
// override fun forkFlow(flow: Flow): Flow {
|
||||
// require(flow is PersistentFlow)
|
||||
// return PersistentFlow(flow)
|
||||
// }
|
||||
//
|
||||
// override fun joinFlow(flows: Collection<Flow>): Flow {
|
||||
// if (flows.isEmpty()) return createEmptyFlow()
|
||||
// flows.singleOrNull()?.let { return it }
|
||||
//
|
||||
// @Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
// val flows = flows as Collection<PersistentFlow>
|
||||
// val commonFlow = flows.reduce(this::lowestCommonFlow)
|
||||
//
|
||||
// val commonVariables = flows.map { it.diffVariablesIterable(commonFlow).toList() }
|
||||
// .intersectSets()
|
||||
// .takeIf { it.isNotEmpty() }
|
||||
// ?: return commonFlow
|
||||
//
|
||||
// for (variable in commonVariables) {
|
||||
// val info = or(flows.map { it.getApprovedDiff(variable, commonFlow) })
|
||||
// if (info.isEmpty) continue
|
||||
// commonFlow.approvedInfos = commonFlow.approvedInfos.addNewInfo(variable, info)
|
||||
// commonFlow.approvedInfosDiff = commonFlow.approvedInfosDiff.addNewInfo(variable, info)
|
||||
// }
|
||||
//
|
||||
// updateAllReceivers(commonFlow)
|
||||
//
|
||||
// return commonFlow
|
||||
// }
|
||||
//
|
||||
// private fun PersistentFlow.diffVariablesIterable(parentFlow: PersistentFlow): Iterable<RealDataFlowVariable> =
|
||||
// object : DiffIterable<RealDataFlowVariable>(parentFlow, this) {
|
||||
// override fun extractIterator(flow: PersistentFlow): Iterator<RealDataFlowVariable> {
|
||||
// return flow.approvedInfosDiff.keys.iterator()
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private abstract class DiffIterable<T>(private val parentFlow: PersistentFlow, private var currentFlow: PersistentFlow) : Iterable<T> {
|
||||
// private var currentIterator = extractIterator(currentFlow)
|
||||
//
|
||||
// abstract fun extractIterator(flow: PersistentFlow): Iterator<T>
|
||||
//
|
||||
// override fun iterator(): Iterator<T> {
|
||||
// return object : Iterator<T> {
|
||||
// override fun hasNext(): Boolean {
|
||||
// if (currentIterator.hasNext()) return true
|
||||
// while (currentFlow != parentFlow) {
|
||||
// currentFlow = currentFlow.previousFlow!!
|
||||
// currentIterator = extractIterator(currentFlow)
|
||||
// if (currentIterator.hasNext()) return true
|
||||
// }
|
||||
// return false
|
||||
// }
|
||||
//
|
||||
// override fun next(): T {
|
||||
// if (!hasNext()) {
|
||||
// throw NoSuchElementException()
|
||||
// }
|
||||
// return currentIterator.next()
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// private fun PersistentFlow.getApprovedDiff(variable: RealDataFlowVariable, parentFlow: PersistentFlow): MutableFirDataFlowInfo {
|
||||
// var flow = this
|
||||
// val result = MutableFirDataFlowInfo()
|
||||
// while (flow != parentFlow) {
|
||||
// flow.approvedInfosDiff[variable]?.let {
|
||||
// result += it
|
||||
// }
|
||||
// flow = flow.previousFlow!!
|
||||
// }
|
||||
// return result
|
||||
// }
|
||||
//
|
||||
// override fun collectInfoForBooleanOperator(
|
||||
// leftFlow: Flow,
|
||||
// leftVariable: DataFlowVariable,
|
||||
// rightFlow: Flow,
|
||||
// rightVariable: DataFlowVariable
|
||||
// ): InfoForBooleanOperator {
|
||||
// require(leftFlow is PersistentFlow && rightFlow is PersistentFlow)
|
||||
// return InfoForBooleanOperator(
|
||||
// leftFlow.conditionalInfos[leftVariable] ?: emptyList(),
|
||||
// rightFlow.conditionalInfos[rightVariable] ?: emptyList(),
|
||||
// rightFlow.approvedInfosDiff
|
||||
// )
|
||||
// }
|
||||
//
|
||||
// override fun changeVariableForConditionFlow(
|
||||
// flow: Flow,
|
||||
// sourceVariable: DataFlowVariable,
|
||||
// newVariable: DataFlowVariable,
|
||||
// transform: ((ConditionalFirDataFlowInfo) -> ConditionalFirDataFlowInfo?)?
|
||||
// ) {
|
||||
// require(flow is PersistentFlow)
|
||||
// with(flow) {
|
||||
// val existingInfo = conditionalInfos[sourceVariable]?.takeIf { it.isNotEmpty() } ?: return
|
||||
// val transformedInfo = if (transform == null) {
|
||||
// existingInfo
|
||||
// } else {
|
||||
// existingInfo.map(transform).toPersistentList()
|
||||
// }
|
||||
// conditionalInfos = conditionalInfos.remove(sourceVariable).put(newVariable, transformedInfo)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun addApprovedInfo(flow: Flow, variable: RealDataFlowVariable, info: FirDataFlowInfo) {
|
||||
// require(flow is PersistentFlow)
|
||||
// with(flow) {
|
||||
// approvedInfos = approvedInfos.addNewInfo(variable, info)
|
||||
// if (previousFlow != null) {
|
||||
// approvedInfosDiff = approvedInfosDiff.addNewInfo(variable, info)
|
||||
// }
|
||||
// if (variable.isThisReference) {
|
||||
// processUpdatedReceiverVariable(flow, variable)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override fun addConditionalInfo(flow: Flow, variable: DataFlowVariable, info: ConditionalFirDataFlowInfo) {
|
||||
// require(flow is PersistentFlow)
|
||||
// with(flow) {
|
||||
// val existingInfo = conditionalInfos[variable]
|
||||
// conditionalInfos = if (existingInfo == null) {
|
||||
// conditionalInfos.put(variable, persistentListOf(info))
|
||||
// } else {
|
||||
// conditionalInfos.put(variable, existingInfo + info)
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// override val Flow.approvedInfos: MutableApprovedInfos
|
||||
// get() = throw IllegalStateException()
|
||||
//
|
||||
// override val Flow.conditionalInfos: ConditionalInfos
|
||||
// get() = throw IllegalStateException()
|
||||
//
|
||||
// private fun lowestCommonFlow(left: PersistentFlow, right: PersistentFlow): PersistentFlow {
|
||||
// val level = minOf(left.level, right.level)
|
||||
// @Suppress("NAME_SHADOWING")
|
||||
// var left = left
|
||||
// while (left.level > level) {
|
||||
// left = left.previousFlow!!
|
||||
// }
|
||||
// @Suppress("NAME_SHADOWING")
|
||||
// var right = right
|
||||
// while (right.level > level) {
|
||||
// right = right.previousFlow!!
|
||||
// }
|
||||
// while (left != right) {
|
||||
// left = left.previousFlow!!
|
||||
// right = right.previousFlow!!
|
||||
// }
|
||||
// return left
|
||||
// }
|
||||
//}
|
||||
@@ -1,106 +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 com.google.common.collect.ArrayListMultimap
|
||||
import com.google.common.collect.HashMultimap
|
||||
import com.google.common.collect.Multimap
|
||||
import org.jetbrains.kotlin.fir.resolve.calls.ConeInferenceContext
|
||||
|
||||
private class SimpleFlow(
|
||||
val approvedInfos: MutableApprovedInfos = mutableMapOf(),
|
||||
val conditionalInfos: ConditionalInfos = HashMultimap.create()
|
||||
) : Flow {
|
||||
override fun getApprovedInfo(variable: RealDataFlowVariable): FirDataFlowInfo? {
|
||||
return approvedInfos[variable]
|
||||
}
|
||||
|
||||
override fun getConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
|
||||
return conditionalInfos[variable]
|
||||
}
|
||||
|
||||
override fun getVariablesInApprovedInfos(): Collection<RealDataFlowVariable> {
|
||||
return approvedInfos.keys
|
||||
}
|
||||
|
||||
override fun removeConditionalInfos(variable: DataFlowVariable): Collection<ConditionalFirDataFlowInfo> {
|
||||
return conditionalInfos.removeAll(variable)
|
||||
}
|
||||
}
|
||||
|
||||
abstract class SimpleLogicSystem(context: ConeInferenceContext) : LogicSystem(context) {
|
||||
override val Flow.approvedInfos: MutableApprovedInfos
|
||||
get() = (this as SimpleFlow).approvedInfos
|
||||
|
||||
override val Flow.conditionalInfos: ConditionalInfos
|
||||
get() = (this as SimpleFlow).conditionalInfos
|
||||
|
||||
override fun createEmptyFlow(): Flow {
|
||||
return SimpleFlow()
|
||||
}
|
||||
|
||||
override fun forkFlow(flow: Flow): Flow {
|
||||
require(flow is SimpleFlow)
|
||||
return SimpleFlow(
|
||||
flow.approvedInfos.mapValuesTo(mutableMapOf()) { (_, info) -> info.copy() },
|
||||
flow.conditionalInfos.copy()
|
||||
)
|
||||
}
|
||||
|
||||
override fun joinFlow(flows: Collection<Flow>): Flow {
|
||||
@Suppress("UNCHECKED_CAST", "NAME_SHADOWING")
|
||||
val flows = flows as Collection<SimpleFlow>
|
||||
flows.singleOrNull()?.let { return it }
|
||||
val approvedFacts: MutableApprovedInfos = mutableMapOf()
|
||||
flows.map { it.approvedInfos.keys }
|
||||
.intersectSets()
|
||||
.forEach { variable ->
|
||||
val infos = flows.map { it.approvedInfos[variable]!! }
|
||||
if (infos.isNotEmpty()) {
|
||||
approvedFacts[variable] = or(infos)
|
||||
}
|
||||
}
|
||||
|
||||
val notApprovedFacts: ConditionalInfos = ArrayListMultimap.create()
|
||||
flows.map { it.conditionalInfos.keySet() }
|
||||
.intersectSets()
|
||||
.forEach { variable ->
|
||||
val infos = flows.map { it.conditionalInfos[variable] }.intersectSets()
|
||||
if (infos.isNotEmpty()) {
|
||||
notApprovedFacts.putAll(variable, infos)
|
||||
}
|
||||
}
|
||||
|
||||
val result = SimpleFlow(approvedFacts, notApprovedFacts)
|
||||
updateAllReceivers(result)
|
||||
return result
|
||||
}
|
||||
|
||||
override fun collectInfoForBooleanOperator(
|
||||
leftFlow: Flow,
|
||||
leftVariable: DataFlowVariable,
|
||||
rightFlow: Flow,
|
||||
rightVariable: DataFlowVariable
|
||||
): InfoForBooleanOperator {
|
||||
require(leftFlow is SimpleFlow && rightFlow is SimpleFlow)
|
||||
return InfoForBooleanOperator(
|
||||
leftFlow.conditionalInfos[leftVariable],
|
||||
rightFlow.conditionalInfos[rightVariable],
|
||||
rightFlow.approvedInfos - leftFlow.approvedInfos
|
||||
)
|
||||
}
|
||||
|
||||
private operator fun ApprovedInfos.minus(other: ApprovedInfos): ApprovedInfos {
|
||||
val result: MutableApprovedInfos = mutableMapOf()
|
||||
forEach { (variable, info) ->
|
||||
require(info is MutableFirDataFlowInfo)
|
||||
result[variable] = other[variable]?.let { info - it } ?: info
|
||||
}
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
private fun <K, V> Multimap<K, V>.copy(): Multimap<K, V> = ArrayListMultimap.create(this)
|
||||
Reference in New Issue
Block a user