All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.jetbrains.kotlin.fir.analysis.cfa.util.PropertyInitializationInfoCollector.kt Maven / Gradle / Ivy

There is a newer version: 2.0.0
Show newest version
/*
 * Copyright 2010-2021 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.analysis.cfa.util

import kotlinx.collections.immutable.PersistentMap
import kotlinx.collections.immutable.persistentMapOf
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.fir.declarations.utils.referredPropertySymbol
import org.jetbrains.kotlin.fir.expressions.FirStatement
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol

class PropertyInitializationInfoCollector(
    private val localProperties: Set,
    private val declaredVariableCollector: DeclaredVariableCollector = DeclaredVariableCollector(),
) : ControlFlowGraphVisitor>>() {
    override fun visitNode(
        node: CFGNode<*>,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        var result: PathAwarePropertyInitializationInfo? = null
        for ((label, info) in data) {
            val resultItem = info.applyLabel(node, label)
            result = result?.merge(resultItem) ?: resultItem
        }
        return result ?: PathAwarePropertyInitializationInfo.EMPTY
    }

    override fun visitVariableAssignmentNode(
        node: VariableAssignmentNode,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        val dataForNode = visitNode(node, data)
        val symbol = node.fir.referredPropertySymbol ?: return dataForNode
        return if (symbol !in localProperties) {
            dataForNode
        } else {
            processVariableWithAssignment(dataForNode, symbol)
        }
    }

    override fun visitVariableDeclarationNode(
        node: VariableDeclarationNode,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        val dataForNode = visitNode(node, data)
        return processVariableWithAssignment(
            dataForNode,
            node.fir.symbol,
            overwriteRange = node.fir.initializer == null && node.fir.delegate == null
        )
    }

    fun getData(graph: ControlFlowGraph) =
        graph.collectDataForNode(
            TraverseDirection.Forward,
            PathAwarePropertyInitializationInfo.EMPTY,
            this
        )

    private fun processVariableWithAssignment(
        dataForNode: PathAwarePropertyInitializationInfo,
        symbol: FirPropertySymbol,
        overwriteRange: Boolean = false,
    ): PathAwarePropertyInitializationInfo {
        assert(dataForNode.keys.isNotEmpty())
        return if (overwriteRange)
            overwriteRange(dataForNode, symbol, EventOccurrencesRange.ZERO, ::PathAwarePropertyInitializationInfo)
        else
            addRange(dataForNode, symbol, EventOccurrencesRange.EXACTLY_ONCE, ::PathAwarePropertyInitializationInfo)
    }

    // --------------------------------------------------
    // Data flows of declared/assigned variables in loops
    // --------------------------------------------------

    private fun enterCapturingStatement(statement: FirStatement): Set =
        declaredVariableCollector.enterCapturingStatement(statement)

    private fun exitCapturingStatement(statement: FirStatement) {
        declaredVariableCollector.exitCapturingStatement(statement)
    }

    // A merge point for a loop with `continue`
    override fun visitLoopEnterNode(
        node: LoopEnterNode,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        val declaredVariableSymbolsInLoop = enterCapturingStatement(node.fir)
        if (declaredVariableSymbolsInLoop.isEmpty())
            return visitNode(node, data)

        return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data)
    }

    // A merge point for while loop
    override fun visitLoopConditionEnterNode(
        node: LoopConditionEnterNode,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        val declaredVariableSymbolsInLoop = declaredVariableCollector.declaredVariablesPerElement[node.loop]
        if (declaredVariableSymbolsInLoop.isEmpty())
            return visitNode(node, data)

        return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data)
    }

    // A merge point for do-while loop
    override fun visitLoopBlockEnterNode(
        node: LoopBlockEnterNode,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        val declaredVariableSymbolsInLoop = declaredVariableCollector.declaredVariablesPerElement[node.fir]
        if (declaredVariableSymbolsInLoop.isEmpty())
            return visitNode(node, data)

        return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data)
    }

    private fun filterDeclaredVariableSymbolsInCapturedScope(
        node: CFGNode<*>,
        declaredVariableSymbolsInCapturedScope: Collection,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        var filteredData = data
        for (variableSymbol in declaredVariableSymbolsInCapturedScope) {
            filteredData = filteredData.map { (label, pathAwareInfo) ->
                label to if (label is LoopBackPath) {
                    removeRange(pathAwareInfo, variableSymbol, ::PathAwarePropertyInitializationInfo)
                } else {
                    pathAwareInfo
                }
            }
        }
        return visitNode(node, filteredData)
    }

    override fun visitLoopExitNode(
        node: LoopExitNode,
        data: Collection>
    ): PathAwarePropertyInitializationInfo {
        exitCapturingStatement(node.fir)
        return visitNode(node, data)
    }
}

internal fun 

, S : ControlFlowInfo, K : Any> addRange( pathAwareInfo: P, key: K, range: EventOccurrencesRange, constructor: (PersistentMap) -> P ): P { // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } // after (if key is p1): // { |-> { p1 |-> PI1 + r }, l1 |-> { p1 |-> r, p2 |-> PI2 } } return updateRange(pathAwareInfo, key, { existingKind -> existingKind + range }, constructor) } private fun

, S : ControlFlowInfo, K : Any> overwriteRange( pathAwareInfo: P, key: K, range: EventOccurrencesRange, constructor: (PersistentMap) -> P ): P { // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } // after (if key is p1): // { |-> { p1 |-> r }, l1 |-> { p1 |-> r, p2 |-> PI2 } } return updateRange(pathAwareInfo, key, { range }, constructor) } private inline fun

, S : ControlFlowInfo, K : Any> updateRange( pathAwareInfo: P, key: K, computeNewRange: (EventOccurrencesRange) -> EventOccurrencesRange, constructor: (PersistentMap) -> P ): P { var resultMap = persistentMapOf() // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } for ((label, dataPerLabel) in pathAwareInfo) { val existingKind = dataPerLabel[key] ?: EventOccurrencesRange.ZERO val kind = computeNewRange.invoke(existingKind) resultMap = resultMap.put(label, dataPerLabel.put(key, kind)) } // after (if key is p1): // { |-> { p1 |-> computeNewRange(PI1) }, l1 |-> { p1 |-> r, p2 |-> PI2 } } return constructor(resultMap) } private fun

, S : ControlFlowInfo, K : Any> removeRange( pathAwareInfo: P, key: K, constructor: (PersistentMap) -> P ): P { var resultMap = persistentMapOf() // before: { |-> { p1 |-> PI1 }, l1 |-> { p2 |-> PI2 } } for ((label, dataPerLabel) in pathAwareInfo) { resultMap = resultMap.put(label, dataPerLabel.remove(key)) } // after (if key is p1): // { |-> { }, l1 |-> { p2 |-> PI2 } } return constructor(resultMap) }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy