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.*;
020 import com.intellij.util.SmartList;
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.ValueParameterDescriptor;
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.psi.ValueArgument;
029 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
030 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedValueArgument;
031 import org.jetbrains.jet.lang.resolve.calls.model.VarargValueArgument;
032 import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
033 import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor;
034 import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
035 import org.jetbrains.k2js.translate.context.TranslationContext;
036 import org.jetbrains.k2js.translate.general.Translation;
037 import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
038 import org.jetbrains.k2js.translate.utils.PsiUtils;
039
040 import java.util.ArrayList;
041 import java.util.List;
042
043 import static org.jetbrains.k2js.translate.utils.PsiUtils.getCallee;
044
045 public final class CallExpressionTranslator extends AbstractCallExpressionTranslator {
046
047 @NotNull
048 public static JsExpression translate(@NotNull JetCallExpression expression,
049 @Nullable JsExpression receiver,
050 @NotNull CallType callType,
051 @NotNull TranslationContext context) {
052 if (InlinedCallExpressionTranslator.shouldBeInlined(expression, context)) {
053 return InlinedCallExpressionTranslator.translate(expression, receiver, callType, context);
054 }
055 return (new CallExpressionTranslator(expression, receiver, callType, context)).translate();
056 }
057
058 private final boolean isNativeFunctionCall;
059 private boolean hasSpreadOperator = false;
060 private TemporaryConstVariable cachedReceiver = null;
061 private List<JsExpression> translatedArguments = null;
062 private JsExpression translatedReceiver = null;
063 private JsExpression translatedCallee = null;
064
065 private CallExpressionTranslator(@NotNull JetCallExpression expression,
066 @Nullable JsExpression receiver,
067 @NotNull CallType callType, @NotNull TranslationContext context) {
068 super(expression, receiver, callType, context);
069 this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor());
070 }
071
072 @NotNull
073 private JsExpression translate() {
074 prepareToBuildCall();
075
076 return CallBuilder.build(context())
077 .receiver(translatedReceiver)
078 .callee(translatedCallee)
079 .args(translatedArguments)
080 .resolvedCall(getResolvedCall())
081 .type(callType)
082 .translate();
083 }
084
085 private void prepareToBuildCall() {
086 translatedArguments = translateArguments();
087 translatedReceiver = getReceiver();
088 translatedCallee = getCalleeExpression();
089 }
090
091 @NotNull
092 private ResolvedCall<?> getResolvedCall() {
093 if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
094 return ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
095 }
096 return resolvedCall;
097 }
098
099 @Nullable
100 private JsExpression getReceiver() {
101 assert translatedArguments != null : "the results of this function depends on the results of translateArguments()";
102 if (receiver == null) {
103 return null;
104 }
105 if (cachedReceiver != null) {
106 return cachedReceiver.assignmentExpression();
107 }
108 return receiver;
109 }
110
111 @Nullable
112 private JsExpression getCalleeExpression() {
113 assert translatedArguments != null : "the results of this function depends on the results of translateArguments()";
114 if (isNativeFunctionCall && hasSpreadOperator) {
115 String functionName = resolvedCall.getCandidateDescriptor().getOriginal().getName().getIdentifier();
116 return new JsNameRef("apply", functionName);
117 }
118 CallableDescriptor candidateDescriptor = resolvedCall.getCandidateDescriptor();
119 if (candidateDescriptor instanceof ExpressionAsFunctionDescriptor) {
120 return translateExpressionAsFunction();
121 }
122 if (resolvedCall instanceof VariableAsFunctionResolvedCall) {
123 return translateVariableForVariableAsFunctionResolvedCall();
124 }
125 return null;
126 }
127
128 @NotNull
129 //TODO: looks hacky and should be modified soon
130 private JsExpression translateVariableForVariableAsFunctionResolvedCall() {
131 JetExpression callee = PsiUtils.getCallee(expression);
132 if (callee instanceof JetSimpleNameExpression) {
133 return ReferenceTranslator.getAccessTranslator((JetSimpleNameExpression) callee, receiver, context()).translateAsGet();
134 }
135 assert receiver != null;
136 return Translation.translateAsExpression(callee, context());
137 }
138
139 @NotNull
140 private JsExpression translateExpressionAsFunction() {
141 return Translation.translateAsExpression(getCallee(expression), context());
142 }
143
144 @NotNull
145 private List<JsExpression> translateArguments() {
146 List<JsExpression> result = new ArrayList<JsExpression>();
147 List<JsExpression> argsBeforeVararg = null;
148 for (ValueParameterDescriptor parameterDescriptor : resolvedCall.getResultingDescriptor().getValueParameters()) {
149 ResolvedValueArgument actualArgument = resolvedCall.getValueArgumentsByIndex().get(parameterDescriptor.getIndex());
150
151 if (actualArgument instanceof VarargValueArgument) {
152 assert !hasSpreadOperator;
153
154 List<ValueArgument> arguments = actualArgument.getArguments();
155 hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
156
157 if (isNativeFunctionCall && hasSpreadOperator) {
158 assert argsBeforeVararg == null;
159 argsBeforeVararg = result;
160 result = new SmartList<JsExpression>();
161 }
162 }
163
164 result.addAll(translateSingleArgument(actualArgument, parameterDescriptor));
165 }
166
167 if (isNativeFunctionCall && hasSpreadOperator) {
168 assert argsBeforeVararg != null;
169 if (!argsBeforeVararg.isEmpty()) {
170 JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result);
171 result = new SmartList<JsExpression>(concatArguments);
172 }
173
174 if (receiver != null) {
175 cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
176 result.add(0, cachedReceiver.reference());
177 }
178 else {
179 result.add(0, JsLiteral.NULL);
180 }
181 }
182
183 return result;
184 }
185
186 @Override
187 public boolean shouldWrapVarargInArray() {
188 return !isNativeFunctionCall && !hasSpreadOperator;
189 }
190 }