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

org.jetbrains.k2js.translate.reference.CallArgumentTranslator 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.dart.compiler.backend.js.ast.*;
import com.intellij.util.SmartList;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ValueParameterDescriptor;
import org.jetbrains.jet.lang.psi.JetExpression;
import org.jetbrains.jet.lang.psi.ValueArgument;
import org.jetbrains.jet.lang.resolve.calls.model.*;
import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
import org.jetbrains.k2js.translate.context.TranslationContext;
import org.jetbrains.k2js.translate.general.AbstractTranslator;
import org.jetbrains.k2js.translate.general.Translation;
import org.jetbrains.k2js.translate.utils.AnnotationsUtils;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class CallArgumentTranslator extends AbstractTranslator {

    @NotNull
    public static ArgumentsInfo translate(
            @NotNull ResolvedCall resolvedCall,
            @Nullable JsExpression receiver,
            @NotNull TranslationContext context
    ) {
        CallArgumentTranslator argumentTranslator = new CallArgumentTranslator(resolvedCall, receiver, context);
        return argumentTranslator.translate();
    }

    public static class ArgumentsInfo {
        private final List translateArguments;
        private final boolean hasSpreadOperator;
        private final TemporaryConstVariable cachedReceiver;

        public ArgumentsInfo(List arguments, boolean operator, TemporaryConstVariable receiver) {
            translateArguments = arguments;
            hasSpreadOperator = operator;
            cachedReceiver = receiver;
        }

        @NotNull
        public List getTranslateArguments() {
            return translateArguments;
        }

        public boolean isHasSpreadOperator() {
            return hasSpreadOperator;
        }

        @Nullable
        public TemporaryConstVariable getCachedReceiver() {
            return cachedReceiver;
        }
    }

    public static void translateSingleArgument(
            @NotNull ResolvedValueArgument actualArgument,
            @NotNull List result,
            @NotNull TranslationContext context,
            boolean shouldWrapVarargInArray
    ) {
        List valueArguments = actualArgument.getArguments();
        if (actualArgument instanceof VarargValueArgument) {
            translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
        }
        else if (actualArgument instanceof DefaultValueArgument) {
            result.add(context.namer().getUndefinedExpression());
        }
        else {
            assert actualArgument instanceof ExpressionValueArgument;
            assert valueArguments.size() == 1;
            JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
            assert argumentExpression != null;
            result.add(Translation.translateAsExpression(argumentExpression, context));
        }
    }

    private static void translateVarargArgument(
            @NotNull List arguments,
            @NotNull List result,
            @NotNull TranslationContext context,
            boolean shouldWrapVarargInArray
    ) {
        if (arguments.isEmpty()) {
            if (shouldWrapVarargInArray) {
                result.add(new JsArrayLiteral(Collections.emptyList()));
            }
            return;
        }

        List list;
        if (shouldWrapVarargInArray) {
            list = arguments.size() == 1 ? new SmartList() : new ArrayList(arguments.size());
            result.add(new JsArrayLiteral(list));
        }
        else {
            list = result;
        }
        for (ValueArgument argument : arguments) {
            JetExpression argumentExpression = argument.getArgumentExpression();
            assert argumentExpression != null;
            list.add(Translation.translateAsExpression(argumentExpression, context));
        }
    }

    @NotNull
    private final ResolvedCall resolvedCall;
    @Nullable
    private final JsExpression receiver;
    private final boolean isNativeFunctionCall;

    private CallArgumentTranslator(
            @NotNull ResolvedCall resolvedCall,
            @Nullable JsExpression receiver,
            @NotNull TranslationContext context
    ) {
        super(context);
        this.resolvedCall = resolvedCall;
        this.receiver = receiver;
        this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor());
    }

    private void removeLastUndefinedArguments(@NotNull List result) {
        int i;
        for (i = result.size() - 1; i >= 0; i--) {
            if (result.get(i) != context().namer().getUndefinedExpression()) {
                break;
            }
        }
        result.subList(i + 1, result.size()).clear();
    }

    private ArgumentsInfo translate() {
        List valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
        if (valueParameters.isEmpty()) {
            return new ArgumentsInfo(Collections.emptyList(), false, null);
        }
        boolean hasSpreadOperator = false;
        TemporaryConstVariable cachedReceiver = null;

        List result = new ArrayList(valueParameters.size());
        List valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex();
        if (valueArgumentsByIndex == null) {
            throw new IllegalStateException("Failed to arrange value arguments by index: " + resolvedCall.getResultingDescriptor());
        }
        List argsBeforeVararg = null;
        for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
            ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());

            if (actualArgument instanceof VarargValueArgument) {
                assert !hasSpreadOperator;

                List arguments = actualArgument.getArguments();
                hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;

                if (isNativeFunctionCall && hasSpreadOperator) {
                    argsBeforeVararg = result;
                    result = new SmartList();
                }
            }

            translateSingleArgument(actualArgument, result, context(), !isNativeFunctionCall && !hasSpreadOperator);
        }

        if (isNativeFunctionCall && hasSpreadOperator) {
            if (!argsBeforeVararg.isEmpty()) {
                JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result);
                result = new SmartList(concatArguments);
            }

            if (receiver != null) {
                cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
                result.add(0, cachedReceiver.reference());
            }
            else {
                result.add(0, JsLiteral.NULL);
            }
        }

        removeLastUndefinedArguments(result);
        return new ArgumentsInfo(result, hasSpreadOperator, cachedReceiver);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy