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