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