org.jetbrains.kotlin.cfg.pseudocode.ControlFlowInstructionsGenerator.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of kotlin-compiler-embeddable Show documentation
Show all versions of kotlin-compiler-embeddable Show documentation
the Kotlin compiler embeddable
/*
* 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