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

org.jetbrains.kotlin.fir.analysis.cfa.util.LocalPropertyAndCapturedWriteCollector.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 org.jetbrains.kotlin.contracts.description.isInPlace
import org.jetbrains.kotlin.fir.declarations.FirAnonymousFunction
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.utils.referredPropertySymbol
import org.jetbrains.kotlin.fir.expressions.FirVariableAssignment
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirSyntheticPropertySymbol

class LocalPropertyAndCapturedWriteCollector private constructor() : ControlFlowGraphVisitorVoid() {
    companion object {
        fun collect(graph: ControlFlowGraph): Pair, Set> {
            val collector = LocalPropertyAndCapturedWriteCollector()
            graph.traverse(TraverseDirection.Forward, collector)
            return collector.symbols.keys to collector.capturedWrites
        }
    }

    // Mapping from a property symbol to its declaration context
    // `true` if the (local) property is declared in the currently visited function.
    // `false` if it is declared in a lambda or a local function (inside the currently visited function).
    private val symbols: MutableMap = mutableMapOf()

    private val lambdaOrLocalFunctionStack: MutableList = mutableListOf()
    private val capturedWrites: MutableSet = mutableSetOf()

    override fun visitNode(node: CFGNode<*>) {}

    override fun visitVariableDeclarationNode(node: VariableDeclarationNode) {
        symbols[node.fir.symbol] = lambdaOrLocalFunctionStack.lastOrNull() == null
    }

    override fun visitPostponedLambdaEnterNode(node: PostponedLambdaEnterNode) {
        lambdaOrLocalFunctionStack.add(node.fir)
    }

    override fun visitPostponedLambdaExitNode(node: PostponedLambdaExitNode) {
        lambdaOrLocalFunctionStack.remove(node.fir.anonymousFunction)
    }

    override fun visitLocalFunctionDeclarationNode(node: LocalFunctionDeclarationNode, data: Nothing?) {
        lambdaOrLocalFunctionStack.add(node.fir)
    }

    override fun visitFunctionExitNode(node: FunctionExitNode) {
        lambdaOrLocalFunctionStack.remove(node.fir)
    }

    override fun visitVariableAssignmentNode(node: VariableAssignmentNode) {
        val symbol = node.fir.referredPropertySymbol ?: return
        if (symbol is FirSyntheticPropertySymbol) {
            symbols[symbol] = true
            return
        }

        // Check if this variable assignment is inside a lambda or a local function.
        if (lambdaOrLocalFunctionStack.isEmpty()) return

        // Check if the assigned variable doesn't belong to any lambda or local function.
        if (symbol !in symbols || symbols[symbol] == false) return

        // If all nested declarations are lambdas that are invoked in-place (according to the contract),
        // this variable assignment is not a captured write.
        if (lambdaOrLocalFunctionStack.all { it is FirAnonymousFunction && it.invocationKind.isInPlace }) return

        capturedWrites.add(node.fir)
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy