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

org.jetbrains.kotlin.cfg.ControlFlowProcessor.kt Maven / Gradle / Ivy

There is a newer version: 2.1.0-RC
Show newest version
/*
 * 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.intellij.psi.PsiElement
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.NOT_NULL_ASSERTION
import org.jetbrains.kotlin.cfg.ControlFlowBuilder.PredefinedOperation.OR
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.config.LanguageFeature
import org.jetbrains.kotlin.config.LanguageFeature.BreakContinueInInlineLambdas
import org.jetbrains.kotlin.config.LanguageFeature.ProhibitQualifiedAccessToUninitializedEnumEntry
import org.jetbrains.kotlin.config.LanguageVersionSettings
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
import org.jetbrains.kotlin.contracts.description.canBeRevisited
import org.jetbrains.kotlin.contracts.description.isDefinitelyVisited
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.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.BindingTrace
import org.jetbrains.kotlin.resolve.CompileTimeConstantUtils
import org.jetbrains.kotlin.resolve.bindingContextUtil.getEnclosingFunctionDescriptor
import org.jetbrains.kotlin.resolve.bindingContextUtil.recordUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.util.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.calls.tower.getFakeDescriptorForObject
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator
import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf
import org.jetbrains.kotlin.resolve.scopes.receivers.*
import org.jetbrains.kotlin.types.expressions.DoubleColonLHS
import org.jetbrains.kotlin.types.expressions.OperatorConventions
import org.jetbrains.kotlin.types.typeUtil.isNothing
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.util.slicedMap.ReadOnlySlice
import java.util.*

typealias DeferredGenerator = (ControlFlowBuilder) -> Unit

class ControlFlowProcessor(
    private val trace: BindingTrace,
    private val languageVersionSettings: LanguageVersionSettings
) {

    private val builder: ControlFlowBuilder = ControlFlowInstructionsGenerator()

    fun generatePseudocode(subroutine: KtElement): Pseudocode {
        val pseudocode = generate(subroutine, null)
        (pseudocode as PseudocodeImpl).postProcess()
        return pseudocode
    }

    private fun generate(subroutine: KtElement, eventOccurrencesRange: EventOccurrencesRange? = null): Pseudocode {
        builder.enterSubroutine(subroutine, eventOccurrencesRange)
        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, eventOccurrencesRange)
    }

    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, null)
        builder.bindLabel(afterDeclaration)
    }

    private class CatchFinallyLabels(val onException: Label?, val toFinally: Label?, val tryExpression: KtTryExpression?)

    private inner class CFPVisitor(private val builder: ControlFlowBuilder) : KtVisitorVoid() {

        private val catchFinallyStack = Stack()

        // Some language constructs (e.g. inlined lambdas) should be partially processed before call
        // (to provide argument for call itself), and partially - after (in case of inlined lambdas,
        // their body should be generated after call). To do so, we store deferred generators, which
        // will be called after call instruction is emitted.
        // Stack is necessary to store generators across nested calls
        private val deferredGeneratorsStack = Stack>()

        private val conditionVisitor = object : KtVisitorVoid() {

            private fun getSubjectExpression(condition: KtWhenCondition): KtExpression? =
                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")
            }
        }

        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 =
            builder.magic(instructionElement, null, elementsToValues(from.asList()), kind).outputValue

        private fun createNonSyntheticValue(to: KtElement, from: List, kind: MagicKind): PseudoValue =
            builder.magic(to, to, elementsToValues(from), kind).outputValue

        private fun createNonSyntheticValue(to: KtElement, kind: MagicKind, vararg from: KtElement?): PseudoValue =
            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 =
            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 =
            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 (resolvedCall == null) {
                    val qualifierExpression =
                        when (languageVersionSettings.supportsFeature(ProhibitQualifiedAccessToUninitializedEnumEntry)) {
                            true -> expression.getTopmostParentQualifiedExpressionForSelector() ?: expression
                            false -> expression
                        }
                    val qualifier = trace.bindingContext[BindingContext.QUALIFIER, qualifierExpression]
                    if (qualifier != null && generateQualifier(expression, qualifier)) return
                }
                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)
            }

            val labelNameExpression = expression.getTargetLabel()
            if (labelNameExpression != null) {
                val deparenthesizedBaseExpression = KtPsiUtil.deparenthesize(expression)
                if (deparenthesizedBaseExpression !is KtLambdaExpression &&
                    deparenthesizedBaseExpression !is KtLoopExpression &&
                    deparenthesizedBaseExpression !is KtNamedFunction
                ) {
                    trace.report(REDUNDANT_LABEL_WARNING.on(labelNameExpression))
                }
            }
        }

        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(listOfNotNull(left, right), expression)
                if (right != null && languageVersionSettings.supportsFeature(LanguageFeature.ProhibitNonExhaustiveIfInRhsOfElvis)) {
                    right.recordUsedAsExpression(trace, true)
                }
            } 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) ControlFlowBuilder.PredefinedOperation.AND else OR
            builder.predefinedOperation(expression, operation, elementsToValues(listOfNotNull(left, right)))
        }

        private fun getValueAsFunction(value: PseudoValue?): () -> PseudoValue? = { value }

        private fun getDeferredValue(expression: KtExpression?): () -> PseudoValue? = {
            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 = listOfNotNull(getBoundOrUnreachableValue(lhs), rhsDeferredValue.invoke())
                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) as? 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 (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) {
                generateCall(resolvedCall).outputValue
            } else {
                generateInstructions(baseExpression)
                createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression)
            }

            if (incrementOrDecrement && resolvedCall != null) {
                visitAssignment(baseExpression, getValueAsFunction(rhsValue), expression)
                if (expression is KtPostfixExpression) {
                    copyValue(baseExpression, expression)
                }
            }
        }

        private fun isIncrementOrDecrement(operationType: IElementType): Boolean =
            operationType === PLUSPLUS || operationType === 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
                catchFinallyStack.push(CatchFinallyLabels(null, null, null))
                startFinally?.let {
                    assert(finishFinally != null) { "startFinally label is set to $startFinally but finishFinally label is not set" }
                    builder.repeatPseudocode(it, finishFinally!!)
                    catchFinallyStack.pop()
                    return
                }
                builder.createUnboundLabel("start finally").let {
                    startFinally = it
                    builder.bindLabel(it)
                }
                generateInstructions(finalExpression)
                builder.createUnboundLabel("finish finally").let {
                    finishFinally = it
                    builder.bindLabel(it)
                }
                catchFinallyStack.pop()
            }
        }

        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.isNotEmpty()

            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
            catchFinallyStack.push(CatchFinallyLabels(onException, onExceptionToFinallyBlock, expression))
            generateInstructions(tryBlock)
            generateJumpsToCatchAndFinally()
            catchFinallyStack.pop()

            if (hasCatches && onException != null) {
                val afterCatches = builder.createUnboundLabel("afterCatches")
                builder.jump(afterCatches, expression)

                builder.bindLabel(onException)
                val catchLabels = LinkedList




© 2015 - 2024 Weber Informatics LLC | Privacy Policy