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.ValueParameterDescriptor;
024 import org.jetbrains.jet.lang.psi.JetExpression;
025 import org.jetbrains.jet.lang.psi.ValueArgument;
026 import org.jetbrains.jet.lang.resolve.calls.model.*;
027 import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
028 import org.jetbrains.k2js.translate.context.TranslationContext;
029 import org.jetbrains.k2js.translate.general.AbstractTranslator;
030 import org.jetbrains.k2js.translate.general.Translation;
031 import org.jetbrains.k2js.translate.utils.AnnotationsUtils;
032
033 import java.util.ArrayList;
034 import java.util.Collections;
035 import java.util.List;
036
037 public class CallArgumentTranslator extends AbstractTranslator {
038
039 @NotNull
040 public static ArgumentsInfo translate(
041 @NotNull ResolvedCall<?> resolvedCall,
042 @Nullable JsExpression receiver,
043 @NotNull TranslationContext context
044 ) {
045 CallArgumentTranslator argumentTranslator = new CallArgumentTranslator(resolvedCall, receiver, context);
046 return argumentTranslator.translate();
047 }
048
049 public static class ArgumentsInfo {
050 private final List<JsExpression> translateArguments;
051 private final boolean hasSpreadOperator;
052 private final TemporaryConstVariable cachedReceiver;
053
054 public ArgumentsInfo(List<JsExpression> arguments, boolean operator, TemporaryConstVariable receiver) {
055 translateArguments = arguments;
056 hasSpreadOperator = operator;
057 cachedReceiver = receiver;
058 }
059
060 @NotNull
061 public List<JsExpression> getTranslateArguments() {
062 return translateArguments;
063 }
064
065 public boolean isHasSpreadOperator() {
066 return hasSpreadOperator;
067 }
068
069 @Nullable
070 public TemporaryConstVariable getCachedReceiver() {
071 return cachedReceiver;
072 }
073 }
074
075 public static void translateSingleArgument(
076 @NotNull ResolvedValueArgument actualArgument,
077 @NotNull List<JsExpression> result,
078 @NotNull TranslationContext context,
079 boolean shouldWrapVarargInArray
080 ) {
081 List<ValueArgument> valueArguments = actualArgument.getArguments();
082 if (actualArgument instanceof VarargValueArgument) {
083 translateVarargArgument(valueArguments, result, context, shouldWrapVarargInArray);
084 }
085 else if (actualArgument instanceof DefaultValueArgument) {
086 result.add(context.namer().getUndefinedExpression());
087 }
088 else {
089 assert actualArgument instanceof ExpressionValueArgument;
090 assert valueArguments.size() == 1;
091 JetExpression argumentExpression = valueArguments.get(0).getArgumentExpression();
092 assert argumentExpression != null;
093 result.add(Translation.translateAsExpression(argumentExpression, context));
094 }
095 }
096
097 private static void translateVarargArgument(
098 @NotNull List<ValueArgument> arguments,
099 @NotNull List<JsExpression> result,
100 @NotNull TranslationContext context,
101 boolean shouldWrapVarargInArray
102 ) {
103 if (arguments.isEmpty()) {
104 if (shouldWrapVarargInArray) {
105 result.add(new JsArrayLiteral(Collections.<JsExpression>emptyList()));
106 }
107 return;
108 }
109
110 List<JsExpression> list;
111 if (shouldWrapVarargInArray) {
112 list = arguments.size() == 1 ? new SmartList<JsExpression>() : new ArrayList<JsExpression>(arguments.size());
113 result.add(new JsArrayLiteral(list));
114 }
115 else {
116 list = result;
117 }
118 for (ValueArgument argument : arguments) {
119 JetExpression argumentExpression = argument.getArgumentExpression();
120 assert argumentExpression != null;
121 list.add(Translation.translateAsExpression(argumentExpression, context));
122 }
123 }
124
125 @NotNull
126 private final ResolvedCall<?> resolvedCall;
127 @Nullable
128 private final JsExpression receiver;
129 private final boolean isNativeFunctionCall;
130
131 private CallArgumentTranslator(
132 @NotNull ResolvedCall<?> resolvedCall,
133 @Nullable JsExpression receiver,
134 @NotNull TranslationContext context
135 ) {
136 super(context);
137 this.resolvedCall = resolvedCall;
138 this.receiver = receiver;
139 this.isNativeFunctionCall = AnnotationsUtils.isNativeObject(resolvedCall.getCandidateDescriptor());
140 }
141
142 private void removeLastUndefinedArguments(@NotNull List<JsExpression> result) {
143 int i;
144 for (i = result.size() - 1; i >= 0; i--) {
145 if (result.get(i) != context().namer().getUndefinedExpression()) {
146 break;
147 }
148 }
149 result.subList(i + 1, result.size()).clear();
150 }
151
152 private ArgumentsInfo translate() {
153 List<ValueParameterDescriptor> valueParameters = resolvedCall.getResultingDescriptor().getValueParameters();
154 if (valueParameters.isEmpty()) {
155 return new ArgumentsInfo(Collections.<JsExpression>emptyList(), false, null);
156 }
157 boolean hasSpreadOperator = false;
158 TemporaryConstVariable cachedReceiver = null;
159
160 List<JsExpression> result = new ArrayList<JsExpression>(valueParameters.size());
161 List<ResolvedValueArgument> valueArgumentsByIndex = resolvedCall.getValueArgumentsByIndex();
162 List<JsExpression> argsBeforeVararg = null;
163 for (ValueParameterDescriptor parameterDescriptor : valueParameters) {
164 ResolvedValueArgument actualArgument = valueArgumentsByIndex.get(parameterDescriptor.getIndex());
165
166 if (actualArgument instanceof VarargValueArgument) {
167 assert !hasSpreadOperator;
168
169 List<ValueArgument> arguments = actualArgument.getArguments();
170 hasSpreadOperator = arguments.size() == 1 && arguments.get(0).getSpreadElement() != null;
171
172 if (isNativeFunctionCall && hasSpreadOperator) {
173 argsBeforeVararg = result;
174 result = new SmartList<JsExpression>();
175 }
176 }
177
178 translateSingleArgument(actualArgument, result, context(), !isNativeFunctionCall && !hasSpreadOperator);
179 }
180
181 if (isNativeFunctionCall && hasSpreadOperator) {
182 if (!argsBeforeVararg.isEmpty()) {
183 JsInvocation concatArguments = new JsInvocation(new JsNameRef("concat", new JsArrayLiteral(argsBeforeVararg)), result);
184 result = new SmartList<JsExpression>(concatArguments);
185 }
186
187 if (receiver != null) {
188 cachedReceiver = context().getOrDeclareTemporaryConstVariable(receiver);
189 result.add(0, cachedReceiver.reference());
190 }
191 else {
192 result.add(0, JsLiteral.NULL);
193 }
194 }
195
196 removeLastUndefinedArguments(result);
197 return new ArgumentsInfo(result, hasSpreadOperator, cachedReceiver);
198 }
199
200 }