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 org.jetbrains.annotations.NotNull;
021 import org.jetbrains.annotations.Nullable;
022 import org.jetbrains.jet.lang.descriptors.*;
023 import org.jetbrains.jet.lang.psi.JetSuperExpression;
024 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
025 import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
026 import org.jetbrains.jet.lang.resolve.calls.util.ExpressionAsFunctionDescriptor;
027 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExpressionReceiver;
028 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
029 import org.jetbrains.k2js.translate.context.Namer;
030 import org.jetbrains.k2js.translate.context.TranslationContext;
031 import org.jetbrains.k2js.translate.general.AbstractTranslator;
032 import org.jetbrains.k2js.translate.intrinsic.functions.basic.FunctionIntrinsic;
033 import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
034 import org.jetbrains.k2js.translate.utils.ErrorReportingUtils;
035 import org.jetbrains.k2js.translate.utils.TranslationUtils;
036
037 import java.util.ArrayList;
038 import java.util.List;
039
040 import static org.jetbrains.k2js.translate.reference.CallParametersResolver.resolveCallParameters;
041 import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
042 import static org.jetbrains.k2js.translate.utils.JsAstUtils.setQualifier;
043
044 //TODO: write tests on calling backing fields as functions
045 public final class CallTranslator extends AbstractTranslator {
046 @NotNull
047 private final List<JsExpression> arguments;
048 @NotNull
049 private final ResolvedCall<?> resolvedCall;
050 @NotNull
051 private final CallableDescriptor descriptor;
052 @NotNull
053 private final CallType callType;
054 @NotNull
055 private final CallParameters callParameters;
056
057 /*package*/ CallTranslator(@Nullable JsExpression receiver, @Nullable JsExpression callee,
058 @NotNull List<JsExpression> arguments,
059 @NotNull ResolvedCall<? extends CallableDescriptor> resolvedCall,
060 @NotNull CallableDescriptor descriptorToCall,
061 @NotNull CallType callType,
062 @NotNull TranslationContext context) {
063 super(context);
064 this.arguments = arguments;
065 this.resolvedCall = resolvedCall;
066 this.callType = callType;
067 this.descriptor = descriptorToCall;
068 this.callParameters = resolveCallParameters(receiver, callee, descriptor, resolvedCall, context);
069 }
070
071 @NotNull
072 public ResolvedCall<? extends CallableDescriptor> getResolvedCall() {
073 return resolvedCall;
074 }
075
076 @NotNull
077 public CallParameters getCallParameters() {
078 return callParameters;
079 }
080
081 @NotNull
082 /*package*/ JsExpression translate() {
083 JsExpression result = intrinsicInvocation();
084 if (result != null) {
085 return result;
086 }
087 if ((descriptor instanceof ConstructorDescriptor)) {
088 return createConstructorCallExpression(translateAsFunctionWithNoThisObject(descriptor));
089 }
090 if (resolvedCall.getReceiverArgument().exists()) {
091 if (AnnotationsUtils.isNativeObject(descriptor)) {
092 return nativeExtensionCall();
093 }
094 return extensionFunctionCall(!(descriptor instanceof ExpressionAsFunctionDescriptor));
095 }
096 if (isExpressionAsFunction()) {
097 return expressionAsFunctionCall();
098 }
099 if (isSuperInvocation()) {
100 return superMethodCall(getThisObjectOrQualifier());
101 }
102
103 return methodCall(getThisObjectOrQualifier());
104 }
105
106 private boolean isExpressionAsFunction() {
107 return descriptor instanceof ExpressionAsFunctionDescriptor ||
108 resolvedCall instanceof VariableAsFunctionResolvedCall;
109 }
110
111 @NotNull
112 private JsExpression expressionAsFunctionCall() {
113 return methodCall(null);
114 }
115
116 @Nullable
117 private JsExpression intrinsicInvocation() {
118 if (descriptor instanceof FunctionDescriptor) {
119 try {
120 FunctionIntrinsic intrinsic = context().intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) descriptor);
121 if (intrinsic.exists()) {
122 return intrinsic.apply(this, arguments, context());
123 }
124 }
125 catch (RuntimeException e) {
126 throw ErrorReportingUtils.reportErrorWithLocation(e, descriptor, bindingContext());
127 }
128 }
129 return null;
130 }
131
132 @NotNull
133 public HasArguments createConstructorCallExpression(@NotNull JsExpression constructorReference) {
134 return new JsNew(constructorReference, arguments);
135 }
136
137 @NotNull
138 private JsExpression translateAsFunctionWithNoThisObject(@NotNull DeclarationDescriptor descriptor) {
139 return ReferenceTranslator.translateAsFQReference(descriptor, context());
140 }
141
142 @NotNull
143 private JsExpression nativeExtensionCall() {
144 return methodCall(callParameters.getReceiver());
145 }
146
147 @NotNull
148 public JsExpression explicitInvokeCall() {
149 return callType.constructCall(callParameters.getThisObject(), new CallType.CallConstructor() {
150 @NotNull
151 @Override
152 public JsExpression construct(@Nullable JsExpression receiver) {
153 return new JsInvocation(receiver, arguments);
154 }
155 }, context());
156 }
157
158 @NotNull
159 public JsExpression extensionFunctionCall(final boolean useThis) {
160 return callType.constructCall(callParameters.getReceiver(), new CallType.CallConstructor() {
161 @NotNull
162 @Override
163 public JsExpression construct(@Nullable JsExpression receiver) {
164 assert receiver != null : "Could not be null for extensions";
165 JsExpression functionReference = callParameters.getFunctionReference();
166 if (useThis) {
167 setQualifier(functionReference, getThisObjectOrQualifier());
168 }
169 return new JsInvocation(callParameters.getFunctionReference(), generateCallArgumentList(receiver));
170 }
171 }, context());
172 }
173
174 @NotNull
175 private List<JsExpression> generateCallArgumentList(@NotNull JsExpression receiver) {
176 return TranslationUtils.generateInvocationArguments(receiver, arguments);
177 }
178
179 private boolean isSuperInvocation() {
180 ReceiverValue thisObject = resolvedCall.getThisObject();
181 return thisObject instanceof ExpressionReceiver && ((ExpressionReceiver) thisObject).getExpression() instanceof JetSuperExpression;
182 }
183
184 @NotNull
185 private JsExpression superMethodCall(@Nullable JsExpression receiver) {
186 return callType.constructCall(receiver, new CallType.CallConstructor() {
187 @NotNull
188 @Override
189 public JsExpression construct(@Nullable JsExpression receiver) {
190 assert receiver != null : "Receiver for superCall must be not null";
191 if (isDirectPropertyAccess()) {
192 return superPropertyAccess(receiver, getPropertyName());
193 }
194
195 JsExpression qualifiedCallee = getQualifiedCallee(Namer.getRefToPrototype(receiver));
196 qualifiedCallee = Namer.getFunctionCallRef(qualifiedCallee);
197 List<JsExpression> arguments = new ArrayList<JsExpression>(CallTranslator.this.arguments.size() + 1);
198 arguments.add(JsLiteral.THIS);
199 arguments.addAll(CallTranslator.this.arguments);
200 return new JsInvocation(qualifiedCallee, arguments);
201 }
202 }, context());
203 }
204
205 @NotNull
206 private JsStringLiteral getPropertyName() {
207 assert descriptor instanceof PropertyAccessorDescriptor;
208 String propertyName = ((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty().getName().asString();
209 return context().program().getStringLiteral(propertyName);
210 }
211
212 @NotNull
213 private JsExpression superPropertyAccess(@NotNull JsExpression classRef, @NotNull JsStringLiteral propertyName) {
214 if (descriptor instanceof PropertyGetterDescriptor) {
215 assert arguments.isEmpty();
216 return new JsInvocation(context().namer().getCallGetProperty(), JsLiteral.THIS, classRef, propertyName);
217 } else {
218 assert descriptor instanceof PropertySetterDescriptor;
219 assert arguments.size() == 1;
220 return new JsInvocation(context().namer().getCallSetProperty(), JsLiteral.THIS, classRef, propertyName, arguments.get(0));
221 }
222 }
223
224 @NotNull
225 private JsExpression methodCall(@Nullable JsExpression receiver) {
226 return callType.constructCall(receiver, new CallType.CallConstructor() {
227 @NotNull
228 @Override
229 public JsExpression construct(@Nullable JsExpression receiver) {
230 JsExpression qualifiedCallee = getQualifiedCallee(receiver);
231 if (isDirectPropertyAccess()) {
232 return directPropertyAccess(qualifiedCallee);
233 }
234
235 return new JsInvocation(qualifiedCallee, arguments);
236 }
237 }, context());
238 }
239
240 @NotNull
241 private JsExpression directPropertyAccess(@NotNull JsExpression callee) {
242 if (descriptor instanceof PropertyGetterDescriptor) {
243 assert arguments.isEmpty();
244 return callee;
245 }
246 else {
247 assert descriptor instanceof PropertySetterDescriptor;
248 assert arguments.size() == 1;
249 return assignment(callee, arguments.get(0));
250 }
251 }
252
253 private boolean isDirectPropertyAccess() {
254 return descriptor instanceof PropertyAccessorDescriptor;
255 }
256
257 @NotNull
258 private JsExpression getQualifiedCallee(@Nullable JsExpression receiver) {
259 JsExpression callee = callParameters.getFunctionReference();
260 if (receiver != null) {
261 setQualifier(callee, receiver);
262 }
263 return callee;
264 }
265
266 @Nullable
267 private JsExpression getThisObjectOrQualifier() {
268 JsExpression thisObject = callParameters.getThisObject();
269 if (thisObject != null) {
270 return thisObject;
271 }
272 return context().getQualifierForDescriptor(descriptor);
273 }
274 }