001/*
002 * Copyright 2010-2013 JetBrains s.r.o.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.jetbrains.k2js.translate.reference;
018
019import com.google.dart.compiler.backend.js.ast.JsExpression;
020import org.jetbrains.annotations.NotNull;
021import org.jetbrains.annotations.Nullable;
022import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
023import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
024import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
025import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
026import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
027import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
028import org.jetbrains.k2js.translate.context.TranslationContext;
029
030import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForExtensionCallReceiver;
031
032public final class CallParametersResolver {
033    public static CallParameters resolveCallParameters(@Nullable JsExpression qualifier,
034            @Nullable JsExpression callee,
035            @NotNull CallableDescriptor descriptor,
036            @NotNull ResolvedCall<? extends CallableDescriptor> call,
037            @NotNull TranslationContext context) {
038        return (new CallParametersResolver(qualifier, callee, descriptor, call, context)).resolve();
039    }
040
041    // the actual qualifier for the call at the call site
042    @Nullable
043    private final JsExpression qualifier;
044    @Nullable
045    private final JsExpression callee;
046    @NotNull
047    private final CallableDescriptor descriptor;
048    @NotNull
049    private final TranslationContext context;
050    @NotNull
051    private final ResolvedCall<? extends CallableDescriptor> resolvedCall;
052    private final boolean isExtensionCall;
053
054    private CallParametersResolver(@Nullable JsExpression qualifier,
055            @Nullable JsExpression callee,
056            @NotNull CallableDescriptor descriptor,
057            @NotNull ResolvedCall<? extends CallableDescriptor> call,
058            @NotNull TranslationContext context) {
059        this.qualifier = qualifier;
060        this.callee = callee;
061        this.descriptor = descriptor;
062        this.context = context;
063        this.resolvedCall = call;
064        this.isExtensionCall = resolvedCall.getReceiverArgument().exists();
065    }
066
067    @NotNull
068    private CallParameters resolve() {
069        JsExpression receiver = isExtensionCall ? getExtensionFunctionCallReceiver() : null;
070        JsExpression functionReference = getFunctionReference();
071        JsExpression thisObject = getThisObject();
072        return new CallParameters(receiver, functionReference, thisObject);
073    }
074
075    @NotNull
076    private JsExpression getFunctionReference() {
077        if (callee != null) {
078            return callee;
079        }
080        if (!(resolvedCall instanceof VariableAsFunctionResolvedCall)) {
081            return ReferenceTranslator.translateAsLocalNameReference(descriptor, context);
082        }
083        ResolvedCallWithTrace<FunctionDescriptor> call = ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
084        return CallBuilder.build(context).resolvedCall(call).translate();
085    }
086
087    @Nullable
088    private JsExpression getThisObject() {
089        if (qualifier != null && !isExtensionCall) {
090            return qualifier;
091        }
092        return context.thisAliasProvider().get(resolvedCall);
093    }
094
095    @NotNull
096    private JsExpression getExtensionFunctionCallReceiver() {
097        if (qualifier != null) {
098            return qualifier;
099        }
100        DeclarationDescriptor receiverDescriptor = getDeclarationDescriptorForExtensionCallReceiver(resolvedCall);
101        return context.getThisObject(receiverDescriptor);
102    }
103}