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

org.jetbrains.kotlin.cfg.pseudocode.ControlFlowInstructionsGenerator.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * Copyright 2010-2015 JetBrains s.r.o.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.jetbrains.kotlin.cfg.pseudocode

import com.intellij.util.containers.Stack
import org.jetbrains.kotlin.cfg.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.BlockScope
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.special.*
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import java.util.*

class ControlFlowInstructionsGenerator : ControlFlowBuilderAdapter() {
    private var builder: ControlFlowBuilder? = null

    override val delegateBuilder: ControlFlowBuilder
        get() = builder ?: throw AssertionError("Builder stack is empty in ControlFlowInstructionsGenerator!")

    private val loopInfo = Stack()
    private val blockScopes = Stack()
    private val elementToLoopInfo = HashMap()
    private val elementToSubroutineInfo = HashMap()
    private var labelCount = 0

    private val builders = Stack()

    private val allBlocks = Stack()

    private fun pushBuilder(scopingElement: KtElement, subroutine: KtElement, shouldInline: Boolean) {
        val worker = ControlFlowInstructionsGeneratorWorker(scopingElement, subroutine, shouldInline)
        builders.push(worker)
        builder = worker
    }

    private fun popBuilder(): ControlFlowInstructionsGeneratorWorker {
        val worker = builders.pop()
        builder = if (!builders.isEmpty()) {
            builders.peek()
        } else {
            null
        }
        return worker
    }

    override fun enterSubroutine(subroutine: KtElement, eventOccurrencesRange: EventOccurrencesRange?) {
        val builder = builder
        val shouldInlnie = eventOccurrencesRange != null
        if (builder != null && subroutine is KtFunctionLiteral) {
            pushBuilder(subroutine, builder.returnSubroutine, shouldInlnie)
        } else {
            pushBuilder(subroutine, subroutine, shouldInlnie)
        }
        delegateBuilder.enterBlockScope(subroutine)
        delegateBuilder.enterSubroutine(subroutine)
    }

    override fun exitSubroutine(subroutine: KtElement, eventOccurrencesRange: EventOccurrencesRange?): Pseudocode {
        super.exitSubroutine(subroutine, eventOccurrencesRange)
        delegateBuilder.exitBlockScope(subroutine)
        val worker = popBuilder()
        if (!builders.empty()) {
            val builder = builders.peek()
            if (eventOccurrencesRange == null) {
                builder.declareFunction(subroutine, worker.pseudocode)
            } else {
                builder.declareInlinedFunction(subroutine, worker.pseudocode, eventOccurrencesRange)
            }
        }
        return worker.pseudocode
    }

    private inner class ControlFlowInstructionsGeneratorWorker(
        scopingElement: KtElement,
        override val returnSubroutine: KtElement,
        shouldInline: Boolean
    ) : ControlFlowBuilder {

        val pseudocode: PseudocodeImpl = PseudocodeImpl(scopingElement, shouldInline)
        private val error: Label = pseudocode.createLabel("error", null)
        private val sink: Label = pseudocode.createLabel("sink", null)

        private val valueFactory = object : PseudoValueFactoryImpl() {
            override fun newValue(element: KtElement?, instruction: InstructionWithValue?): PseudoValue {
                val value = super.newValue(element, instruction)
                if (element != null) {
                    bindValue(value, element)
                }
                return value
            }
        }

        private fun add(instruction: Instruction) {
            pseudocode.addInstruction(instruction)
        }

        override fun createUnboundLabel(): Label = pseudocode.createLabel("L" + labelCount++, null)

        override fun createUnboundLabel(name: String): Label = pseudocode.createLabel("L" + labelCount++, name)

        override fun enterLoop(expression: KtLoopExpression): LoopInfo {
            if (expression is KtDoWhileExpression) {
                (pseudocode.rootPseudocode as PseudocodeImpl).containsDoWhile = true
            }

            val info = LoopInfo(
                expression,
                createUnboundLabel("loop entry point"),
                createUnboundLabel("loop exit point"),
                createUnboundLabel("body entry point"),
                createUnboundLabel("body exit point"),
                createUnboundLabel("condition entry point")
            )
            bindLabel(info.entryPoint)
            elementToLoopInfo.put(expression, info)
            return info
        }

        override fun enterLoopBody(expression: KtLoopExpression) {
            val info = elementToLoopInfo[expression]!!
            bindLabel(info.bodyEntryPoint)
            loopInfo.push(info)
            allBlocks.push(info)
        }

        override fun exitLoopBody(expression: KtLoopExpression) {
            val info = loopInfo.pop()
            elementToLoopInfo.remove(expression)
            allBlocks.pop()
            bindLabel(info.bodyExitPoint)
        }

        override val currentLoop: KtLoopExpression?
            get() = if (loopInfo.empty()) null else loopInfo.peek().element

        override fun enterSubroutine(subroutine: KtElement, eventOccurrencesRange: EventOccurrencesRange?) {
            val blockInfo = SubroutineInfo(
                subroutine,
                /* entry point */ createUnboundLabel(),
                /* exit point  */ createUnboundLabel()
            )
            elementToSubroutineInfo.put(subroutine, blockInfo)
            allBlocks.push(blockInfo)
            bindLabel(blockInfo.entryPoint)
            add(SubroutineEnterInstruction(subroutine, currentScope))
        }

        override val currentSubroutine: KtElement
            get() = pseudocode.correspondingElement

        override fun getLoopConditionEntryPoint(loop: KtLoopExpression): Label? = elementToLoopInfo[loop]?.conditionEntryPoint

        override fun getLoopExitPoint(loop: KtLoopExpression): Label? =// It's quite possible to have null here, see testBreakInsideLocal
            elementToLoopInfo[loop]?.exitPoint

        override fun getSubroutineExitPoint(labelElement: KtElement): Label? =
// It's quite possible to have null here, e.g. for non-local returns (see KT-10823)
            elementToSubroutineInfo[labelElement]?.exitPoint

        private val currentScope: BlockScope
            get() = blockScopes.peek()

        override fun enterBlockScope(block: KtElement) {
            val current = if (blockScopes.isEmpty()) null else currentScope
            val scope = BlockScope(current, block)
            blockScopes.push(scope)
        }

        override fun exitBlockScope(block: KtElement) {
            val currentScope = currentScope
            assert(currentScope.block === block) {
                "Exit from not the current block scope.\n" +
                        "Current scope is for a block: " + currentScope.block.text + ".\n" +
                        "Exit from the scope for: " + block.text
            }
            blockScopes.pop()
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        private fun handleJumpInsideTryFinally(jumpTarget: Label) {
            val finallyBlocks = ArrayList()

            for (blockInfo in allBlocks.asReversed()) {
                when (blockInfo) {
                    is BreakableBlockInfo -> if (blockInfo.referablePoints.contains(jumpTarget) || jumpTarget === error) {
                        for (finallyBlockInfo in finallyBlocks) {
                            finallyBlockInfo.generateFinallyBlock()
                        }
                        return
                    }
                    is TryFinallyBlockInfo -> finallyBlocks.add(blockInfo)
                }
            }
        }

        override fun exitSubroutine(subroutine: KtElement, eventOccurrencesRange: EventOccurrencesRange?): Pseudocode {
            getSubroutineExitPoint(subroutine)?.let { bindLabel(it) }
            pseudocode.addExitInstruction(SubroutineExitInstruction(subroutine, currentScope, false))
            bindLabel(error)
            pseudocode.addErrorInstruction(SubroutineExitInstruction(subroutine, currentScope, true))
            bindLabel(sink)
            pseudocode.addSinkInstruction(SubroutineSinkInstruction(subroutine, currentScope, ""))
            elementToSubroutineInfo.remove(subroutine)
            allBlocks.pop()
            return pseudocode
        }

        override fun mark(element: KtElement) {
            add(MarkInstruction(element, currentScope))
        }

        override fun getBoundValue(element: KtElement?): PseudoValue? = pseudocode.getElementValue(element)

        override fun bindValue(value: PseudoValue, element: KtElement) {
            pseudocode.bindElementToValue(element, value)
        }

        override fun newValue(element: KtElement?): PseudoValue = valueFactory.newValue(element, null)

        override fun returnValue(returnExpression: KtExpression, returnValue: PseudoValue, subroutine: KtElement) {
            val exitPoint = getSubroutineExitPoint(subroutine) ?: return
            handleJumpInsideTryFinally(exitPoint)
            add(ReturnValueInstruction(returnExpression, currentScope, exitPoint, returnValue, subroutine))
        }

        override fun returnNoValue(returnExpression: KtReturnExpression, subroutine: KtElement) {
            val exitPoint = getSubroutineExitPoint(subroutine) ?: return
            handleJumpInsideTryFinally(exitPoint)
            add(ReturnNoValueInstruction(returnExpression, currentScope, exitPoint, subroutine))
        }

        override fun write(
            assignment: KtElement,
            lValue: KtElement,
            rValue: PseudoValue,
            target: AccessTarget,
            receiverValues: Map
        ) {
            add(WriteValueInstruction(assignment, currentScope, target, receiverValues, lValue, rValue))
        }

        override fun declareParameter(parameter: KtParameter) {
            add(VariableDeclarationInstruction(parameter, currentScope))
        }

        override fun declareVariable(property: KtVariableDeclaration) {
            add(VariableDeclarationInstruction(property, currentScope))
        }

        override fun declareFunction(subroutine: KtElement, pseudocode: Pseudocode) {
            add(LocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope))
        }

        override fun declareInlinedFunction(subroutine: KtElement, pseudocode: Pseudocode, eventOccurrencesRange: EventOccurrencesRange) {
            add(InlinedLocalFunctionDeclarationInstruction(subroutine, pseudocode, currentScope, eventOccurrencesRange))
        }

        override fun declareEntryOrObject(entryOrObject: KtClassOrObject) {
            add(VariableDeclarationInstruction(entryOrObject, currentScope))
        }

        override fun loadUnit(expression: KtExpression) {
            add(LoadUnitValueInstruction(expression, currentScope))
        }

        override fun jump(label: Label, element: KtElement) {
            handleJumpInsideTryFinally(label)
            add(UnconditionalJumpInstruction(element, label, currentScope))
        }

        override fun jumpOnFalse(label: Label, element: KtElement, conditionValue: PseudoValue?) {
            handleJumpInsideTryFinally(label)
            add(ConditionalJumpInstruction(element, false, currentScope, label, conditionValue))
        }

        override fun jumpOnTrue(label: Label, element: KtElement, conditionValue: PseudoValue?) {
            handleJumpInsideTryFinally(label)
            add(ConditionalJumpInstruction(element, true, currentScope, label, conditionValue))
        }

        override fun bindLabel(label: Label) {
            pseudocode.bindLabel(label as PseudocodeLabel)
        }

        override fun nondeterministicJump(label: Label, element: KtElement, inputValue: PseudoValue?) {
            handleJumpInsideTryFinally(label)
            add(NondeterministicJumpInstruction(element, listOf(label), currentScope, inputValue))
        }

        override fun nondeterministicJump(label: List




© 2015 - 2024 Weber Informatics LLC | Privacy Policy