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.JsName;
021    import com.google.dart.compiler.backend.js.ast.JsNameRef;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.jet.lang.descriptors.CallableDescriptor;
025    import org.jetbrains.jet.lang.psi.JetCallExpression;
026    import org.jetbrains.jet.lang.psi.JetExpression;
027    import org.jetbrains.jet.lang.psi.JetSimpleNameExpression;
028    import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
029    import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
030    import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor;
031    import org.jetbrains.k2js.translate.context.TranslationContext;
032    import org.jetbrains.k2js.translate.general.Translation;
033    import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
034    import org.jetbrains.k2js.translate.utils.PsiUtils;
035    
036    import static org.jetbrains.k2js.translate.utils.PsiUtils.getCallee;
037    
038    public final class CallExpressionTranslator extends AbstractCallExpressionTranslator {
039    
040        @NotNull
041        public static JsExpression translate(@NotNull JetCallExpression expression,
042                @Nullable JsExpression receiver,
043                @NotNull CallType callType,
044                @NotNull TranslationContext context) {
045            if (InlinedCallExpressionTranslator.shouldBeInlined(expression, context)) {
046                return InlinedCallExpressionTranslator.translate(expression, receiver, callType, context);
047            }
048            return (new CallExpressionTranslator(expression, receiver, callType, context)).translate();
049        }
050    
051        private final boolean isNativeFunctionCall;
052        private CallArgumentTranslator.ArgumentsInfo argumentsInfo = null;
053        private JsExpression translatedReceiver = null;
054        private JsExpression translatedCallee = null;
055    
056        private CallExpressionTranslator(@NotNull JetCallExpression expression,
057                @Nullable JsExpression receiver,
058                @NotNull CallType callType, @NotNull TranslationContext context) {
059            super(expression, receiver, callType, context);
060            this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor());
061        }
062    
063        @NotNull
064        private JsExpression translate() {
065            prepareToBuildCall();
066    
067            return CallBuilder.build(context())
068                    .receiver(translatedReceiver)
069                    .callee(translatedCallee)
070                    .args(argumentsInfo.getTranslateArguments())
071                    .resolvedCall(getResolvedCall())
072                    .type(callType)
073                    .translate();
074        }
075    
076        private void prepareToBuildCall() {
077            argumentsInfo = CallArgumentTranslator.translate(resolvedCall, receiver, context());
078            translatedReceiver = getReceiver();
079            translatedCallee = getCalleeExpression();
080        }
081    
082        @NotNull
083        private ResolvedCall<?> getResolvedCall() {
084            if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
085                return ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
086            }
087            return resolvedCall;
088        }
089    
090        @Nullable
091        private JsExpression getReceiver() {
092            assert argumentsInfo != null : "the results of this function depends on the argumentsInfo";
093            if (receiver == null) {
094                return null;
095            }
096            if (argumentsInfo.getCachedReceiver() != null) {
097                return argumentsInfo.getCachedReceiver().assignmentExpression();
098            }
099            return receiver;
100        }
101    
102        @Nullable
103        private JsExpression getCalleeExpression() {
104            assert argumentsInfo != null : "the results of this function depends on the argumentsInfo";
105            if (isNativeFunctionCall && argumentsInfo.isHasSpreadOperator()) {
106                JsName functionName = context().getNameForDescriptor(resolvedCall.getCandidateDescriptor());
107                return new JsNameRef("apply", functionName.makeRef());
108            }
109            CallableDescriptor candidateDescriptor = resolvedCall.getCandidateDescriptor();
110            if (candidateDescriptor instanceof ExpressionAsFunctionDescriptor) {
111                return translateExpressionAsFunction();
112            }
113            if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
114                return translateVariableForVariableAsFunctionResolvedCall();
115            }
116            return null;
117        }
118    
119        @NotNull
120        //TODO: looks hacky and should be modified soon
121        private JsExpression translateVariableForVariableAsFunctionResolvedCall() {
122            JetExpression callee = PsiUtils.getCallee(expression);
123            if (callee instanceof JetSimpleNameExpression) {
124                return ReferenceTranslator.getAccessTranslator((JetSimpleNameExpression) callee, receiver, context()).translateAsGet();
125            }
126            assert receiver != null;
127            return Translation.translateAsExpression(callee, context());
128        }
129    
130        @NotNull
131        private JsExpression translateExpressionAsFunction() {
132            return Translation.translateAsExpression(getCallee(expression), context());
133        }
134    
135    }