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 }