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