001 /*
002 * Copyright 2010-2014 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.text.StringUtil;
021 import com.intellij.util.Function;
022 import com.intellij.util.containers.ContainerUtil;
023 import org.jetbrains.annotations.NotNull;
024 import org.jetbrains.annotations.Nullable;
025 import org.jetbrains.jet.backend.common.CodegenUtil;
026 import org.jetbrains.jet.lang.descriptors.*;
027 import org.jetbrains.jet.lang.descriptors.impl.AnonymousFunctionDescriptor;
028 import org.jetbrains.jet.lang.psi.*;
029 import org.jetbrains.jet.lang.resolve.OverrideResolver;
030 import org.jetbrains.jet.lang.resolve.name.FqName;
031 import org.jetbrains.jet.lang.resolve.name.Name;
032 import org.jetbrains.jet.lang.resolve.scopes.JetScope;
033 import org.jetbrains.jet.lang.types.JetType;
034 import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
035 import org.jetbrains.k2js.translate.context.TemporaryConstVariable;
036 import org.jetbrains.k2js.translate.context.TranslationContext;
037 import org.jetbrains.k2js.translate.general.Translation;
038
039 import java.util.*;
040
041 import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*;
042 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getFqName;
043 import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isAnonymousObject;
044 import static org.jetbrains.k2js.translate.context.Namer.getKotlinBackingFieldName;
045 import static org.jetbrains.k2js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression;
046 import static org.jetbrains.k2js.translate.utils.JsAstUtils.assignment;
047 import static org.jetbrains.k2js.translate.utils.JsAstUtils.createDataDescriptor;
048
049 public final class TranslationUtils {
050 public static final Comparator<FunctionDescriptor> OVERLOADED_FUNCTION_COMPARATOR = new OverloadedFunctionComparator();
051
052 private TranslationUtils() {
053 }
054
055 @NotNull
056 public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function,
057 @NotNull FunctionDescriptor descriptor,
058 @NotNull TranslationContext context) {
059 if (JsDescriptorUtils.isExtension(descriptor)) {
060 return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context);
061 }
062 else {
063 JsStringLiteral getOrSet = context.program().getStringLiteral(descriptor instanceof PropertyGetterDescriptor ? "get" : "set");
064 return new JsPropertyInitializer(getOrSet, function);
065 }
066 }
067
068 @NotNull
069 public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) {
070 return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)), "<simpleReturnFunction>");
071 }
072
073 @NotNull
074 private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function,
075 @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) {
076 JsObjectLiteral meta = createDataDescriptor(function, descriptor.getModality().isOverridable(), false);
077 return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta);
078 }
079
080 @NotNull
081 public static JsExpression translateExclForBinaryEqualLikeExpr(@NotNull JsBinaryOperation baseBinaryExpression) {
082 return new JsBinaryOperation(notOperator(baseBinaryExpression.getOperator()), baseBinaryExpression.getArg1(), baseBinaryExpression.getArg2());
083 }
084
085 public static boolean isEqualLikeOperator(@NotNull JsBinaryOperator operator) {
086 return notOperator(operator) != null;
087 }
088
089 @Nullable
090 private static JsBinaryOperator notOperator(@NotNull JsBinaryOperator operator) {
091 switch (operator) {
092 case REF_EQ:
093 return REF_NEQ;
094 case REF_NEQ:
095 return REF_EQ;
096 case EQ:
097 return NEQ;
098 case NEQ:
099 return EQ;
100 default:
101 return null;
102 }
103 }
104
105 @NotNull
106 public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) {
107 return nullCheck(expressionToCheck, false);
108 }
109
110 @NotNull
111 public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) {
112 return nullCheck(expressionToCheck, true);
113 }
114
115 @NotNull
116 public static JsBinaryOperation nullCheck(@NotNull JsExpression expressionToCheck, boolean isNegated) {
117 JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ;
118 return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL);
119 }
120
121 @NotNull
122 public static JsConditional notNullConditional(
123 @NotNull JsExpression expression,
124 @NotNull JsExpression elseExpression,
125 @NotNull TranslationContext context
126 ) {
127 JsExpression testExpression;
128 JsExpression thenExpression;
129 if (isCacheNeeded(expression)) {
130 TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(expression);
131 testExpression = isNotNullCheck(tempVar.value());
132 thenExpression = tempVar.value();
133 }
134 else {
135 testExpression = isNotNullCheck(expression);
136 thenExpression = expression;
137 }
138
139 return new JsConditional(testExpression, thenExpression, elseExpression);
140 }
141
142 @NotNull
143 public static String getMangledName(@NotNull PropertyDescriptor descriptor, @NotNull String suggestedName) {
144 return getStableMangledName(suggestedName, getFqName(descriptor).asString());
145 }
146
147 @NotNull
148 public static String getSuggestedName(@NotNull DeclarationDescriptor descriptor) {
149 String suggestedName = descriptor.getName().asString();
150
151 if (descriptor instanceof FunctionDescriptor) {
152 suggestedName = getMangledName((FunctionDescriptor) descriptor);
153 }
154
155 return suggestedName;
156 }
157
158 @NotNull
159 private static String getMangledName(@NotNull FunctionDescriptor descriptor) {
160 if (needsStableMangling(descriptor)) {
161 return getStableMangledName(descriptor);
162 }
163
164 return getSimpleMangledName(descriptor);
165 }
166
167 //TODO extend logic for nested/inner declarations
168 private static boolean needsStableMangling(FunctionDescriptor descriptor) {
169 // Use stable mangling for overrides because we use stable mangling when any function inside a overridable declaration
170 // for avoid clashing names when inheritance.
171 if (JsDescriptorUtils.isOverride(descriptor)) {
172 return true;
173 }
174
175 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
176
177 if (containingDeclaration instanceof PackageFragmentDescriptor) {
178 return descriptor.getVisibility().isPublicAPI();
179 }
180 else if (containingDeclaration instanceof ClassDescriptor) {
181 ClassDescriptor classDescriptor = (ClassDescriptor) containingDeclaration;
182
183 // Use stable mangling when it's inside an overridable declaration to avoid clashing names on inheritance.
184 if (classDescriptor.getModality().isOverridable()) {
185 return true;
186 }
187
188 // valueOf() is created in the library with a mangled name for every enum class
189 if (CodegenUtil.isEnumValueOfMethod(descriptor)) {
190 return true;
191 }
192
193 // Don't use stable mangling when it inside a non-public API declaration.
194 if (!classDescriptor.getVisibility().isPublicAPI()) {
195 return false;
196 }
197
198 // Ignore the `protected` visibility because it can be use outside a containing declaration
199 // only when the containing declaration is overridable.
200 if (descriptor.getVisibility() == Visibilities.PUBLIC) {
201 return true;
202 }
203
204 return false;
205 }
206
207 assert containingDeclaration instanceof CallableMemberDescriptor :
208 "containingDeclaration for descriptor have unsupported type for mangling, " +
209 "descriptor: " + descriptor + ", containingDeclaration: " + containingDeclaration;
210
211 return false;
212 }
213
214 @NotNull
215 public static String getMangledMemberNameForExplicitDelegation(@NotNull String suggestedName, FqName classFqName, FqName typeFqName) {
216 String forCalculateId = classFqName.asString() + ":" + typeFqName.asString();
217 return getStableMangledName(suggestedName, forCalculateId);
218 }
219
220 @NotNull
221 private static String getStableMangledName(@NotNull String suggestedName, String forCalculateId) {
222 int absHashCode = Math.abs(forCalculateId.hashCode());
223 String suffix = absHashCode == 0 ? "" : ("_" + Integer.toString(absHashCode, Character.MAX_RADIX) + "$");
224 return suggestedName + suffix;
225 }
226
227 @NotNull
228 private static String getStableMangledName(@NotNull FunctionDescriptor descriptor) {
229 return getStableMangledName(descriptor.getName().asString(), getArgumentTypesAsString(descriptor));
230 }
231
232 @NotNull
233 private static String getSimpleMangledName(@NotNull final FunctionDescriptor descriptor) {
234 DeclarationDescriptor declaration = descriptor.getContainingDeclaration();
235
236 JetScope jetScope = null;
237 if (declaration instanceof PackageFragmentDescriptor) {
238 jetScope = ((PackageFragmentDescriptor) declaration).getMemberScope();
239 }
240 else if (declaration instanceof ClassDescriptor) {
241 jetScope = ((ClassDescriptor) declaration).getDefaultType().getMemberScope();
242 }
243
244 int counter = 0;
245
246 if (jetScope != null) {
247 Collection<DeclarationDescriptor> declarations = jetScope.getAllDescriptors();
248 List<FunctionDescriptor> overloadedFunctions = ContainerUtil.mapNotNull(declarations, new Function<DeclarationDescriptor, FunctionDescriptor>() {
249 @Override
250 public FunctionDescriptor fun(DeclarationDescriptor declarationDescriptor) {
251 if (!(declarationDescriptor instanceof FunctionDescriptor)) return null;
252
253 FunctionDescriptor functionDescriptor = (FunctionDescriptor) declarationDescriptor;
254
255 String name = AnnotationsUtils.getNameForAnnotatedObjectWithOverrides(functionDescriptor);
256
257 // when name == null it's mean that it's not native.
258 if (name == null) {
259 // skip functions without arguments, because we don't use mangling for them
260 if (needsStableMangling(functionDescriptor) && !functionDescriptor.getValueParameters().isEmpty()) return null;
261
262 name = declarationDescriptor.getName().asString();
263 }
264
265 return descriptor.getName().asString().equals(name) ? functionDescriptor : null;
266 }
267 });
268
269 if (overloadedFunctions.size() > 1) {
270 Collections.sort(overloadedFunctions, OVERLOADED_FUNCTION_COMPARATOR);
271 counter = ContainerUtil.indexOfIdentity(overloadedFunctions, descriptor);
272 assert counter >= 0;
273 }
274 }
275
276 String name = descriptor.getName().asString();
277 return counter == 0 ? name : name + '_' + counter;
278 }
279
280 private static String getArgumentTypesAsString(FunctionDescriptor descriptor) {
281 StringBuilder argTypes = new StringBuilder();
282
283 ReceiverParameterDescriptor receiverParameter = descriptor.getReceiverParameter();
284 if (receiverParameter != null) {
285 argTypes.append(getJetTypeFqName(receiverParameter.getType())).append(".");
286 }
287
288 argTypes.append(StringUtil.join(descriptor.getValueParameters(), new Function<ValueParameterDescriptor, String>() {
289 @Override
290 public String fun(ValueParameterDescriptor descriptor) {
291 return getJetTypeFqName(descriptor.getType());
292 }
293 }, ","));
294
295 return argTypes.toString();
296 }
297
298 @NotNull
299 public static String getJetTypeFqName(@NotNull JetType jetType) {
300 ClassifierDescriptor declaration = jetType.getConstructor().getDeclarationDescriptor();
301 assert declaration != null;
302
303 if (declaration instanceof TypeParameterDescriptor) {
304 return getJetTypeFqName(((TypeParameterDescriptor) declaration).getUpperBoundsAsType());
305 }
306
307 return getFqName(declaration).asString();
308 }
309
310 @NotNull
311 public static JsNameRef backingFieldReference(@NotNull TranslationContext context,
312 @NotNull PropertyDescriptor descriptor) {
313 JsName backingFieldName = context.getNameForDescriptor(descriptor);
314 if(!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) {
315 String backingFieldMangledName;
316 if (descriptor.getVisibility() != Visibilities.PRIVATE) {
317 backingFieldMangledName = getMangledName(descriptor, getKotlinBackingFieldName(backingFieldName.getIdent()));
318 } else {
319 backingFieldMangledName = getKotlinBackingFieldName(backingFieldName.getIdent());
320 }
321 backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName, false);
322 }
323 return new JsNameRef(backingFieldName, JsLiteral.THIS);
324 }
325
326 @NotNull
327 public static JsExpression assignmentToBackingField(@NotNull TranslationContext context,
328 @NotNull PropertyDescriptor descriptor,
329 @NotNull JsExpression assignTo) {
330 JsNameRef backingFieldReference = backingFieldReference(context, descriptor);
331 return assignment(backingFieldReference, assignTo);
332 }
333
334 @Nullable
335 public static JsExpression translateInitializerForProperty(@NotNull JetProperty declaration,
336 @NotNull TranslationContext context) {
337 JsExpression jsInitExpression = null;
338 JetExpression initializer = declaration.getInitializer();
339 if (initializer != null) {
340 jsInitExpression = Translation.translateAsExpression(initializer, context);
341 }
342 return jsInitExpression;
343 }
344
345 @NotNull
346 public static JsExpression translateBaseExpression(@NotNull TranslationContext context,
347 @NotNull JetUnaryExpression expression) {
348 JetExpression baseExpression = PsiUtils.getBaseExpression(expression);
349 return Translation.translateAsExpression(baseExpression, context);
350 }
351
352 @NotNull
353 public static JsExpression translateLeftExpression(@NotNull TranslationContext context,
354 @NotNull JetBinaryExpression expression) {
355 return translateLeftExpression(context, expression, context.dynamicContext().jsBlock());
356 }
357
358 @NotNull
359 public static JsExpression translateLeftExpression(
360 @NotNull TranslationContext context,
361 @NotNull JetBinaryExpression expression,
362 @NotNull JsBlock block
363 ) {
364 JetExpression left = expression.getLeft();
365 assert left != null : "Binary expression should have a left expression: " + expression.getText();
366 return Translation.translateAsExpression(left, context, block);
367 }
368
369 @NotNull
370 public static JsExpression translateRightExpression(@NotNull TranslationContext context,
371 @NotNull JetBinaryExpression expression) {
372 return translateRightExpression(context, expression, context.dynamicContext().jsBlock());
373 }
374
375 @NotNull
376 public static JsExpression translateRightExpression(
377 @NotNull TranslationContext context,
378 @NotNull JetBinaryExpression expression,
379 @NotNull JsBlock block) {
380 JetExpression rightExpression = expression.getRight();
381 assert rightExpression != null : "Binary expression should have a right expression";
382 return Translation.translateAsExpression(rightExpression, context, block);
383 }
384
385 public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context,
386 @NotNull JetOperationExpression expression) {
387 CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression);
388
389 if (operationDescriptor == null || !(operationDescriptor instanceof FunctionDescriptor)) return true;
390
391 JetType returnType = operationDescriptor.getReturnType();
392 if (returnType != null &&
393 KotlinBuiltIns.getInstance().getCharType().equals(returnType) || (KotlinBuiltIns.getInstance().getLongType().equals(returnType))) return false;
394
395 if (context.intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) operationDescriptor).exists()) return true;
396
397 return false;
398 }
399
400 @NotNull
401 public static List<JsExpression> generateInvocationArguments(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments) {
402 if (arguments.isEmpty()) {
403 return Collections.singletonList(receiver);
404 }
405
406 List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size());
407 argumentList.add(receiver);
408 argumentList.addAll(arguments);
409 return argumentList;
410 }
411
412 public static boolean isCacheNeeded(@NotNull JsExpression expression) {
413 return !(expression instanceof JsLiteral.JsValueLiteral) &&
414 !JsAstUtils.isEmptyExpression(expression) &&
415 (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null);
416 }
417
418 @NotNull
419 public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) {
420 JsInvocation throwNPE = new JsInvocation(context.namer().throwNPEFunctionRef());
421 JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context);
422
423 JsExpression thenExpression = ensureNotNull.getThenExpression();
424 if (thenExpression instanceof JsNameRef) {
425 // associate (cache) ensureNotNull expression to new TemporaryConstVariable with same name.
426 context.associateExpressionToLazyValue(ensureNotNull,
427 new TemporaryConstVariable(((JsNameRef) thenExpression).getName(), ensureNotNull));
428 }
429
430 return ensureNotNull;
431 }
432
433 @NotNull
434 public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
435 Collection<FunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getFunctions(Name.identifier(functionName));
436 assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
437 return getSuggestedName(functions.iterator().next());
438 }
439
440 @NotNull
441 public static String getSuggestedNameForInnerDeclaration(TranslationContext context, DeclarationDescriptor descriptor) {
442 String suggestedName = "";
443 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration();
444 //noinspection ConstantConditions
445 if (containingDeclaration != null &&
446 !(containingDeclaration instanceof ClassOrPackageFragmentDescriptor) &&
447 !(containingDeclaration instanceof AnonymousFunctionDescriptor) &&
448 !(containingDeclaration instanceof ConstructorDescriptor && isAnonymousObject(containingDeclaration.getContainingDeclaration()))) {
449 suggestedName = context.getNameForDescriptor(containingDeclaration).getIdent();
450 }
451
452 if (!suggestedName.isEmpty() && !suggestedName.endsWith("$")) {
453 suggestedName += "$";
454 }
455
456 if (descriptor.getName().isSpecial()) {
457 suggestedName += "f";
458 }
459 else {
460 suggestedName += context.getNameForDescriptor(descriptor).getIdent();
461 }
462 return suggestedName;
463 }
464
465 private static class OverloadedFunctionComparator implements Comparator<FunctionDescriptor> {
466 @Override
467 public int compare(@NotNull FunctionDescriptor a, @NotNull FunctionDescriptor b) {
468 // native functions first
469 if (isNativeOrOverrideNative(a)) {
470 if (!isNativeOrOverrideNative(b)) return -1;
471 }
472 else if (isNativeOrOverrideNative(b)) {
473 return 1;
474 }
475
476 // be visibility
477 // Actually "internal" > "private", but we want to have less number for "internal", so compare b with a instead of a with b.
478 Integer result = Visibilities.compare(b.getVisibility(), a.getVisibility());
479 if (result != null && result != 0) return result;
480
481 // by arity
482 int aArity = arity(a);
483 int bArity = arity(b);
484 if (aArity != bArity) return aArity - bArity;
485
486 // by stringify argument types
487 String aArguments = getArgumentTypesAsString(a);
488 String bArguments = getArgumentTypesAsString(b);
489 assert aArguments != bArguments;
490
491 return aArguments.compareTo(bArguments);
492 }
493
494 private static int arity(FunctionDescriptor descriptor) {
495 return descriptor.getValueParameters().size() + (descriptor.getReceiverParameter() == null ? 0 : 1);
496 }
497
498 private static boolean isNativeOrOverrideNative(FunctionDescriptor descriptor) {
499 if (AnnotationsUtils.isNativeObject(descriptor)) return true;
500
501 Set<FunctionDescriptor> declarations = OverrideResolver.getAllOverriddenDeclarations(descriptor);
502 for (FunctionDescriptor memberDescriptor : declarations) {
503 if (AnnotationsUtils.isNativeObject(memberDescriptor)) return true;
504 }
505 return false;
506 }
507 }
508 }