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.JsLiteral;
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.FunctionDescriptor;
025 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCall;
026 import org.jetbrains.jet.lang.resolve.calls.model.ResolvedCallWithTrace;
027 import org.jetbrains.jet.lang.resolve.calls.model.VariableAsFunctionResolvedCall;
028 import org.jetbrains.jet.lang.resolve.scopes.receivers.ClassReceiver;
029 import org.jetbrains.jet.lang.resolve.scopes.receivers.ExtensionReceiver;
030 import org.jetbrains.jet.lang.resolve.scopes.receivers.ReceiverValue;
031 import org.jetbrains.jet.lang.resolve.scopes.receivers.ThisReceiver;
032 import org.jetbrains.k2js.translate.context.TranslationContext;
033
034 import static org.jetbrains.k2js.translate.utils.JsDescriptorUtils.getDeclarationDescriptorForReceiver;
035
036 public final class CallParametersResolver {
037 public static CallParameters resolveCallParameters(@Nullable JsExpression qualifier,
038 @Nullable JsExpression callee,
039 @NotNull CallableDescriptor descriptor,
040 @NotNull ResolvedCall<? extends CallableDescriptor> call,
041 @NotNull TranslationContext context) {
042 return (new CallParametersResolver(qualifier, callee, descriptor, call, context)).resolve();
043 }
044
045 // the actual qualifier for the call at the call site
046 @Nullable
047 private final JsExpression qualifier;
048 @Nullable
049 private final JsExpression callee;
050 @NotNull
051 private final CallableDescriptor descriptor;
052 @NotNull
053 private final TranslationContext context;
054 @NotNull
055 private final ResolvedCall<? extends CallableDescriptor> resolvedCall;
056 private final boolean isExtensionCall;
057
058 private CallParametersResolver(@Nullable JsExpression qualifier,
059 @Nullable JsExpression callee,
060 @NotNull CallableDescriptor descriptor,
061 @NotNull ResolvedCall<? extends CallableDescriptor> call,
062 @NotNull TranslationContext context) {
063 this.qualifier = qualifier;
064 this.callee = callee;
065 this.descriptor = descriptor;
066 this.context = context;
067 this.resolvedCall = call;
068 this.isExtensionCall = resolvedCall.getReceiverArgument().exists();
069 }
070
071 @NotNull
072 private CallParameters resolve() {
073 JsExpression receiver = isExtensionCall ? getExtensionFunctionCallReceiver() : null;
074 JsExpression functionReference = getFunctionReference();
075 JsExpression thisObject = getThisObject();
076 return new CallParameters(receiver, functionReference, thisObject);
077 }
078
079 @NotNull
080 private JsExpression getFunctionReference() {
081 if (callee != null) {
082 return callee;
083 }
084 if (!(resolvedCall instanceof VariableAsFunctionResolvedCall)) {
085 return ReferenceTranslator.translateAsLocalNameReference(descriptor, context);
086 }
087 ResolvedCallWithTrace<FunctionDescriptor> call = ((VariableAsFunctionResolvedCall) resolvedCall).getFunctionCall();
088 return CallBuilder.build(context).resolvedCall(call).translate();
089 }
090
091 @Nullable
092 private JsExpression getThisObject() {
093 if (qualifier != null && !isExtensionCall) {
094 return qualifier;
095 }
096
097 ReceiverValue thisObject = resolvedCall.getThisObject();
098 if (!thisObject.exists()) {
099 return null;
100 }
101
102 if (thisObject instanceof ClassReceiver) {
103 JsExpression ref = context.getAliasForDescriptor(((ClassReceiver) thisObject).getDeclarationDescriptor());
104 return ref == null ? JsLiteral.THIS : ref;
105 }
106 else if (thisObject instanceof ExtensionReceiver) {
107 return context.getAliasForDescriptor(getDeclarationDescriptorForReceiver(thisObject));
108 }
109
110 return resolvedCall.getReceiverArgument().exists() && resolvedCall.getExplicitReceiverKind().isThisObject() ? JsLiteral.THIS : null;
111 }
112
113 @NotNull
114 private JsExpression getExtensionFunctionCallReceiver() {
115 if (qualifier != null) {
116 return qualifier;
117 }
118 return context.getThisObject(((ThisReceiver) resolvedCall.getReceiverArgument()).getDeclarationDescriptor());
119 }
120 }