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

org.jetbrains.jet.lang.cfg.JetControlFlowProcessor Maven / Gradle / Ivy

There is a newer version: 2.0.0-RC2
Show newest version
/*
 * Copyright 2010-2013 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.jet.lang.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.Function0;
import kotlin.Function1;
import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.cfg.pseudocode.*;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.AccessTarget;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.InstructionWithValue;
import org.jetbrains.jet.lang.cfg.pseudocode.instructions.eval.MagicKind;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
import org.jetbrains.jet.lang.psi.*;
import org.jetbrains.jet.lang.psi.psiUtil.PsiUtilPackage;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.CompileTimeConstantUtils;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.jet.lang.resolve.constants.CompileTimeConstant;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
import org.jetbrains.jet.lang.resolve.scopes.receivers.TransientReceiver;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.expressions.OperatorConventions;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.lexer.JetToken;
import org.jetbrains.jet.lexer.JetTokens;

import java.util.*;

import static org.jetbrains.jet.lang.cfg.JetControlFlowBuilder.PredefinedOperation.*;
import static org.jetbrains.jet.lang.diagnostics.Errors.*;
import static org.jetbrains.jet.lang.resolve.calls.callUtil.CallUtilPackage.getResolvedCall;
import static org.jetbrains.jet.lexer.JetTokens.*;

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 JetElement subroutine) {
        Pseudocode pseudocode = generate(subroutine);
        ((PseudocodeImpl) pseudocode).postProcess();
        return pseudocode;
    }

    @NotNull
    private Pseudocode generate(@NotNull JetElement subroutine) {
        builder.enterSubroutine(subroutine);
        CFPVisitor cfpVisitor = new CFPVisitor(builder);
        if (subroutine instanceof JetDeclarationWithBody) {
            JetDeclarationWithBody declarationWithBody = (JetDeclarationWithBody) subroutine;
            List valueParameters = declarationWithBody.getValueParameters();
            for (JetParameter valueParameter : valueParameters) {
                cfpVisitor.generateInstructions(valueParameter);
            }
            JetExpression 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 JetExpression bodyExpression, @NotNull JetElement subroutine) {
        CallableDescriptor subroutineDescriptor = (CallableDescriptor) trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, subroutine);
        if (subroutineDescriptor == null) return;

        JetType returnType = subroutineDescriptor.getReturnType();
        KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
        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 JetDeclaration subroutine) {
        JetElement parent = PsiTreeUtil.getParentOfType(subroutine, JetElement.class);
        assert parent != null;

        Label afterDeclaration = builder.createUnboundLabel("after local declaration");

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

    private class CFPVisitor extends JetVisitorVoid {
        private final JetControlFlowBuilder builder;

        private final JetVisitorVoid conditionVisitor = new JetVisitorVoid() {

            private JetExpression getSubjectExpression(JetWhenCondition condition) {
                JetWhenExpression whenExpression = PsiTreeUtil.getParentOfType(condition, JetWhenExpression.class);
                return whenExpression != null ? whenExpression.getSubjectExpression() : null;
            }

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

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

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

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

                JetExpression 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 visitJetElement(@NotNull JetElement element) {
                throw new UnsupportedOperationException("[JetControlFlowProcessor] " + element.toString());
            }
        };

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

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

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

        private void checkNothingType(JetElement element) {
            if (!(element instanceof JetExpression)) return;

            JetExpression expression = JetPsiUtil.deparenthesize((JetExpression) element);
            if (expression == null) return;

            if (expression instanceof JetStatementExpression || expression instanceof JetTryExpression
                    || expression instanceof JetIfExpression || expression instanceof JetWhenExpression) {
                return;
            }

            JetType type = trace.getBindingContext().get(BindingContext.EXPRESSION_TYPE, expression);
            if (type != null && KotlinBuiltIns.isNothing(type)) {
                builder.jumpToError(expression);
            }
        }

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

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

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

        @NotNull
        private Map defaultTypeMap(List values) {
            return PseudocodePackage.expectedTypeFor(AllTypes.INSTANCE$, values);
        }

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

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

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

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

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

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

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

        @NotNull
        private AccessTarget getDeclarationAccessTarget(JetElement 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 JetParenthesizedExpression expression) {
            mark(expression);
            JetExpression innerExpression = expression.getExpression();
            if (innerExpression != null) {
                generateInstructions(innerExpression);
                copyValue(innerExpression, expression);
            }
        }

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

        @Override
        public void visitThisExpression(@NotNull JetThisExpression expression) {
            ResolvedCall resolvedCall = 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 JetConstantExpression expression) {
            CompileTimeConstant constant = trace.get(BindingContext.COMPILE_TIME_VALUE, expression);
            builder.loadConstant(expression, constant);
        }

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

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

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

            JetExpression left = expression.getLeft();
            JetExpression 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 = getResolvedCall(expression, trace.getBindingContext());
                if (resolvedCall != null) {
                    PseudoValue rhsValue = generateCall(resolvedCall).getOutputValue();
                    Name assignMethodName = OperatorConventions.getNameForOperationSymbol((JetToken) 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(JetBinaryExpression expression) {
            IElementType operationType = expression.getOperationReference().getReferencedNameElementType();
            JetExpression left = expression.getLeft();
            JetExpression 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 JetExpression expression) {
            return new Function0() {
                @Override
                public PseudoValue invoke() {
                    generateInstructions(expression);
                    return getBoundOrUnreachableValue(expression);
                }
            };
        }

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

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

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

            Map receiverValues = SmartFMap.emptyMap();
            AccessTarget accessTarget = AccessTarget.BlackBox.INSTANCE$;
            if (left instanceof JetSimpleNameExpression || left instanceof JetQualifiedExpression) {
                accessTarget = getResolvedCallAccessTarget(PsiUtilPackage.getQualifiedElementSelector(left));
                if (accessTarget instanceof AccessTarget.Call) {
                    receiverValues = getReceiverValues(((AccessTarget.Call) accessTarget).getResolvedCall());
                }
            }
            else if (left instanceof JetProperty) {
                accessTarget = getDeclarationAccessTarget(left);
            }

            recordWrite(left, accessTarget, rhsDeferredValue.invoke(), receiverValues, parentExpression);
        }

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

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

                List arguments = Arrays.asList(getBoundOrUnreachableValue(lhs), rhsDeferredValue.invoke());
                builder.magic(parentExpression, parentExpression, arguments, defaultTypeMap(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 (((JetOperationExpression) 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 = KotlinPackage.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 = KotlinPackage.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 recordWrite(
                @NotNull JetExpression left,
                @NotNull AccessTarget target,
                @Nullable PseudoValue rightValue,
                @NotNull Map receiverValues,
                @NotNull JetExpression parentExpression
        ) {
            if (target == AccessTarget.BlackBox.INSTANCE$) {
                List values = ContainerUtil.createMaybeSingletonList(rightValue);
                builder.magic(parentExpression, parentExpression, values, defaultTypeMap(values), MagicKind.UNSUPPORTED_ELEMENT);
            }
            else {
                PseudoValue rValue =
                        rightValue != null ? rightValue : createSyntheticValue(parentExpression, MagicKind.UNRECOGNIZED_WRITE_RHS);
                builder.write(parentExpression, left, rValue, target, receiverValues);
            }
        }

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

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

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

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

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

            return inputExpressions;
        }

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

            boolean incrementOrDecrement = isIncrementOrDecrement(operationType);
            ResolvedCall resolvedCall = 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 JetPostfixExpression) {
                    copyValue(baseExpression, expression);
                }
            }
        }

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

        @Override
        public void visitIfExpression(@NotNull JetIfExpression expression) {
            mark(expression);
            List branches = new ArrayList(2);
            JetExpression condition = expression.getCondition();
            if (condition != null) {
                generateInstructions(condition);
            }
            Label elseLabel = builder.createUnboundLabel("else branch");
            builder.jumpOnFalse(elseLabel, expression, builder.getBoundValue(condition));
            JetExpression 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);
            JetExpression 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 JetFinallySection finallyBlock;
            private Label startFinally = null;
            private Label finishFinally = null;

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

            public void generate() {
                JetBlockExpression 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 JetTryExpression expression) {
            mark(expression);

            JetFinallySection 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 (JetCatchClause catchClause : expression.getCatchClauses()) {
                branches.add(catchClause.getCatchBody());
            }
            mergeValues(branches, expression);
        }

        // Returns label for 'finally' block
        @Nullable
        private Label generateTryAndCatches(@NotNull JetTryExpression 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);
            }

            JetBlockExpression 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