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.utils;
018
019 import com.google.dart.compiler.backend.js.ast.*;
020 import com.intellij.openapi.util.Pair;
021 import com.intellij.util.SmartList;
022 import org.jetbrains.annotations.NotNull;
023 import org.jetbrains.annotations.Nullable;
024 import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
025 import org.jetbrains.jet.lang.descriptors.FunctionDescriptor;
026 import org.jetbrains.jet.lang.descriptors.PropertyDescriptor;
027 import org.jetbrains.jet.lang.descriptors.PropertyGetterDescriptor;
028 import org.jetbrains.jet.lang.psi.*;
029 import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
030 import org.jetbrains.k2js.translate.context.TranslationContext;
031 import org.jetbrains.k2js.translate.general.Translation;
032
033 import java.util.ArrayList;
034 import java.util.Collections;
035 import java.util.List;
036
037 import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
038 import static org.jetbrains.k2js.translate.utils.BindingUtils.getFunctionDescriptorForOperationExpression;
039 import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
040 import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor;
041
042 public final class TranslationUtils {
043 private TranslationUtils() {
044 }
045
046 @NotNull
047 public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
048 @NotNull FunctionDescriptor descriptor,
049 @NotNull TranslationContext context) {
050 if (JsDescriptorUtils.isExtension(descriptor)) {
051 return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
052 }
053 else {
054 JsStringLiteral getOrSet = context.program().getStringLiteral(descriptor instanceof PropertyGetterDescriptor ? "get" : "set");
055 return new JsPropertyInitializer(getOrSet, function);
056 }
057 }
058
059 @NotNull
060 public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
061 return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)));
062 }
063
064 @NotNull
065 private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
066 @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
067 JsObjectLiteral meta = createDataDescriptor(function, descriptor.getModality().isOverridable());
068 return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
069 }
070
071 @NotNull
072 public static JsExpression translateExclForBinaryEqualLikeExpr(@NotNull JsBinaryOperation baseBinaryExpression) {
073 return new JsBinaryOperation(notOperator(baseBinaryExpression.getOperator()), baseBinaryExpression.getArg1(), baseBinaryExpression.getArg2());
074 }
075
076 public static boolean isEqualLikeOperator(@NotNull JsBinaryOperator operator) {
077 return notOperator(operator) != null;
078 }
079
080 @Nullable
081 private static JsBinaryOperator notOperator(@NotNull JsBinaryOperator operator) {
082 switch (operator) {
083 case REF_EQ:
084 return REF_NEQ;
085 case REF_NEQ:
086 return REF_EQ;
087 case EQ:
088 return NEQ;
089 case NEQ:
090 return EQ;
091 default:
092 return null;
093 }
094 }
095
096 @NotNull
097 public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) {
098 return nullCheck(expressionToCheck, false);
099 }
100
101 @NotNull
102 public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
103 return nullCheck(expressionToCheck, true);
104 }
105
106 @NotNull
107 public static JsBinaryOperation nullCheck(@NotNull JsExpression expressionToCheck, boolean isNegated) {
108 JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
109 return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
110 }
111
112 @NotNull
113 public static JsConditional notNullConditional(
114 @NotNull JsExpression expression,
115 @NotNull JsExpression elseExpression,
116 @NotNull TranslationContext context
117 ) {
118 JsExpression testExpression;
119 JsExpression thenExpression;
120 if (isCacheNeeded(expression)) {
121 TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(expression);
122 testExpression = isNotNullCheck(tempVar.value());
123 thenExpression = tempVar.value();
124 }
125 else {
126 testExpression = isNotNullCheck(expression);
127 thenExpression = expression;
128 }
129
130 return new JsConditional(testExpression, thenExpression, elseExpression);
131 }
132
133 @NotNull
134 public static List<JsExpression> translateArgumentList(@NotNull TranslationContext context,
135 @NotNull List<? extends ValueArgument> jetArguments) {
136 if (jetArguments.isEmpty()) {
137 return Collections.emptyList();
138 }
139
140 List<JsExpression> jsArguments = new SmartList<JsExpression>();
141 for (ValueArgument argument : jetArguments) {
142 jsArguments.add(translateArgument(context, argument));
143 }
144 return jsArguments;
145 }
146
147 @NotNull
148 private static JsExpression translateArgument(@NotNull TranslationContext context, @NotNull ValueArgument argument) {
149 JetExpression jetExpression = argument.getArgumentExpression();
150 assert jetExpression != null : "Argument with no expression";
151 return Translation.translateAsExpression(jetExpression, context);
152 }
153
154 @NotNull
155 public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
156 @NotNull PropertyDescriptor descriptor) {
157 JsName backingFieldName = context.getNameForDescriptor(descriptor);
158 return new JsNameRef(backingFieldName, JsLiteral.THIS);
159 }
160
161 @NotNull
162 public static JsExpression assignmentToBackingField(@NotNull TranslationContext context,
163 @NotNull PropertyDescriptor descriptor,
164 @NotNull JsExpression assignTo) {
165 JsNameRef backingFieldReference = backingFieldReference(context, descriptor);
166 return assignment(backingFieldReference, assignTo);
167 }
168
169 @Nullable
170 public static JsExpression translateInitializerForProperty(@NotNull JetProperty declaration,
171 @NotNull TranslationContext context) {
172 JsExpression jsInitExpression = null;
173 JetExpression initializer = declaration.getInitializer();
174 if (initializer != null) {
175 jsInitExpression = Translation.translateAsExpression(initializer, context);
176 }
177 return jsInitExpression;
178 }
179
180 @NotNull
181 public static JsNameRef getQualifiedReference(@NotNull TranslationContext context, @NotNull DeclarationDescriptor descriptor) {
182 return new JsNameRef(context.getNameForDescriptor(descriptor), context.getQualifierForDescriptor(descriptor));
183 }
184
185 @NotNull
186 public static List<JsExpression> translateExpressionList(@NotNull TranslationContext context,
187 @NotNull List<JetExpression> expressions) {
188 List<JsExpression> result = new ArrayList<JsExpression>();
189 for (JetExpression expression : expressions) {
190 result.add(Translation.translateAsExpression(expression, context));
191 }
192 return result;
193 }
194
195 @NotNull
196 public static JsExpression translateBaseExpression(@NotNull TranslationContext context,
197 @NotNull JetUnaryExpression expression) {
198 JetExpression baseExpression = PsiUtils.getBaseExpression(expression);
199 return Translation.translateAsExpression(baseExpression, context);
200 }
201
202 @NotNull
203 public static JsExpression translateLeftExpression(@NotNull TranslationContext context,
204 @NotNull JetBinaryExpression expression) {
205 JetExpression left = expression.getLeft();
206 assert left != null : "Binary expression should have a left expression: " + expression.getText();
207 return Translation.translateAsExpression(left, context);
208 }
209
210 @NotNull
211 public static JsExpression translateRightExpression(@NotNull TranslationContext context,
212 @NotNull JetBinaryExpression expression) {
213 JetExpression rightExpression = expression.getRight();
214 assert rightExpression != null : "Binary expression should have a right expression";
215 return Translation.translateAsExpression(rightExpression, context);
216 }
217
218 public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context,
219 @NotNull JetOperationExpression expression) {
220 FunctionDescriptor operationDescriptor = getFunctionDescriptorForOperationExpression(context.bindingContext(), expression);
221
222 if (operationDescriptor == null) return true;
223 if (context.intrinsics().getFunctionIntrinsics().getIntrinsic(operationDescriptor).exists()) return true;
224
225 return false;
226 }
227
228 @NotNull
229 public static List<JsExpression> generateInvocationArguments(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments) {
230 if (arguments.isEmpty()) {
231 return Collections.singletonList(receiver);
232 }
233
234 List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size());
235 argumentList.add(receiver);
236 argumentList.addAll(arguments);
237 return argumentList;
238 }
239
240 public static boolean isCacheNeeded(@NotNull JsExpression expression) {
241 return !(expression instanceof JsLiteral) &&
242 (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
243 }
244
245 @NotNull
246 public static Pair<JsVars.JsVar, JsExpression> createTemporaryIfNeed(
247 @NotNull JsExpression expression,
248 @NotNull TranslationContext context
249 ) {
250 // don't create temp variable for simple expression
251 if (isCacheNeeded(expression)) {
252 return context.dynamicContext().createTemporary(expression);
253 }
254 else {
255 return Pair.create(null, expression);
256 }
257 }
258
259 @NotNull
260 public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
261 JsInvocation throwNPE = new JsInvocation(context.namer().throwNPEFunctionRef());
262 JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context);
263
264 JsExpression thenExpression = ensureNotNull.getThenExpression();
265 if (thenExpression instanceof JsNameRef) {
266 // associate (cache) ensureNotNull expression to new TemporaryConstVariable with same name.
267 context.associateExpressionToLazyValue(ensureNotNull,
268 new TemporaryConstVariable(((JsNameRef) thenExpression).getName(), ensureNotNull));
269 }
270
271 return ensureNotNull;
272 }
273 }