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

org.jetbrains.k2js.translate.reference.InlinedCallExpressionTranslator Maven / Gradle / Ivy

/*
 * 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.k2js.translate.reference;

import com.google.common.collect.Maps;
import com.google.dart.compiler.backend.js.ast.JsExpression;
import com.google.dart.compiler.backend.js.ast.JsLiteral;
import com.google.dart.compiler.backend.js.ast.JsNode;
import com.google.dart.compiler.backend.js.ast.JsReturn;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
import org.jetbrains.jet.lang.descriptors.SimpleFunctionDescriptor;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetCallExpression;
import org.jetbrains.jet.lang.psi.JetFunction;
import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
import org.jetbrains.k2js.translate.callTranslator.CallInfo;
import org.jetbrains.k2js.translate.callTranslator.CallTranslatorPackage;
import org.jetbrains.k2js.translate.context.TemporaryVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.utils.JsAstUtils;
import org.jetbrains.k2js.translate.utils.mutator.LastExpressionMutator;
import org.jetbrains.k2js.translate.utils.mutator.Mutator;

import java.util.List;
import java.util.Map;

import static org.jetbrains.k2js.translate.reference.CallArgumentTranslator.translateSingleArgument;
import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionForDescriptor;
import static org.jetbrains.k2js.translate.utils.FunctionBodyTranslator.translateFunctionBody;

public final class InlinedCallExpressionTranslator extends AbstractCallExpressionTranslator {

    @SuppressWarnings("UnusedParameters")
    public static boolean shouldBeInlined(@NotNull JetCallExpression expression, @NotNull TranslationContext context) {
        //TODO: inlining turned off
        //ResolvedCall resolvedCall = getResolvedCallForCallExpression(context.bindingContext(), expression);
        //CallableDescriptor descriptor = resolvedCall.getCandidateDescriptor();
        //if (descriptor instanceof SimpleFunctionDescriptor) {
        //    return ((SimpleFunctionDescriptor)descriptor).isInline();
        //}
        return false;
    }

    @NotNull
    public static JsExpression translate(
            @NotNull JetCallExpression expression,
            @Nullable JsExpression receiver,
            @NotNull TranslationContext context
    ) {
        return (new InlinedCallExpressionTranslator(expression, receiver, context)).translate();
    }

    private InlinedCallExpressionTranslator(
            @NotNull JetCallExpression expression, @Nullable JsExpression receiver,
            @NotNull TranslationContext context
    ) {
        super(expression, receiver, context);
    }

    @NotNull
    private JsExpression translate() {
        TranslationContext contextWithAllParametersAliased = createContextForInlining();
        JsNode translatedBody = translateFunctionBody(getFunctionDescriptor(), getFunctionBody(), contextWithAllParametersAliased);
        //TODO: declare uninitialized temporary
        TemporaryVariable temporaryVariable = contextWithAllParametersAliased.declareTemporary(JsLiteral.NULL);
        JsNode mutatedBody = LastExpressionMutator.mutateLastExpression(translatedBody, new InlineFunctionMutator(temporaryVariable));
        context().addStatementToCurrentBlock(JsAstUtils.convertToBlock(mutatedBody));
        return temporaryVariable.reference();
    }

    @NotNull
    private JetFunction getFunctionBody() {
        return getFunctionForDescriptor(bindingContext(), getFunctionDescriptor());
    }

    @NotNull
    private SimpleFunctionDescriptor getFunctionDescriptor() {
        CallableDescriptor descriptor = resolvedCall.getCandidateDescriptor().getOriginal();
        assert descriptor instanceof SimpleFunctionDescriptor : "Inlined functions should have descriptor of type SimpleFunctionDescriptor";
        return (SimpleFunctionDescriptor)descriptor;
    }

    @NotNull
    private TranslationContext createContextForInlining() {
        TranslationContext contextForInlining = context();
        contextForInlining = createContextWithAliasForThisExpression(contextForInlining);
        return createContextWithAliasesForParameters(contextForInlining);
    }

    @NotNull
    private TranslationContext createContextWithAliasesForParameters(@NotNull TranslationContext contextForInlining) {
        Map aliases = Maps.newHashMap();
        for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) {
            TemporaryVariable aliasForArgument = createAliasForArgument(parameterDescriptor);
            aliases.put(parameterDescriptor, aliasForArgument.name().makeRef());
        }
        return contextForInlining.innerContextWithDescriptorsAliased(aliases);
    }

    @NotNull
    private TranslationContext createContextWithAliasForThisExpression(@NotNull TranslationContext contextForInlining) {
        TranslationContext contextWithAliasForThisExpression = contextForInlining;
        SimpleFunctionDescriptor functionDescriptor = getFunctionDescriptor();
        CallInfo callInfo = CallTranslatorPackage.getCallInfo(contextForInlining, resolvedCall, receiver);
        JsExpression receiver = callInfo.getReceiverObject();
        if (receiver != null) {
            contextWithAliasForThisExpression =
                contextWithAlias(contextWithAliasForThisExpression, receiver, functionDescriptor.getReceiverParameter());
        }
        JsExpression thisObject = callInfo.getThisObject();
        if (thisObject != null) {
            contextWithAliasForThisExpression =
                contextWithAlias(contextWithAliasForThisExpression, thisObject, functionDescriptor.getExpectedThisObject());
        }
        return contextWithAliasForThisExpression;
    }

    @NotNull
    private TranslationContext contextWithAlias(@NotNull TranslationContext contextWithAliasForThisExpression,
                                                @NotNull JsExpression aliasExpression, @Nullable DeclarationDescriptor descriptorToAlias) {
        TemporaryVariable aliasForReceiver = context().declareTemporary(aliasExpression);
        assert descriptorToAlias != null;
        TranslationContext newContext =
                contextWithAliasForThisExpression.innerContextWithAliased(descriptorToAlias, aliasForReceiver.reference());
        newContext.addStatementToCurrentBlock(aliasForReceiver.assignmentExpression().makeStmt());
        return newContext;
    }

    @NotNull
    private TemporaryVariable createAliasForArgument(@NotNull ValueParameterDescriptor parameterDescriptor) {
        List actualArguments = resolvedCall.getValueArgumentsByIndex();
        if (actualArguments == null) {
            throw new IllegalStateException("Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor());
        }
        ResolvedValueArgument actualArgument = actualArguments.get(parameterDescriptor.getIndex());
        JsExpression translatedArgument = translateArgument(actualArgument);
        TemporaryVariable aliasForArgument = context().declareTemporary(translatedArgument);
        context().addStatementToCurrentBlock(aliasForArgument.assignmentExpression().makeStmt());
        return aliasForArgument;
    }

    @NotNull
    private JsExpression translateArgument(@NotNull ResolvedValueArgument actualArgument) {
        List result = new SmartList();
        translateSingleArgument(actualArgument, result, context(), true);
        assert result.size() == 1 : "We always wrap varargs in kotlin calls.";
        return result.get(0);
    }

    private static final class InlineFunctionMutator implements Mutator {

        @NotNull
        private final TemporaryVariable toAssignTo;

        private InlineFunctionMutator(@NotNull TemporaryVariable to) {
            toAssignTo = to;
        }

        @NotNull
        @Override
        public JsNode mutate(@NotNull JsNode node) {
            if (node instanceof JsReturn) {
                JsExpression returnedExpression = ((JsReturn)node).getExpression();
                return JsAstUtils.assignment(toAssignTo.name().makeRef(), returnedExpression);
            }
            return node;
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy