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

org.jetbrains.kotlin.cfg.JetControlFlowProcessor Maven / Gradle / Ivy

There is a newer version: 2.1.0-Beta1
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;

import com.google.common.collect.Lists;
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 kotlin.ArraysKt;
import kotlin.CollectionsKt;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
import org.jetbrains.kotlin.cfg.pseudocode.JetControlFlowInstructionsGenerator;
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.lexer.KtToken;
import org.jetbrains.kotlin.lexer.KtTokens;
import org.jetbrains.kotlin.name.Name;
import org.jetbrains.kotlin.psi.*;
import org.jetbrains.kotlin.psi.psiUtil.KtPsiUtilKt;
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.CallUtilKt;
import org.jetbrains.kotlin.resolve.calls.model.*;
import org.jetbrains.kotlin.resolve.calls.tasks.ExplicitReceiverKind;
import org.jetbrains.kotlin.resolve.constants.CompileTimeConstant;
import org.jetbrains.kotlin.resolve.constants.evaluate.ConstantExpressionEvaluator;
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
import org.jetbrains.kotlin.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.kotlin.types.KotlinType;
import org.jetbrains.kotlin.types.expressions.OperatorConventions;

import java.util.*;

import static org.jetbrains.kotlin.cfg.JetControlFlowBuilder.PredefinedOperation.*;
import static org.jetbrains.kotlin.diagnostics.Errors.*;
import static org.jetbrains.kotlin.lexer.KtTokens.*;

public class JetControlFlowProcessor {

    private final JetControlFlowBuilder builder;
    private final BindingTrace trace;

    public JetControlFlowProcessor(BindingTrace trace) {
        this.builder = new JetControlFlowInstructionsGenerator();
        this.trace = trace;
    }

    @NotNull
    public Pseudocode generatePseudocode(@NotNull KtElement subroutine) {
        Pseudocode pseudocode = generate(subroutine);
        ((PseudocodeImpl) pseudocode).postProcess();
        return pseudocode;
    }

    @NotNull
    private Pseudocode generate(@NotNull KtElement subroutine) {
        builder.enterSubroutine(subroutine);
        CFPVisitor cfpVisitor = new CFPVisitor(builder);
        if (subroutine instanceof KtDeclarationWithBody && !(subroutine instanceof KtSecondaryConstructor)) {
            KtDeclarationWithBody declarationWithBody = (KtDeclarationWithBody) subroutine;
            List valueParameters = declarationWithBody.getValueParameters();
            for (KtParameter valueParameter : valueParameters) {
                cfpVisitor.generateInstructions(valueParameter);
            }
            KtExpression bodyExpression = declarationWithBody.getBodyExpression();
            if (bodyExpression != null) {
                cfpVisitor.generateInstructions(bodyExpression);
                if (!declarationWithBody.hasBlockBody()) {
                    generateImplicitReturnValue(bodyExpression, subroutine);
                }
            }
        } else {
            cfpVisitor.generateInstructions(subroutine);
        }
        return builder.exitSubroutine(subroutine);
    }

    private void generateImplicitReturnValue(@NotNull KtExpression bodyExpression, @NotNull KtElement subroutine) {
        CallableDescriptor subroutineDescriptor = (CallableDescriptor) trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine);
        if (subroutineDescriptor == null) return;

        KotlinType returnType = subroutineDescriptor.getReturnType();
        if (returnType != null && KotlinBuiltIns.isUnit(returnType) && subroutineDescriptor instanceof AnonymousFunctionDescriptor) return;

        PseudoValue returnValue = builder.getBoundValue(bodyExpression);
        if (returnValue == null) return;

        builder.returnValue(bodyExpression, returnValue, subroutine);
    }

    private void processLocalDeclaration(@NotNull KtDeclaration subroutine) {
        Label afterDeclaration = builder.createUnboundLabel("after local declaration");

        builder.nondeterministicJump(afterDeclaration, subroutine, null);
        generate(subroutine);
        builder.bindLabel(afterDeclaration);
    }

    private class CFPVisitor extends KtVisitorVoid {
        private final JetControlFlowBuilder builder;

        private final KtVisitorVoid conditionVisitor = new KtVisitorVoid() {

            private KtExpression getSubjectExpression(KtWhenCondition condition) {
                KtWhenExpression whenExpression = PsiTreeUtil.getParentOfType(condition, KtWhenExpression.class);
                return whenExpression != null ? whenExpression.getSubjectExpression() : null;
            }

            @Override
            public void visitWhenConditionInRange(@NotNull KtWhenConditionInRange condition) {
                if (!generateCall(condition.getOperationReference())) {
                    KtExpression rangeExpression = condition.getRangeExpression();
                    generateInstructions(rangeExpression);
                    createNonSyntheticValue(condition, MagicKind.UNRESOLVED_CALL, rangeExpression);
                }
            }

            @Override
            public void visitWhenConditionIsPattern(@NotNull KtWhenConditionIsPattern condition) {
                mark(condition);
                createNonSyntheticValue(condition, MagicKind.IS, getSubjectExpression(condition));
            }

            @Override
            public void visitWhenConditionWithExpression(@NotNull KtWhenConditionWithExpression condition) {
                mark(condition);

                KtExpression expression = condition.getExpression();
                generateInstructions(expression);

                KtExpression 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
            public void visitKtElement(@NotNull KtElement element) {
                throw new UnsupportedOperationException("[JetControlFlowProcessor] " + element.toString());
            }
        };

        private CFPVisitor(@NotNull JetControlFlowBuilder builder) {
            this.builder = builder;
        }

        private void mark(KtElement element) {
            builder.mark(element);
        }

        public void generateInstructions(@Nullable KtElement element) {
            if (element == null) return;
            element.accept(this);
            checkNothingType(element);
        }

        private void checkNothingType(KtElement element) {
            if (!(element instanceof KtExpression)) return;

            KtExpression expression = KtPsiUtil.deparenthesize((KtExpression) element);
            if (expression == null) return;

            if (expression instanceof KtStatementExpression || expression instanceof KtTryExpression
                || expression instanceof KtIfExpression || expression instanceof KtWhenExpression) {
                return;
            }

            KotlinType type = trace.getBindingContext().getType(expression);
            if (type != null && KotlinBuiltIns.isNothing(type)) {
                builder.jumpToError(expression);
            }
        }

        @NotNull
        private PseudoValue createSyntheticValue(@NotNull KtElement instructionElement, @NotNull MagicKind kind, KtElement... from) {
            List values = elementsToValues(from.length > 0 ? Arrays.asList(from) : Collections.emptyList());
            return builder.magic(instructionElement, null, values, kind).getOutputValue();
        }

        @NotNull
        private PseudoValue createNonSyntheticValue(
                @NotNull KtElement to, @NotNull List from, @NotNull MagicKind kind
        ) {
            List values = elementsToValues(from);
            return builder.magic(to, to, values, kind).getOutputValue();
        }

        @NotNull
        private PseudoValue createNonSyntheticValue(@NotNull KtElement to, @NotNull MagicKind kind, KtElement... from) {
            return createNonSyntheticValue(to, Arrays.asList(from), kind);
        }

        private void mergeValues(@NotNull List from, @NotNull KtExpression to) {
            builder.merge(to, elementsToValues(from));
        }

        private void copyValue(@Nullable KtElement from, @NotNull KtElement to) {
            PseudoValue value = getBoundOrUnreachableValue(from);
            if (value != null) {
                builder.bindValue(value, to);
            }
        }

        @Nullable
        private PseudoValue getBoundOrUnreachableValue(@Nullable KtElement element) {
            if (element == null) return null;

            PseudoValue value = builder.getBoundValue(element);
            return value != null || element instanceof KtDeclaration ? value : builder.newValue(element);
        }

        private List elementsToValues(List from) {
            if (from.isEmpty()) return Collections.emptyList();
            return CollectionsKt.filterNotNull(
                    CollectionsKt.map(
                            from,
                            new Function1() {
                                @Override
                                public PseudoValue invoke(KtElement element) {
                                    return getBoundOrUnreachableValue(element);
                                }
                            }
                    )
            );
        }

        private void generateInitializer(@NotNull KtDeclaration declaration, @NotNull PseudoValue initValue) {
            builder.write(
                    declaration,
                    declaration,
                    initValue,
                    getDeclarationAccessTarget(declaration),
                    Collections.emptyMap()
            );
        }

        @NotNull
        private AccessTarget getResolvedCallAccessTarget(KtElement element) {
            ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(element, trace.getBindingContext());
            return resolvedCall != null ? new AccessTarget.Call(resolvedCall) : AccessTarget.BlackBox.INSTANCE$;
        }

        @NotNull
        private AccessTarget getDeclarationAccessTarget(KtElement element) {
            DeclarationDescriptor descriptor = trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, element);
            return descriptor instanceof VariableDescriptor
                   ? new AccessTarget.Declaration((VariableDescriptor) descriptor)
                   : AccessTarget.BlackBox.INSTANCE$;
        }

        @Override
        public void visitParenthesizedExpression(@NotNull KtParenthesizedExpression expression) {
            mark(expression);
            KtExpression innerExpression = expression.getExpression();
            if (innerExpression != null) {
                generateInstructions(innerExpression);
                copyValue(innerExpression, expression);
            }
        }

        @Override
        public void visitAnnotatedExpression(@NotNull KtAnnotatedExpression expression) {
            KtExpression baseExpression = expression.getBaseExpression();
            if (baseExpression != null) {
                generateInstructions(baseExpression);
                copyValue(baseExpression, expression);
            }
        }

        @Override
        public void visitThisExpression(@NotNull KtThisExpression expression) {
            ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
            if (resolvedCall == null) {
                createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL);
                return;
            }

            CallableDescriptor resultingDescriptor = resolvedCall.getResultingDescriptor();
            if (resultingDescriptor instanceof ReceiverParameterDescriptor) {
                builder.readVariable(expression, resolvedCall, getReceiverValues(resolvedCall));
            }

            copyValue(expression, expression.getInstanceReference());
        }

        @Override
        public void visitConstantExpression(@NotNull KtConstantExpression expression) {
            CompileTimeConstant constant = ConstantExpressionEvaluator.getConstant(expression, trace.getBindingContext());
            builder.loadConstant(expression, constant);
        }

        @Override
        public void visitSimpleNameExpression(@NotNull KtSimpleNameExpression expression) {
            ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
            if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
                VariableAsFunctionResolvedCall variableAsFunctionResolvedCall = (VariableAsFunctionResolvedCall) resolvedCall;
                generateCall(variableAsFunctionResolvedCall.getVariableCall());
            }
            else if (!generateCall(expression) && !(expression.getParent() instanceof KtCallExpression)) {
                createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, generateAndGetReceiverIfAny(expression));
            }
        }

        @Override
        public void visitLabeledExpression(@NotNull KtLabeledExpression expression) {
            mark(expression);
            KtExpression baseExpression = expression.getBaseExpression();
            if (baseExpression != null) {
                generateInstructions(baseExpression);
                copyValue(baseExpression, expression);
            }
        }

        @SuppressWarnings("SuspiciousMethodCalls")
        @Override
        public void visitBinaryExpression(@NotNull KtBinaryExpression expression) {
            KtSimpleNameExpression operationReference = expression.getOperationReference();
            IElementType operationType = operationReference.getReferencedNameElementType();

            KtExpression left = expression.getLeft();
            KtExpression right = expression.getRight();
            if (operationType == ANDAND || operationType == OROR) {
                generateBooleanOperation(expression);
            }
            else if (operationType == EQ) {
                visitAssignment(left, getDeferredValue(right), expression);
            }
            else if (OperatorConventions.ASSIGNMENT_OPERATIONS.containsKey(operationType)) {
                ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());
                if (resolvedCall != null) {
                    PseudoValue rhsValue = generateCall(resolvedCall).getOutputValue();
                    Name assignMethodName = OperatorConventions.getNameForOperationSymbol((KtToken) expression.getOperationToken());
                    if (!resolvedCall.getResultingDescriptor().getName().equals(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);
                Label afterElvis = builder.createUnboundLabel("after elvis operator");
                builder.jumpOnTrue(afterElvis, expression, builder.getBoundValue(left));
                if (right != null) {
                    generateInstructions(right);
                }
                builder.bindLabel(afterElvis);
                mergeValues(Arrays.asList(left, right), expression);
            }
            else {
                if (!generateCall(expression)) {
                    generateBothArgumentsAndMark(expression);
                }
            }
        }

        private void generateBooleanOperation(KtBinaryExpression expression) {
            IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
            KtExpression left = expression.getLeft();
            KtExpression right = expression.getRight();

            Label 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));
            }
            if (right != null) {
                generateInstructions(right);
            }
            builder.bindLabel(resultLabel);
            JetControlFlowBuilder.PredefinedOperation operation = operationType == ANDAND ? AND : OR;
            builder.predefinedOperation(expression, operation, elementsToValues(Arrays.asList(left, right)));
        }

        private Function0 getValueAsFunction(final PseudoValue value) {
            return new Function0() {
                @Override
                public PseudoValue invoke() {
                    return value;
                }
            };
        }

        private Function0 getDeferredValue(final KtExpression expression) {
            return new Function0() {
                @Override
                public PseudoValue invoke() {
                    generateInstructions(expression);
                    return getBoundOrUnreachableValue(expression);
                }
            };
        }

        private void generateBothArgumentsAndMark(KtBinaryExpression expression) {
            KtExpression left = KtPsiUtil.deparenthesize(expression.getLeft());
            if (left != null) {
                generateInstructions(left);
            }
            KtExpression right = expression.getRight();
            if (right != null) {
                generateInstructions(right);
            }
            mark(expression);
            createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, left, right);
        }

        private void visitAssignment(
                KtExpression lhs,
                @NotNull Function0 rhsDeferredValue,
                KtExpression parentExpression
        ) {
            KtExpression left = KtPsiUtil.deparenthesize(lhs);
            if (left == null) {
                List arguments = Collections.singletonList(rhsDeferredValue.invoke());
                builder.magic(parentExpression, parentExpression, arguments, MagicKind.UNSUPPORTED_ELEMENT);
                return;
            }

            if (left instanceof KtArrayAccessExpression) {
                generateArrayAssignment((KtArrayAccessExpression) left, rhsDeferredValue, parentExpression);
                return;
            }

            Map receiverValues = SmartFMap.emptyMap();
            AccessTarget accessTarget = AccessTarget.BlackBox.INSTANCE$;
            if (left instanceof KtSimpleNameExpression || left instanceof KtQualifiedExpression) {
                accessTarget = getResolvedCallAccessTarget(KtPsiUtilKt.getQualifiedElementSelector(left));
                if (accessTarget instanceof AccessTarget.Call) {
                    receiverValues = getReceiverValues(((AccessTarget.Call) accessTarget).getResolvedCall());
                }
            }
            else if (left instanceof KtProperty) {
                accessTarget = getDeclarationAccessTarget(left);
            }

            if (accessTarget == AccessTarget.BlackBox.INSTANCE$ && !(left instanceof KtProperty)) {
                generateInstructions(left);
                createSyntheticValue(left, MagicKind.VALUE_CONSUMER, left);
            }

            PseudoValue rightValue = rhsDeferredValue.invoke();
            PseudoValue rValue =
                    rightValue != null ? rightValue : createSyntheticValue(parentExpression, MagicKind.UNRECOGNIZED_WRITE_RHS);
            builder.write(parentExpression, left, rValue, accessTarget, receiverValues);
        }

        private void generateArrayAssignment(
                KtArrayAccessExpression lhs,
                @NotNull Function0 rhsDeferredValue,
                @NotNull KtExpression parentExpression
        ) {
            ResolvedCall setResolvedCall = trace.get(BindingContext.INDEXED_LVALUE_SET, lhs);

            if (setResolvedCall == null) {
                generateArrayAccess(lhs, null);

                List arguments = CollectionsKt.filterNotNull(
                        Arrays.asList(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 (((KtOperationExpression) parentExpression).getOperationReference().getReferencedNameElementType() == EQ) {
                mark(lhs);
            }

            generateInstructions(lhs.getArrayExpression());

            Map receiverValues = getReceiverValues(setResolvedCall);
            SmartFMap 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 SmartFMap getArraySetterArguments(
                Function0 rhsDeferredValue,
                final ResolvedCall setResolvedCall
        ) {
            List valueArguments = CollectionsKt.flatMapTo(
                    setResolvedCall.getResultingDescriptor().getValueParameters(),
                    new ArrayList(),
                    new Function1>() {
                        @Override
                        public Iterable invoke(ValueParameterDescriptor descriptor) {
                            ResolvedValueArgument resolvedValueArgument = setResolvedCall.getValueArguments().get(descriptor);
                            return resolvedValueArgument != null
                                   ? resolvedValueArgument.getArguments()
                                   : Collections.emptyList();
                        }
                    }
            );

            ValueArgument rhsArgument = CollectionsKt.lastOrNull(valueArguments);
            SmartFMap argumentValues = SmartFMap.emptyMap();
            for (ValueArgument valueArgument : valueArguments) {
                ArgumentMapping argumentMapping = setResolvedCall.getArgumentMapping(valueArgument);
                if (argumentMapping.isError() || (!(argumentMapping instanceof ArgumentMatch))) continue;

                ValueParameterDescriptor parameterDescriptor = ((ArgumentMatch) argumentMapping).getValueParameter();
                if (valueArgument != rhsArgument) {
                    argumentValues = generateValueArgument(valueArgument, parameterDescriptor, argumentValues);
                }
                else {
                    PseudoValue rhsValue = rhsDeferredValue.invoke();
                    if (rhsValue != null) {
                        argumentValues = argumentValues.plus(rhsValue, parameterDescriptor);
                    }
                }
            }
            return argumentValues;
        }

        private void generateArrayAccess(KtArrayAccessExpression arrayAccessExpression, @Nullable ResolvedCall resolvedCall) {
            if (builder.getBoundValue(arrayAccessExpression) != null) return;
            mark(arrayAccessExpression);
            if (!checkAndGenerateCall(resolvedCall)) {
                generateArrayAccessWithoutCall(arrayAccessExpression);
            }
        }

        private void generateArrayAccessWithoutCall(KtArrayAccessExpression arrayAccessExpression) {
            createNonSyntheticValue(arrayAccessExpression, generateArrayAccessArguments(arrayAccessExpression), MagicKind.UNRESOLVED_CALL);
        }

        private List generateArrayAccessArguments(KtArrayAccessExpression arrayAccessExpression) {
            List inputExpressions = new ArrayList();

            KtExpression arrayExpression = arrayAccessExpression.getArrayExpression();
            inputExpressions.add(arrayExpression);
            generateInstructions(arrayExpression);

            for (KtExpression index : arrayAccessExpression.getIndexExpressions()) {
                generateInstructions(index);
                inputExpressions.add(index);
            }

            return inputExpressions;
        }

        @Override
        public void visitUnaryExpression(@NotNull KtUnaryExpression expression) {
            KtSimpleNameExpression operationSign = expression.getOperationReference();
            IElementType operationType = operationSign.getReferencedNameElementType();
            KtExpression baseExpression = expression.getBaseExpression();
            if (baseExpression == null) return;
            if (KtTokens.EXCLEXCL == operationType) {
                generateInstructions(baseExpression);
                builder.predefinedOperation(expression, NOT_NULL_ASSERTION, elementsToValues(Collections.singletonList(baseExpression)));
                return;
            }

            boolean incrementOrDecrement = isIncrementOrDecrement(operationType);
            ResolvedCall resolvedCall = CallUtilKt.getResolvedCall(expression, trace.getBindingContext());

            PseudoValue rhsValue;
            if (resolvedCall != null) {
                rhsValue = generateCall(resolvedCall).getOutputValue();
            }
            else {
                generateInstructions(baseExpression);
                rhsValue = createNonSyntheticValue(expression, MagicKind.UNRESOLVED_CALL, baseExpression);
            }

            if (incrementOrDecrement) {
                visitAssignment(baseExpression, getValueAsFunction(rhsValue), expression);
                if (expression instanceof KtPostfixExpression) {
                    copyValue(baseExpression, expression);
                }
            }
        }

        private boolean isIncrementOrDecrement(IElementType operationType) {
            return operationType == KtTokens.PLUSPLUS || operationType == KtTokens.MINUSMINUS;
        }

        @Override
        public void visitIfExpression(@NotNull KtIfExpression expression) {
            mark(expression);
            List branches = new ArrayList(2);
            KtExpression condition = expression.getCondition();
            if (condition != null) {
                generateInstructions(condition);
            }
            Label elseLabel = builder.createUnboundLabel("else branch");
            builder.jumpOnFalse(elseLabel, expression, builder.getBoundValue(condition));
            KtExpression thenBranch = expression.getThen();
            if (thenBranch != null) {
                branches.add(thenBranch);
                generateInstructions(thenBranch);
            }
            else {
                builder.loadUnit(expression);
            }
            Label resultLabel = builder.createUnboundLabel("'if' expression result");
            builder.jump(resultLabel, expression);
            builder.bindLabel(elseLabel);
            KtExpression elseBranch = expression.getElse();
            if (elseBranch != null) {
                branches.add(elseBranch);
                generateInstructions(elseBranch);
            }
            else {
                builder.loadUnit(expression);
            }
            builder.bindLabel(resultLabel);
            mergeValues(branches, expression);
        }

        private class FinallyBlockGenerator {
            private final KtFinallySection finallyBlock;
            private Label startFinally = null;
            private Label finishFinally = null;

            private FinallyBlockGenerator(KtFinallySection block) {
                finallyBlock = block;
            }

            public void generate() {
                KtBlockExpression finalExpression = finallyBlock.getFinalExpression();
                if (finalExpression == null) return;
                if (startFinally != null) {
                    assert finishFinally != null;
                    builder.repeatPseudocode(startFinally, finishFinally);
                    return;
                }
                startFinally = builder.createUnboundLabel("start finally");
                builder.bindLabel(startFinally);
                generateInstructions(finalExpression);
                finishFinally = builder.createUnboundLabel("finish finally");
                builder.bindLabel(finishFinally);
            }
        }

        @Override
        public void visitTryExpression(@NotNull KtTryExpression expression) {
            mark(expression);

            KtFinallySection finallyBlock = expression.getFinallyBlock();
            final FinallyBlockGenerator finallyBlockGenerator = new FinallyBlockGenerator(finallyBlock);
            boolean hasFinally = finallyBlock != null;
            if (hasFinally) {
                builder.enterTryFinally(new GenerationTrigger() {
                    private boolean working = false;

                    @Override
                    public void 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;
                    }
                });
            }

            Label onExceptionToFinallyBlock = generateTryAndCatches(expression);

            if (hasFinally) {
                assert onExceptionToFinallyBlock != null : "No finally lable generated: " + expression.getText();

                builder.exitTryFinally();

                Label skipFinallyToErrorBlock = builder.createUnboundLabel("skipFinallyToErrorBlock");
                builder.jump(skipFinallyToErrorBlock, expression);
                builder.bindLabel(onExceptionToFinallyBlock);
                finallyBlockGenerator.generate();
                builder.jumpToError(expression);
                builder.bindLabel(skipFinallyToErrorBlock);

                finallyBlockGenerator.generate();
            }

            List branches = new ArrayList();
            branches.add(expression.getTryBlock());
            for (KtCatchClause catchClause : expression.getCatchClauses()) {
                branches.add(catchClause.getCatchBody());
            }
            mergeValues(branches, expression);
        }

        // Returns label for 'finally' block
        @Nullable
        private Label generateTryAndCatches(@NotNull KtTryExpression expression) {
            List catchClauses = expression.getCatchClauses();
            boolean hasCatches = !catchClauses.isEmpty();

            Label onException = null;
            if (hasCatches) {
                onException = builder.createUnboundLabel("onException");
                builder.nondeterministicJump(onException, expression, null);
            }

            Label onExceptionToFinallyBlock = null;
            if (expression.getFinallyBlock() != null) {
                onExceptionToFinallyBlock = builder.createUnboundLabel("onExceptionToFinallyBlock");
                builder.nondeterministicJump(onExceptionToFinallyBlock, expression, null);
            }

            KtBlockExpression tryBlock = expression.getTryBlock();
            generateInstructions(tryBlock);

            if (hasCatches) {
                Label afterCatches = builder.createUnboundLabel("afterCatches");
                builder.jump(afterCatches, expression);

                builder.bindLabel(onException);
                LinkedList




© 2015 - 2024 Weber Informatics LLC | Privacy Policy