
org.jetbrains.kotlin.cfg.ControlFlowProcessor.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2016 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
import com.google.common.collect.Lists
import com.intellij.psi.tree.IElementType
import com.intellij.psi.util.PsiTreeUtil
import com.intellij.util.SmartFMap
import com.intellij.util.containers.ContainerUtil
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
import org.jetbrains.kotlin.cfg.ControlFlowBuilder.PredefinedOperation.*
import org.jetbrains.kotlin.cfg.pseudocode.ControlFlowInstructionsGenerator
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
import org.jetbrains.kotlin.cfg.pseudocode.PseudocodeImpl
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.AccessTarget
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.InstructionWithValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.MagicKind
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors.*
import org.jetbrains.kotlin.lexer.KtToken
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.lexer.KtTokens.*
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingContextUtils
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import java.util.*
class ControlFlowProcessor(private val trace: BindingTrace) {
private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()
fun generatePseudocode(subroutine: KtElement): Pseudocode {
val pseudocode = generate(subroutine)
(pseudocode as PseudocodeImpl).postProcess()
return pseudocode
}
private fun generate(subroutine: KtElement): Pseudocode {
builder.enterSubroutine(subroutine)
val cfpVisitor = CFPVisitor(builder)
if (subroutine is KtDeclarationWithBody && subroutine !is KtSecondaryConstructor) {
val valueParameters = subroutine.valueParameters
for (valueParameter in valueParameters) {
cfpVisitor.generateInstructions(valueParameter)
}
val bodyExpression = subroutine.bodyExpression
if (bodyExpression != null) {
cfpVisitor.generateInstructions(bodyExpression)
if (!subroutine.hasBlockBody()) {
generateImplicitReturnValue(bodyExpression, subroutine)
}
}
}
else {
cfpVisitor.generateInstructions(subroutine)
}
return builder.exitSubroutine(subroutine)
}
private fun generateImplicitReturnValue(bodyExpression: KtExpression, subroutine: KtElement) {
val subroutineDescriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine) as CallableDescriptor? ?: return
val returnType = subroutineDescriptor.returnType
if (returnType != null && KotlinBuiltIns.isUnit(returnType) && subroutineDescriptor is AnonymousFunctionDescriptor) return
val returnValue = builder.getBoundValue(bodyExpression) ?: return
builder.returnValue(bodyExpression, returnValue, subroutine)
}
private fun processLocalDeclaration(subroutine: KtDeclaration) {
val afterDeclaration = builder.createUnboundLabel("after local declaration")
builder.nondeterministicJump(afterDeclaration, subroutine, null)
generate(subroutine)
builder.bindLabel(afterDeclaration)
}
private inner class CFPVisitor(private val builder: ControlFlowBuilder) : KtVisitorVoid() {
private val conditionVisitor = object : KtVisitorVoid() {
private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? {
return condition.getStrictParentOfType()?.subjectExpression
}
override fun visitWhenConditionInRange(condition: KtWhenConditionInRange) {
if (!generateCall(condition.operationReference)) {
val rangeExpression = condition.rangeExpression
generateInstructions(rangeExpression)
createNonSyntheticValue(condition, MagicKind.UNRESOLVED_CALL, rangeExpression)
}
}
override fun visitWhenConditionIsPattern(condition: KtWhenConditionIsPattern) {
mark(condition)
createNonSyntheticValue(condition, MagicKind.IS, getSubjectExpression(condition))
}
override fun visitWhenConditionWithExpression(condition: KtWhenConditionWithExpression) {
mark(condition)
val expression = condition.expression
generateInstructions(expression)
val subjectExpression = getSubjectExpression(condition)
if (subjectExpression != null) {
// todo: this can be replaced by equals() invocation (when corresponding resolved call is recorded)
createNonSyntheticValue(condition, MagicKind.EQUALS_IN_WHEN_CONDITION, subjectExpression, expression)
}
else {
copyValue(expression, condition)
}
}
override fun visitKtElement(element: KtElement) {
throw UnsupportedOperationException("[ControlFlowProcessor] " + element.toString())
}
}
private fun mark(element: KtElement) {
builder.mark(element)
}
fun generateInstructions(element: KtElement?) {
if (element == null) return
element.accept(this)
checkNothingType(element)
}
private fun checkNothingType(element: KtElement) {
if (element !is KtExpression) return
val expression = KtPsiUtil.deparenthesize(element) ?: return
if (expression is KtStatementExpression || expression is KtTryExpression
|| expression is KtIfExpression || expression is KtWhenExpression) {
return
}
val type = trace.bindingContext.getType(expression)
if (type != null && KotlinBuiltIns.isNothing(type)) {
builder.jumpToError(expression)
}
}
private fun createSyntheticValue(instructionElement: KtElement, kind: MagicKind, vararg from: KtElement): PseudoValue {
return builder.magic(instructionElement, null, elementsToValues(from.asList()), kind).outputValue
}
private fun createNonSyntheticValue(to: KtElement, from: List, kind: MagicKind): PseudoValue {
return builder.magic(to, to, elementsToValues(from), kind).outputValue
}
private fun createNonSyntheticValue(to: KtElement, kind: MagicKind, vararg from: KtElement?): PseudoValue {
return createNonSyntheticValue(to, from.asList(), kind)
}
private fun mergeValues(from: List, to: KtExpression) {
builder.merge(to, elementsToValues(from))
}
private fun copyValue(from: KtElement?, to: KtElement) {
getBoundOrUnreachableValue(from)?.let { builder.bindValue(it, to) }
}
private fun getBoundOrUnreachableValue(element: KtElement?): PseudoValue? {
if (element == null) return null
val value = builder.getBoundValue(element)
return if (value != null || element is KtDeclaration) value else builder.newValue(element)
}
private fun elementsToValues(from: List): List {
return from.mapNotNull { element -> getBoundOrUnreachableValue(element) }
}
private fun generateInitializer(declaration: KtDeclaration, initValue: PseudoValue) {
builder.write(declaration, declaration, initValue, getDeclarationAccessTarget(declaration), emptyMap())
}
private fun getResolvedCallAccessTarget(element: KtElement?): AccessTarget {
return element.getResolvedCall(trace.bindingContext)?.let { AccessTarget.Call(it) }
?: AccessTarget.BlackBox
}
private fun getDeclarationAccessTarget(element: KtElement): AccessTarget {
val descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element)
return if (descriptor is VariableDescriptor)
AccessTarget.Declaration(descriptor)
else
AccessTarget.BlackBox
}
override fun visitParenthesizedExpression(expression: KtParenthesizedExpression) {
mark(expression)
val innerExpression = expression.expression
if (innerExpression != null) {
generateInstructions(innerExpression)
copyValue(innerExpression, expression)
}
}
override fun visitAnnotatedExpression(expression: KtAnnotatedExpression) {
val baseExpression = expression.baseExpression
if (baseExpression != null) {
generateInstructions(baseExpression)
copyValue(baseExpression, expression)
}
}
override fun visitThisExpression(expression: KtThisExpression) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall == null) {
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL)
return
}
val resultingDescriptor = resolvedCall.resultingDescriptor
if (resultingDescriptor is ReceiverParameterDescriptor) {
builder.readVariable(expression, resolvedCall, getReceiverValues(resolvedCall))
}
copyValue(expression, expression.instanceReference)
}
override fun visitConstantExpression(expression: KtConstantExpression) {
val constant = ConstantExpressionEvaluator.getConstant(expression, trace.bindingContext)
builder.loadConstant(expression, constant)
}
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall is VariableAsFunctionResolvedCall) {
generateCall(resolvedCall.variableCall)
}
else if (!generateCall(expression) && expression.parent !is KtCallExpression) {
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, generateAndGetReceiverIfAny(expression))
}
}
override fun visitLabeledExpression(expression: KtLabeledExpression) {
mark(expression)
val baseExpression = expression.baseExpression
if (baseExpression != null) {
generateInstructions(baseExpression)
copyValue(baseExpression, expression)
}
}
override fun visitBinaryExpression(expression: KtBinaryExpression) {
val operationReference = expression.operationReference
val operationType = operationReference.getReferencedNameElementType()
val left = expression.left
val right = expression.right
if (operationType === ANDAND || operationType === OROR) {
generateBooleanOperation(expression)
}
else if (operationType === EQ) {
visitAssignment(left, getDeferredValue(right), expression)
}
else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
if (resolvedCall != null) {
val rhsValue = generateCall(resolvedCall).outputValue
val assignMethodName = OperatorConventions.getNameForOperationSymbol(expression.operationToken as KtToken)
if (resolvedCall.resultingDescriptor.name != assignMethodName) {
/* At this point assignment of the form a += b actually means a = a + b
* So we first generate call of "+" operation and then use its output pseudo-value
* as a right-hand side when generating assignment call
*/
visitAssignment(left, getValueAsFunction(rhsValue), expression)
}
}
else {
generateBothArgumentsAndMark(expression)
}
}
else if (operationType === ELVIS) {
generateInstructions(left)
mark(expression)
val afterElvis = builder.createUnboundLabel("after elvis operator")
builder.jumpOnTrue(afterElvis, expression, builder.getBoundValue(left))
generateInstructions(right)
builder.bindLabel(afterElvis)
mergeValues(listOf(left, right).filterNotNull(), expression)
}
else {
if (!generateCall(expression)) {
generateBothArgumentsAndMark(expression)
}
}
}
private fun generateBooleanOperation(expression: KtBinaryExpression) {
val operationType = expression.operationReference.getReferencedNameElementType()
val left = expression.left
val right = expression.right
val resultLabel = builder.createUnboundLabel("result of boolean operation")
generateInstructions(left)
if (operationType === ANDAND) {
builder.jumpOnFalse(resultLabel, expression, builder.getBoundValue(left))
}
else {
builder.jumpOnTrue(resultLabel, expression, builder.getBoundValue(left))
}
generateInstructions(right)
builder.bindLabel(resultLabel)
val operation = if (operationType === ANDAND) AND else OR
builder.predefinedOperation(expression, operation, elementsToValues(listOf(left, right).filterNotNull()))
}
private fun getValueAsFunction(value: PseudoValue?) = { value }
private fun getDeferredValue(expression: KtExpression?) = {
generateInstructions(expression)
getBoundOrUnreachableValue(expression)
}
private fun generateBothArgumentsAndMark(expression: KtBinaryExpression) {
val left = KtPsiUtil.deparenthesize(expression.left)
if (left != null) {
generateInstructions(left)
}
val right = expression.right
if (right != null) {
generateInstructions(right)
}
mark(expression)
createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, left, right)
}
private fun visitAssignment(
lhs: KtExpression?,
rhsDeferredValue: () -> PseudoValue?,
parentExpression: KtExpression
) {
val left = KtPsiUtil.deparenthesize(lhs)
if (left == null) {
val arguments = rhsDeferredValue()?.let { listOf(it) } ?: emptyList()
builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNSUPPORTED_ELEMENT)
return
}
if (left is KtArrayAccessExpression) {
generateArrayAssignment(left, rhsDeferredValue, parentExpression)
return
}
var receiverValues: Map = SmartFMap.emptyMap()
var accessTarget: AccessTarget = AccessTarget.BlackBox
if (left is KtSimpleNameExpression || left is KtQualifiedExpression) {
accessTarget = getResolvedCallAccessTarget(left.getQualifiedElementSelector())
if (accessTarget is AccessTarget.Call) {
receiverValues = getReceiverValues(accessTarget.resolvedCall)
}
}
else if (left is KtProperty) {
accessTarget = getDeclarationAccessTarget(left)
}
if (accessTarget === AccessTarget.BlackBox && left !is KtProperty) {
generateInstructions(left)
createSyntheticValue(left, MagicKind.VALUE_CONSUMER, left)
}
val rightValue = rhsDeferredValue.invoke()
val rValue = rightValue ?: createSyntheticValue(parentExpression, MagicKind.UNRECOGNIZED_WRITE_RHS)
builder.write(parentExpression, left, rValue, accessTarget, receiverValues)
}
private fun generateArrayAssignment(
lhs: KtArrayAccessExpression,
rhsDeferredValue: () -> PseudoValue?,
parentExpression: KtExpression
) {
val setResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_SET, lhs)
if (setResolvedCall == null) {
generateArrayAccess(lhs, null)
val arguments = listOf(getBoundOrUnreachableValue(lhs), rhsDeferredValue.invoke()).filterNotNull()
builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNRESOLVED_CALL)
return
}
// In case of simple ('=') array assignment mark instruction is not generated yet, so we put it before generating "set" call
if ((parentExpression as KtOperationExpression).operationReference.getReferencedNameElementType() === EQ) {
mark(lhs)
}
generateInstructions(lhs.arrayExpression)
val receiverValues = getReceiverValues(setResolvedCall)
val argumentValues = getArraySetterArguments(rhsDeferredValue, setResolvedCall)
builder.call(parentExpression, setResolvedCall, receiverValues, argumentValues)
}
/* We assume that assignment right-hand side corresponds to the last argument of the call
* So receiver instructions/pseudo-values are generated for all arguments except the last one which is replaced
* by pre-generated pseudo-value
* For example, assignment a[1, 2] += 3 means a.set(1, 2, a.get(1) + 3), so in order to generate "set" call
* we first generate instructions for 1 and 2 whereas 3 is replaced by pseudo-value corresponding to "a.get(1) + 3"
*/
private fun getArraySetterArguments(
rhsDeferredValue: () -> PseudoValue?,
setResolvedCall: ResolvedCall
): SmartFMap {
val valueArguments = setResolvedCall.resultingDescriptor.valueParameters.flatMapTo(
ArrayList()
) { descriptor -> setResolvedCall.valueArguments[descriptor]?.arguments ?: emptyList() }
val rhsArgument = valueArguments.lastOrNull()
var argumentValues = SmartFMap.emptyMap()
for (valueArgument in valueArguments) {
val argumentMapping = setResolvedCall.getArgumentMapping(valueArgument)
if (argumentMapping.isError() || argumentMapping !is ArgumentMatch) continue
val parameterDescriptor = argumentMapping.valueParameter
if (valueArgument !== rhsArgument) {
argumentValues = generateValueArgument(valueArgument, parameterDescriptor, argumentValues)
}
else {
val rhsValue = rhsDeferredValue.invoke()
if (rhsValue != null) {
argumentValues = argumentValues.plus(rhsValue, parameterDescriptor)
}
}
}
return argumentValues
}
private fun generateArrayAccess(arrayAccessExpression: KtArrayAccessExpression, resolvedCall: ResolvedCall<*>?) {
if (builder.getBoundValue(arrayAccessExpression) != null) return
mark(arrayAccessExpression)
if (!checkAndGenerateCall(resolvedCall)) {
generateArrayAccessWithoutCall(arrayAccessExpression)
}
}
private fun generateArrayAccessWithoutCall(arrayAccessExpression: KtArrayAccessExpression) {
createNonSyntheticValue(arrayAccessExpression, generateArrayAccessArguments(arrayAccessExpression), MagicKind.UNRESOLVED_CALL)
}
private fun generateArrayAccessArguments(arrayAccessExpression: KtArrayAccessExpression): List {
val inputExpressions = ArrayList()
val arrayExpression = arrayAccessExpression.arrayExpression
if (arrayExpression != null) {
inputExpressions.add(arrayExpression)
}
generateInstructions(arrayExpression)
for (index in arrayAccessExpression.indexExpressions) {
generateInstructions(index)
inputExpressions.add(index)
}
return inputExpressions
}
override fun visitUnaryExpression(expression: KtUnaryExpression) {
val operationSign = expression.operationReference
val operationType = operationSign.getReferencedNameElementType()
val baseExpression = expression.baseExpression ?: return
if (KtTokens.EXCLEXCL === operationType) {
generateInstructions(baseExpression)
builder.predefinedOperation(expression, NOT_NULL_ASSERTION, elementsToValues(listOf(baseExpression)))
return
}
val incrementOrDecrement = isIncrementOrDecrement(operationType)
val resolvedCall = expression.getResolvedCall(trace.bindingContext)
val rhsValue: PseudoValue?
if (resolvedCall != null) {
rhsValue = generateCall(resolvedCall).outputValue
}
else {
generateInstructions(baseExpression)
rhsValue = createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression)
}
if (incrementOrDecrement) {
visitAssignment(baseExpression, getValueAsFunction(rhsValue), expression)
if (expression is KtPostfixExpression) {
copyValue(baseExpression, expression)
}
}
}
private fun isIncrementOrDecrement(operationType: IElementType): Boolean {
return operationType === KtTokens.PLUSPLUS || operationType === KtTokens.MINUSMINUS
}
override fun visitIfExpression(expression: KtIfExpression) {
mark(expression)
val branches = ArrayList(2)
val condition = expression.condition
generateInstructions(condition)
val elseLabel = builder.createUnboundLabel("else branch")
builder.jumpOnFalse(elseLabel, expression, builder.getBoundValue(condition))
val thenBranch = expression.then
if (thenBranch != null) {
branches.add(thenBranch)
generateInstructions(thenBranch)
}
else {
builder.loadUnit(expression)
}
val resultLabel = builder.createUnboundLabel("'if' expression result")
builder.jump(resultLabel, expression)
builder.bindLabel(elseLabel)
val elseBranch = expression.`else`
if (elseBranch != null) {
branches.add(elseBranch)
generateInstructions(elseBranch)
}
else {
builder.loadUnit(expression)
}
builder.bindLabel(resultLabel)
mergeValues(branches, expression)
}
private inner class FinallyBlockGenerator(private val finallyBlock: KtFinallySection?) {
private var startFinally: Label? = null
private var finishFinally: Label? = null
fun generate() {
val finalExpression = finallyBlock?.finalExpression ?: return
startFinally?.let {
assert(finishFinally != null) { "startFinally label is set to $startFinally but finishFinally label is not set" }
builder.repeatPseudocode(it, finishFinally!!)
return
}
builder.createUnboundLabel("start finally").let {
startFinally = it
builder.bindLabel(it)
}
generateInstructions(finalExpression)
builder.createUnboundLabel("finish finally").let {
finishFinally = it
builder.bindLabel(it)
}
}
}
override fun visitTryExpression(expression: KtTryExpression) {
mark(expression)
val finallyBlock = expression.finallyBlock
val finallyBlockGenerator = FinallyBlockGenerator(finallyBlock)
val hasFinally = finallyBlock != null
if (hasFinally) {
builder.enterTryFinally(object : GenerationTrigger {
private var working = false
override fun generate() {
// This checks are needed for the case of having e.g. return inside finally: 'try {return} finally{return}'
if (working) return
working = true
finallyBlockGenerator.generate()
working = false
}
})
}
val onExceptionToFinallyBlock = generateTryAndCatches(expression)
if (hasFinally) {
assert(onExceptionToFinallyBlock != null) { "No finally label generated: " + expression.text }
builder.exitTryFinally()
val skipFinallyToErrorBlock = builder.createUnboundLabel("skipFinallyToErrorBlock")
builder.jump(skipFinallyToErrorBlock, expression)
builder.bindLabel(onExceptionToFinallyBlock!!)
finallyBlockGenerator.generate()
builder.jumpToError(expression)
builder.bindLabel(skipFinallyToErrorBlock)
finallyBlockGenerator.generate()
}
val branches = ArrayList()
branches.add(expression.tryBlock)
for (catchClause in expression.catchClauses) {
catchClause.catchBody?.let { branches.add(it) }
}
mergeValues(branches, expression)
}
// Returns label for 'finally' block
private fun generateTryAndCatches(expression: KtTryExpression): Label? {
val catchClauses = expression.catchClauses
val hasCatches = !catchClauses.isEmpty()
var onException: Label? = null
if (hasCatches) {
onException = builder.createUnboundLabel("onException")
builder.nondeterministicJump(onException, expression, null)
}
var onExceptionToFinallyBlock: Label? = null
if (expression.finallyBlock != null) {
onExceptionToFinallyBlock = builder.createUnboundLabel("onExceptionToFinallyBlock")
builder.nondeterministicJump(onExceptionToFinallyBlock, expression, null)
}
val tryBlock = expression.tryBlock
generateInstructions(tryBlock)
if (hasCatches && onException != null) {
val afterCatches = builder.createUnboundLabel("afterCatches")
builder.jump(afterCatches, expression)
builder.bindLabel(onException)
val catchLabels = Lists.newLinkedList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy