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 }