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