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