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.JetType; 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, descriptor.getModality().isOverridable(), 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 return new JsNameRef(backingFieldName, JsLiteral.THIS); 150 } 151 152 @NotNull 153 public static JsExpression assignmentToBackingField(@NotNull TranslationContext context, 154 @NotNull PropertyDescriptor descriptor, 155 @NotNull JsExpression assignTo) { 156 JsNameRef backingFieldReference = backingFieldReference(context, descriptor); 157 return assignment(backingFieldReference, assignTo); 158 } 159 160 @Nullable 161 public static JsExpression translateInitializerForProperty(@NotNull JetProperty declaration, 162 @NotNull TranslationContext context) { 163 JsExpression jsInitExpression = null; 164 JetExpression initializer = declaration.getInitializer(); 165 if (initializer != null) { 166 jsInitExpression = Translation.translateAsExpression(initializer, context); 167 } 168 return jsInitExpression; 169 } 170 171 @NotNull 172 public static JsExpression translateBaseExpression(@NotNull TranslationContext context, 173 @NotNull JetUnaryExpression expression) { 174 JetExpression baseExpression = PsiUtils.getBaseExpression(expression); 175 return Translation.translateAsExpression(baseExpression, context); 176 } 177 178 @NotNull 179 public static JsExpression translateLeftExpression(@NotNull TranslationContext context, 180 @NotNull JetBinaryExpression expression) { 181 return translateLeftExpression(context, expression, context.dynamicContext().jsBlock()); 182 } 183 184 @NotNull 185 public static JsExpression translateLeftExpression( 186 @NotNull TranslationContext context, 187 @NotNull JetBinaryExpression expression, 188 @NotNull JsBlock block 189 ) { 190 JetExpression left = expression.getLeft(); 191 assert left != null : "Binary expression should have a left expression: " + expression.getText(); 192 return Translation.translateAsExpression(left, context, block); 193 } 194 195 @NotNull 196 public static JsExpression translateRightExpression(@NotNull TranslationContext context, 197 @NotNull JetBinaryExpression expression) { 198 return translateRightExpression(context, expression, context.dynamicContext().jsBlock()); 199 } 200 201 @NotNull 202 public static JsExpression translateRightExpression( 203 @NotNull TranslationContext context, 204 @NotNull JetBinaryExpression expression, 205 @NotNull JsBlock block) { 206 JetExpression rightExpression = expression.getRight(); 207 assert rightExpression != null : "Binary expression should have a right expression"; 208 return Translation.translateAsExpression(rightExpression, context, block); 209 } 210 211 public static boolean hasCorrespondingFunctionIntrinsic(@NotNull TranslationContext context, 212 @NotNull JetOperationExpression expression) { 213 CallableDescriptor operationDescriptor = getCallableDescriptorForOperationExpression(context.bindingContext(), expression); 214 215 if (operationDescriptor == null || !(operationDescriptor instanceof FunctionDescriptor)) return true; 216 217 JetType returnType = operationDescriptor.getReturnType(); 218 if (returnType != null && 219 KotlinBuiltIns.getInstance().getCharType().equals(returnType) || (KotlinBuiltIns.getInstance().getLongType().equals(returnType))) return false; 220 221 if (context.intrinsics().getFunctionIntrinsic((FunctionDescriptor) operationDescriptor).exists()) return true; 222 223 return false; 224 } 225 226 @NotNull 227 public static List<JsExpression> generateInvocationArguments(@NotNull JsExpression receiver, @NotNull List<JsExpression> arguments) { 228 if (arguments.isEmpty()) { 229 return Collections.singletonList(receiver); 230 } 231 232 List<JsExpression> argumentList = new ArrayList<JsExpression>(1 + arguments.size()); 233 argumentList.add(receiver); 234 argumentList.addAll(arguments); 235 return argumentList; 236 } 237 238 public static boolean isCacheNeeded(@NotNull JsExpression expression) { 239 return !(expression instanceof JsLiteral.JsValueLiteral) && 240 !JsAstUtils.isEmptyExpression(expression) && 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(context.namer().throwNPEFunctionRef()); 247 JsConditional ensureNotNull = notNullConditional(expression, throwNPE, context); 248 249 JsExpression thenExpression = ensureNotNull.getThenExpression(); 250 if (thenExpression instanceof JsNameRef) { 251 // associate (cache) ensureNotNull expression to new TemporaryConstVariable with same name. 252 context.associateExpressionToLazyValue(ensureNotNull, 253 new TemporaryConstVariable(((JsNameRef) thenExpression).getName(), ensureNotNull)); 254 } 255 256 return ensureNotNull; 257 } 258 259 @NotNull 260 public static String getSuggestedNameForInnerDeclaration(TranslationContext context, DeclarationDescriptor descriptor) { 261 String suggestedName = ""; 262 DeclarationDescriptor containingDeclaration = descriptor.getContainingDeclaration(); 263 //noinspection ConstantConditions 264 if (containingDeclaration != null && 265 !(containingDeclaration instanceof ClassOrPackageFragmentDescriptor) && 266 !(containingDeclaration instanceof AnonymousFunctionDescriptor) && 267 !(containingDeclaration instanceof ConstructorDescriptor && isAnonymousObject(containingDeclaration.getContainingDeclaration()))) { 268 suggestedName = context.getNameForDescriptor(containingDeclaration).getIdent(); 269 } 270 271 if (!suggestedName.isEmpty() && !suggestedName.endsWith("$")) { 272 suggestedName += "$"; 273 } 274 275 if (descriptor.getName().isSpecial()) { 276 suggestedName += "f"; 277 } 278 else { 279 suggestedName += context.getNameForDescriptor(descriptor).getIdent(); 280 } 281 return suggestedName; 282 } 283 }