001 /* 002 * Copyright 2010-2015 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.kotlin.js.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 org.jetbrains.annotations.NotNull; 023 import org.jetbrains.annotations.Nullable; 024 import org.jetbrains.kotlin.builtins.KotlinBuiltIns; 025 import org.jetbrains.kotlin.descriptors.*; 026 import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor; 027 import org.jetbrains.kotlin.js.translate.context.TemporaryConstVariable; 028 import org.jetbrains.kotlin.js.translate.context.TranslationContext; 029 import org.jetbrains.kotlin.js.translate.general.Translation; 030 import org.jetbrains.kotlin.psi.*; 031 import org.jetbrains.kotlin.resolve.DescriptorUtils; 032 import org.jetbrains.kotlin.types.JetType; 033 import org.jetbrains.kotlin.types.TypeProjection; 034 035 import java.util.ArrayList; 036 import java.util.Collections; 037 import java.util.List; 038 039 import static com.google.dart.compiler.backend.js.ast.JsBinaryOperator.*; 040 import static org.jetbrains.kotlin.js.translate.context.Namer.getKotlinBackingFieldName; 041 import static org.jetbrains.kotlin.js.translate.utils.BindingUtils.getCallableDescriptorForOperationExpression; 042 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.assignment; 043 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.createDataDescriptor; 044 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getMangledName; 045 import static org.jetbrains.kotlin.resolve.DescriptorUtils.getFqName; 046 import static org.jetbrains.kotlin.resolve.DescriptorUtils.isAnonymousObject; 047 048 public final class TranslationUtils { 049 050 private TranslationUtils() { 051 } 052 053 @NotNull 054 public static JsPropertyInitializer translateFunctionAsEcma5PropertyDescriptor(@NotNull JsFunction function, 055 @NotNull FunctionDescriptor descriptor, 056 @NotNull TranslationContext context) { 057 if (DescriptorUtils.isExtension(descriptor)) { 058 return translateExtensionFunctionAsEcma5DataDescriptor(function, descriptor, context); 059 } 060 else { 061 JsStringLiteral getOrSet = context.program().getStringLiteral(descriptor instanceof PropertyGetterDescriptor ? "get" : "set"); 062 return new JsPropertyInitializer(getOrSet, function); 063 } 064 } 065 066 @NotNull 067 public static JsFunction simpleReturnFunction(@NotNull JsScope functionScope, @NotNull JsExpression returnExpression) { 068 return new JsFunction(functionScope, new JsBlock(new JsReturn(returnExpression)), "<simpleReturnFunction>"); 069 } 070 071 @NotNull 072 private static JsPropertyInitializer translateExtensionFunctionAsEcma5DataDescriptor(@NotNull JsFunction function, 073 @NotNull FunctionDescriptor descriptor, @NotNull TranslationContext context) { 074 JsObjectLiteral meta = createDataDescriptor(function, descriptor.getModality().isOverridable(), false); 075 return new JsPropertyInitializer(context.getNameForDescriptor(descriptor).makeRef(), meta); 076 } 077 078 @NotNull 079 public static JsExpression translateExclForBinaryEqualLikeExpr(@NotNull JsBinaryOperation baseBinaryExpression) { 080 return new JsBinaryOperation(notOperator(baseBinaryExpression.getOperator()), baseBinaryExpression.getArg1(), baseBinaryExpression.getArg2()); 081 } 082 083 public static boolean isEqualLikeOperator(@NotNull JsBinaryOperator operator) { 084 return notOperator(operator) != null; 085 } 086 087 @Nullable 088 private static JsBinaryOperator notOperator(@NotNull JsBinaryOperator operator) { 089 switch (operator) { 090 case REF_EQ: 091 return REF_NEQ; 092 case REF_NEQ: 093 return REF_EQ; 094 case EQ: 095 return NEQ; 096 case NEQ: 097 return EQ; 098 default: 099 return null; 100 } 101 } 102 103 @NotNull 104 public static JsBinaryOperation isNullCheck(@NotNull JsExpression expressionToCheck) { 105 return nullCheck(expressionToCheck, false); 106 } 107 108 @NotNull 109 public static JsBinaryOperation isNotNullCheck(@NotNull JsExpression expressionToCheck) { 110 return nullCheck(expressionToCheck, true); 111 } 112 113 @NotNull 114 public static JsBinaryOperation nullCheck(@NotNull JsExpression expressionToCheck, boolean isNegated) { 115 JsBinaryOperator operator = isNegated ? JsBinaryOperator.NEQ : JsBinaryOperator.EQ; 116 return new JsBinaryOperation(operator, expressionToCheck, JsLiteral.NULL); 117 } 118 119 @NotNull 120 public static JsConditional notNullConditional( 121 @NotNull JsExpression expression, 122 @NotNull JsExpression elseExpression, 123 @NotNull TranslationContext context 124 ) { 125 JsExpression testExpression; 126 JsExpression thenExpression; 127 if (isCacheNeeded(expression)) { 128 TemporaryConstVariable tempVar = context.getOrDeclareTemporaryConstVariable(expression); 129 testExpression = isNotNullCheck(tempVar.value()); 130 thenExpression = tempVar.value(); 131 } 132 else { 133 testExpression = isNotNullCheck(expression); 134 thenExpression = expression; 135 } 136 137 return new JsConditional(testExpression, thenExpression, elseExpression); 138 } 139 140 @NotNull 141 public static String getJetTypeFqName(@NotNull JetType jetType, final boolean printTypeArguments) { 142 ClassifierDescriptor declaration = jetType.getConstructor().getDeclarationDescriptor(); 143 assert declaration != null; 144 145 if (declaration instanceof TypeParameterDescriptor) { 146 return StringUtil.join(((TypeParameterDescriptor) declaration).getUpperBounds(), new Function<JetType, String>() { 147 @Override 148 public String fun(JetType type) { 149 return getJetTypeFqName(type, printTypeArguments); 150 } 151 }, "&"); 152 } 153 154 List<TypeProjection> typeArguments = jetType.getArguments(); 155 156 String typeArgumentsAsString; 157 158 if (printTypeArguments && !typeArguments.isEmpty()) { 159 String joinedTypeArguments = StringUtil.join(typeArguments, new Function<TypeProjection, String>() { 160 @Override 161 public String fun(TypeProjection typeProjection) { 162 return getJetTypeFqName(typeProjection.getType(), false); 163 } 164 }, ", "); 165 166 typeArgumentsAsString = "<" + joinedTypeArguments + ">"; 167 } 168 else { 169 typeArgumentsAsString = ""; 170 } 171 172 return getFqName(declaration).asString() + typeArgumentsAsString; 173 } 174 175 @NotNull 176 public static JsNameRef backingFieldReference(@NotNull TranslationContext context, 177 @NotNull PropertyDescriptor descriptor) { 178 JsName backingFieldName = context.getNameForDescriptor(descriptor); 179 if(!JsDescriptorUtils.isSimpleFinalProperty(descriptor)) { 180 String backingFieldMangledName; 181 if (!Visibilities.isPrivate(descriptor.getVisibility())) { 182 backingFieldMangledName = getMangledName(descriptor, getKotlinBackingFieldName(backingFieldName.getIdent())); 183 } else { 184 backingFieldMangledName = getKotlinBackingFieldName(backingFieldName.getIdent()); 185 } 186 backingFieldName = context.declarePropertyOrPropertyAccessorName(descriptor, backingFieldMangledName, false); 187 } 188 return new JsNameRef(backingFieldName, JsLiteral.THIS); 189 } 190 191 @NotNull 192 public static JsExpression assignmentToBackingField(@NotNull TranslationContext context, 193 @NotNull PropertyDescriptor descriptor, 194 @NotNull JsExpression assignTo) { 195 JsNameRef backingFieldReference = backingFieldReference(context, descriptor); 196 return assignment(backingFieldReference, assignTo); 197 } 198 199 @Nullable 200 public static JsExpression translateInitializerForProperty(@NotNull JetProperty declaration, 201 @NotNull TranslationContext context) { 202 JsExpression jsInitExpression = null; 203 JetExpression initializer = declaration.getInitializer(); 204 if (initializer != null) { 205 jsInitExpression = Translation.translateAsExpression(initializer, context); 206 } 207 return jsInitExpression; 208 } 209 210 @NotNull 211 public static JsExpression translateBaseExpression(@NotNull TranslationContext context, 212 @NotNull JetUnaryExpression expression) { 213 JetExpression baseExpression = PsiUtils.getBaseExpression(expression); 214 return Translation.translateAsExpression(baseExpression, context); 215 } 216 217 @NotNull 218 public static JsExpression translateLeftExpression(@NotNull TranslationContext context, 219 @NotNull JetBinaryExpression expression) { 220 return translateLeftExpression(context, expression, context.dynamicContext().jsBlock()); 221 } 222 223 @NotNull 224 public static JsExpression translateLeftExpression( 225 @NotNull TranslationContext context, 226 @NotNull JetBinaryExpression expression, 227 @NotNull JsBlock block 228 ) { 229 JetExpression left = expression.getLeft(); 230 assert left != null : "Binary expression should have a left expression: " + expression.getText(); 231 return Translation.translateAsExpression(left, context, block); 232 } 233 234 @NotNull 235 public static JsExpression translateRightExpression(@NotNull TranslationContext context, 236 @NotNull JetBinaryExpression expression) { 237 return translateRightExpression(context, expression, context.dynamicContext().jsBlock()); 238 } 239 240 @NotNull 241 public static JsExpression translateRightExpression( 242 @NotNull TranslationContext context, 243 @NotNull JetBinaryExpression expression, 244 @NotNull JsBlock block) { 245 JetExpression rightExpression = expression.getRight(); 246 assert rightExpression != null : "Binary expression should have a right expression"; 247 return Translation.translateAsExpression(rightExpression, context, block); 248 } 249 250 public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context, 251 @NotNull JetOperationExpression expression) { 252 CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression); 253 254 if (operationDescriptor == null || !(operationDescriptor instanceof FunctionDescriptor)) return true; 255 256 JetType returnType = operationDescriptor.getReturnType(); 257 if (returnType != null && 258 KotlinBuiltIns.getInstance().getCharType().equals(returnType) || (KotlinBuiltIns.getInstance().getLongType().equals(returnType))) return false; 259 260 if (context.intrinsics().getFunctionIntrinsics().getIntrinsic((FunctionDescriptor) operationDescriptor).exists()) return true; 261 262 return false; 263 } 264 265 @NotNull 266 public static List<JsExpression> generateInvocationArguments(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments) { 267 if (arguments.isEmpty()) { 268 return Collections.singletonList(receiver); 269 } 270 271 List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size()); 272 argumentList.add(receiver); 273 argumentList.addAll(arguments); 274 return argumentList; 275 } 276 277 public static boolean isCacheNeeded(@NotNull JsExpression expression) { 278 return !(expression instanceof JsLiteral.JsValueLiteral) && 279 !JsAstUtils.isEmptyExpression(expression) && 280 (!(expression instanceof JsNameRef) || ((JsNameRef) expression).getQualifier() != null); 281 } 282 283 @NotNull 284 public static JsConditional sure(@NotNull JsExpression expression, @NotNull TranslationContext context) { 285 JsInvocation throwNPE = new JsInvocation(context.namer().throwNPEFunctionRef()); 286 JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context); 287 288 JsExpression thenExpression = ensureNotNull.getThenExpression(); 289 if (thenExpression instanceof JsNameRef) { 290 // associate (cache) ensureNotNull expression to new TemporaryConstVariable with same name. 291 context.associateExpressionToLazyValue(ensureNotNull, 292 new TemporaryConstVariable(((JsNameRef) thenExpression).getName(), ensureNotNull)); 293 } 294 295 return ensureNotNull; 296 } 297 298 @NotNull 299 public static String getSuggestedNameForInnerDeclaration(TranslationContext context, DeclarationDescriptor descriptor) { 300 String suggestedName = ""; 301 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 302 //noinspection ConstantConditions 303 if (containingDeclaration != null && 304 !(containingDeclaration instanceof ClassOrPackageFragmentDescriptor) && 305 !(containingDeclaration instanceof AnonymousFunctionDescriptor) && 306 !(containingDeclaration instanceof ConstructorDescriptor && isAnonymousObject(containingDeclaration.getContainingDeclaration()))) { 307 suggestedName = context.getNameForDescriptor(containingDeclaration).getIdent(); 308 } 309 310 if (!suggestedName.isEmpty() && !suggestedName.endsWith("$")) { 311 suggestedName += "$"; 312 } 313 314 if (descriptor.getName().isSpecial()) { 315 suggestedName += "f"; 316 } 317 else { 318 suggestedName += context.getNameForDescriptor(descriptor).getIdent(); 319 } 320 return suggestedName; 321 } 322 }