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    
017    package org.jetbrains.k2js.translate.reference;
018    
019    import com.google.dart.compiler.backend.js.ast.JsExpression;
020    import com.google.dart.compiler.backend.js.ast.JsLiteral;
021    import org.jetbrains.annotations.NotNull;
022    import org.jetbrains.annotations.Nullable;
023    import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
024    import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
025    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
026    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
027    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
028    import org.jetbrains.jet.lang.resolve.scopes.receivers.ClassReceiver;
029    import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
030    import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
031    import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
032    import org.jetbrains.k2js.translate.context.TranslationContext;
033    
034    import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForReceiver;
035    
036    public final class CallParametersResolver {
037        public static CallParameters resolveCallParameters(@Nullable JsExpression qualifier,
038                @Nullable JsExpression callee,
039                @NotNull CallableDescriptor descriptor,
040                @NotNull ResolvedCall<? extends CallableDescriptor> call,
041                @NotNull TranslationContext context) {
042            return (new CallParametersResolver(qualifier, callee, descriptor, call, context)).resolve();
043        }
044    
045        // the actual qualifier for the call at the call site
046        @Nullable
047        private final JsExpression qualifier;
048        @Nullable
049        private final JsExpression callee;
050        @NotNull
051        private final CallableDescriptor descriptor;
052        @NotNull
053        private final TranslationContext context;
054        @NotNull
055        private final ResolvedCall<? extends CallableDescriptor> resolvedCall;
056        private final boolean isExtensionCall;
057    
058        private CallParametersResolver(@Nullable JsExpression qualifier,
059                @Nullable JsExpression callee,
060                @NotNull CallableDescriptor descriptor,
061                @NotNull ResolvedCall<? extends CallableDescriptor> call,
062                @NotNull TranslationContext context) {
063            this.qualifier = qualifier;
064            this.callee = callee;
065            this.descriptor = descriptor;
066            this.context = context;
067            this.resolvedCall = call;
068            this.isExtensionCall = resolvedCall.getReceiverArgument().exists();
069        }
070    
071        @NotNull
072        private CallParameters resolve() {
073            JsExpression receiver = isExtensionCall ? getExtensionFunctionCallReceiver() : null;
074            JsExpression functionReference = getFunctionReference();
075            JsExpression thisObject = getThisObject();
076            return new CallParameters(receiver, functionReference, thisObject);
077        }
078    
079        @NotNull
080        private JsExpression getFunctionReference() {
081            if (callee != null) {
082                return callee;
083            }
084            if (!(resolvedCall instanceof VariableAsFunctionResolvedCall)) {
085                return ReferenceTranslator.translateAsLocalNameReference(descriptor, context);
086            }
087            ResolvedCallWithTrace<FunctionDescriptor> call = ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
088            return CallBuilder.build(context).resolvedCall(call).translate();
089        }
090    
091        @Nullable
092        private JsExpression getThisObject() {
093            if (qualifier != null && !isExtensionCall) {
094                return qualifier;
095            }
096    
097            ReceiverValue thisObject = resolvedCall.getThisObject();
098            if (!thisObject.exists()) {
099                return null;
100            }
101    
102            if (thisObject instanceof ClassReceiver) {
103                JsExpression ref = context.getAliasForDescriptor(((ClassReceiver) thisObject).getDeclarationDescriptor());
104                return ref == null ? JsLiteral.THIS : ref;
105            }
106            else if (thisObject instanceof ExtensionReceiver) {
107                return context.getAliasForDescriptor(getDeclarationDescriptorForReceiver(thisObject));
108            }
109    
110            return resolvedCall.getReceiverArgument().exists() && resolvedCall.getExplicitReceiverKind().isThisObject() ? JsLiteral.THIS : null;
111        }
112    
113        @NotNull
114        private JsExpression getExtensionFunctionCallReceiver() {
115            if (qualifier != null) {
116                return qualifier;
117            }
118            return context.getThisObject(((ThisReceiver) resolvedCall.getReceiverArgument()).getDeclarationDescriptor());
119        }
120    }