001 /* 002 * Copyright 2010-2016 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.context; 018 019 import com.google.dart.compiler.backend.js.ast.*; 020 import com.google.dart.compiler.backend.js.ast.metadata.MetadataProperties; 021 import com.google.dart.compiler.backend.js.ast.metadata.SideEffectKind; 022 import com.google.dart.compiler.backend.js.ast.metadata.TypeCheck; 023 import com.intellij.openapi.util.text.StringUtil; 024 import org.jetbrains.annotations.NotNull; 025 import org.jetbrains.annotations.Nullable; 026 import org.jetbrains.kotlin.descriptors.CallableDescriptor; 027 import org.jetbrains.kotlin.descriptors.ClassDescriptor; 028 import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; 029 import org.jetbrains.kotlin.idea.KotlinLanguage; 030 import org.jetbrains.kotlin.js.resolve.JsPlatform; 031 import org.jetbrains.kotlin.name.FqName; 032 import org.jetbrains.kotlin.name.FqNameUnsafe; 033 import org.jetbrains.kotlin.resolve.DescriptorUtils; 034 035 import java.util.Arrays; 036 import java.util.Collections; 037 import java.util.List; 038 039 import static com.google.dart.compiler.backend.js.ast.JsScopesKt.JsObjectScope; 040 import static org.jetbrains.kotlin.js.translate.utils.JsAstUtils.pureFqn; 041 import static org.jetbrains.kotlin.js.translate.utils.JsDescriptorUtils.getModuleName; 042 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getStableMangledNameForDescriptor; 043 import static org.jetbrains.kotlin.js.translate.utils.ManglingUtils.getSuggestedName; 044 045 /** 046 * Encapsulates different types of constants and naming conventions. 047 */ 048 public final class Namer { 049 public static final String KOTLIN_NAME = KotlinLanguage.NAME; 050 public static final String KOTLIN_LOWER_NAME = KOTLIN_NAME.toLowerCase(); 051 052 public static final String EQUALS_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getAny(), "equals"); 053 public static final String COMPARE_TO_METHOD_NAME = getStableMangledNameForDescriptor(JsPlatform.INSTANCE.getBuiltIns().getComparable(), "compareTo"); 054 public static final String NUMBER_RANGE = "NumberRange"; 055 public static final String CHAR_RANGE = "CharRange"; 056 public static final String LONG_FROM_NUMBER = "fromNumber"; 057 public static final String LONG_TO_NUMBER = "toNumber"; 058 public static final String LONG_FROM_INT = "fromInt"; 059 public static final String LONG_ZERO = "ZERO"; 060 public static final String LONG_ONE = "ONE"; 061 public static final String LONG_NEG_ONE = "NEG_ONE"; 062 public static final String PRIMITIVE_COMPARE_TO = "primitiveCompareTo"; 063 public static final String IS_CHAR = "isChar"; 064 public static final String IS_NUMBER = "isNumber"; 065 public static final String IS_CHAR_SEQUENCE = "isCharSequence"; 066 067 public static final String CALLEE_NAME = "$fun"; 068 069 public static final String CALL_FUNCTION = "call"; 070 private static final String APPLY_FUNCTION = "apply"; 071 072 public static final String OUTER_FIELD_NAME = "$outer"; 073 074 private static final String CLASS_OBJECT_NAME = "createClass"; 075 private static final String ENUM_CLASS_OBJECT_NAME = "createEnumClass"; 076 private static final String TRAIT_OBJECT_NAME = "createTrait"; 077 private static final String OBJECT_OBJECT_NAME = "createObject"; 078 private static final String CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME = "getCallableRefForMemberFunction"; 079 private static final String CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME = "getCallableRefForExtensionFunction"; 080 private static final String CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME = "getCallableRefForLocalExtensionFunction"; 081 private static final String CALLABLE_REF_FOR_CONSTRUCTOR_NAME = "getCallableRefForConstructor"; 082 private static final String CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY = "getCallableRefForTopLevelProperty"; 083 private static final String CALLABLE_REF_FOR_MEMBER_PROPERTY = "getCallableRefForMemberProperty"; 084 private static final String CALLABLE_REF_FOR_EXTENSION_PROPERTY = "getCallableRefForExtensionProperty"; 085 086 private static final String SETTER_PREFIX = "set_"; 087 private static final String GETTER_PREFIX = "get_"; 088 private static final String BACKING_FIELD_PREFIX = "$"; 089 private static final String DELEGATE = "$delegate"; 090 091 private static final String SUPER_METHOD_NAME = "baseInitializer"; 092 093 private static final String ROOT_PACKAGE = "_"; 094 095 private static final String RECEIVER_PARAMETER_NAME = "$receiver"; 096 public static final String ANOTHER_THIS_PARAMETER_NAME = "$this"; 097 098 private static final String THROW_NPE_FUN_NAME = "throwNPE"; 099 private static final String THROW_CLASS_CAST_EXCEPTION_FUN_NAME = "throwCCE"; 100 private static final String PROTOTYPE_NAME = "prototype"; 101 private static final String CAPTURED_VAR_FIELD = "v"; 102 103 public static final JsNameRef IS_ARRAY_FUN_REF = new JsNameRef("isArray", "Array"); 104 public static final String DEFINE_INLINE_FUNCTION = "defineInlineFunction"; 105 106 private static final JsNameRef JS_OBJECT = new JsNameRef("Object"); 107 private static final JsNameRef JS_OBJECT_CREATE_FUNCTION = new JsNameRef("create", JS_OBJECT); 108 109 public static final String LOCAL_MODULE_PREFIX = "$module$"; 110 111 public static boolean isUndefined(@NotNull JsExpression expr) { 112 if (expr instanceof JsPrefixOperation) { 113 JsUnaryOperator op = ((JsPrefixOperation) expr).getOperator(); 114 115 return op == JsUnaryOperator.VOID; 116 } 117 118 return false; 119 } 120 121 @NotNull 122 public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor) { 123 String moduleName = getModuleName(functionDescriptor); 124 FqNameUnsafe fqNameParent = DescriptorUtils.getFqName(functionDescriptor).parent(); 125 String qualifier = null; 126 127 if (!fqNameParent.isRoot()) { 128 qualifier = fqNameParent.asString(); 129 } 130 131 String mangledName = getSuggestedName(functionDescriptor); 132 return StringUtil.join(Arrays.asList(moduleName, qualifier, mangledName), "."); 133 } 134 135 @NotNull 136 public static String getReceiverParameterName() { 137 return RECEIVER_PARAMETER_NAME; 138 } 139 140 @NotNull 141 public static String getRootPackageName() { 142 return ROOT_PACKAGE; 143 } 144 145 @NotNull 146 public static JsNameRef superMethodNameRef(@NotNull JsName superClassJsName) { 147 return pureFqn(SUPER_METHOD_NAME, superClassJsName.makeRef()); 148 } 149 150 @NotNull 151 public static String getNameForAccessor(@NotNull String propertyName, boolean isGetter, boolean useNativeAccessor) { 152 if (useNativeAccessor) { 153 return propertyName; 154 } 155 156 if (isGetter) { 157 return getNameForGetter(propertyName); 158 } 159 else { 160 return getNameForSetter(propertyName); 161 } 162 } 163 164 @NotNull 165 public static String getKotlinBackingFieldName(@NotNull String propertyName) { 166 return getNameWithPrefix(propertyName, BACKING_FIELD_PREFIX); 167 } 168 169 @NotNull 170 private static String getNameForGetter(@NotNull String propertyName) { 171 return getNameWithPrefix(propertyName, GETTER_PREFIX); 172 } 173 174 @NotNull 175 private static String getNameForSetter(@NotNull String propertyName) { 176 return getNameWithPrefix(propertyName, SETTER_PREFIX); 177 } 178 179 @NotNull 180 public static String getPrototypeName() { 181 return PROTOTYPE_NAME; 182 } 183 184 @NotNull 185 public static JsNameRef getRefToPrototype(@NotNull JsExpression classOrTraitExpression) { 186 return pureFqn(getPrototypeName(), classOrTraitExpression); 187 } 188 189 @NotNull 190 public static String getDelegatePrefix() { 191 return DELEGATE; 192 } 193 194 @NotNull 195 public static String getDelegateName(@NotNull String propertyName) { 196 return propertyName + DELEGATE; 197 } 198 199 @NotNull 200 public static JsNameRef getDelegateNameRef(String propertyName) { 201 return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS); 202 } 203 204 @NotNull 205 private static String getNameWithPrefix(@NotNull String name, @NotNull String prefix) { 206 return prefix + name; 207 } 208 209 @NotNull 210 public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) { 211 return pureFqn(CALL_FUNCTION, functionExpression); 212 } 213 214 @NotNull 215 public static JsNameRef getFunctionApplyRef(@NotNull JsExpression functionExpression) { 216 return pureFqn(APPLY_FUNCTION, functionExpression); 217 } 218 219 @NotNull 220 public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) { 221 return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, getRefToPrototype(referenceToClass)); 222 } 223 224 @NotNull 225 public static JsNameRef getCapturedVarAccessor(@NotNull JsExpression ref) { 226 return pureFqn(CAPTURED_VAR_FIELD, ref); 227 } 228 229 @NotNull 230 public static String isInstanceSuggestedName(@NotNull TypeParameterDescriptor descriptor) { 231 return "is" + descriptor.getName().getIdentifier(); 232 } 233 234 @NotNull 235 public static Namer newInstance(@NotNull JsScope rootScope) { 236 return new Namer(rootScope); 237 } 238 239 @NotNull 240 private final JsObjectScope kotlinScope; 241 @NotNull 242 private final JsName className; 243 @NotNull 244 private final JsName enumClassName; 245 @NotNull 246 private final JsName traitName; 247 @NotNull 248 private final JsExpression definePackage; 249 @NotNull 250 private final JsExpression defineRootPackage; 251 @NotNull 252 private final JsName objectName; 253 @NotNull 254 private final JsName callableRefForMemberFunctionName; 255 @NotNull 256 private final JsName callableRefForExtensionFunctionName; 257 @NotNull 258 private final JsName callableRefForLocalExtensionFunctionName; 259 @NotNull 260 private final JsName callableRefForConstructorName; 261 @NotNull 262 private final JsName callableRefForTopLevelProperty; 263 @NotNull 264 private final JsName callableRefForMemberProperty; 265 @NotNull 266 private final JsName callableRefForExtensionProperty; 267 @NotNull 268 private final JsExpression callGetProperty; 269 @NotNull 270 private final JsExpression callSetProperty; 271 272 @NotNull 273 private final JsName isTypeName; 274 275 @NotNull 276 private final JsExpression modulesMap; 277 278 private Namer(@NotNull JsScope rootScope) { 279 kotlinScope = JsObjectScope(rootScope, "Kotlin standard object"); 280 traitName = kotlinScope.declareName(TRAIT_OBJECT_NAME); 281 282 definePackage = kotlin("definePackage"); 283 defineRootPackage = kotlin("defineRootPackage"); 284 285 callGetProperty = kotlin("callGetter"); 286 callSetProperty = kotlin("callSetter"); 287 288 className = kotlinScope.declareName(CLASS_OBJECT_NAME); 289 enumClassName = kotlinScope.declareName(ENUM_CLASS_OBJECT_NAME); 290 objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME); 291 callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME); 292 callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME); 293 callableRefForLocalExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME); 294 callableRefForConstructorName = kotlinScope.declareName(CALLABLE_REF_FOR_CONSTRUCTOR_NAME); 295 callableRefForTopLevelProperty = kotlinScope.declareName(CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY); 296 callableRefForMemberProperty = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_PROPERTY); 297 callableRefForExtensionProperty = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_PROPERTY); 298 299 isTypeName = kotlinScope.declareName("isType"); 300 modulesMap = kotlin("modules"); 301 } 302 303 @NotNull 304 public JsExpression classCreationMethodReference() { 305 return kotlin(className); 306 } 307 308 @NotNull 309 public JsExpression enumClassCreationMethodReference() { 310 return kotlin(enumClassName); 311 } 312 313 @NotNull 314 public JsExpression traitCreationMethodReference() { 315 return kotlin(traitName); 316 } 317 318 @NotNull 319 public JsExpression packageDefinitionMethodReference() { 320 return definePackage; 321 } 322 323 @NotNull 324 public JsExpression rootPackageDefinitionMethodReference() { 325 return defineRootPackage; 326 } 327 328 @NotNull 329 public JsExpression objectCreationMethodReference() { 330 return kotlin(objectName); 331 } 332 333 @NotNull 334 public JsExpression callableRefForMemberFunctionReference() { 335 return kotlin(callableRefForMemberFunctionName); 336 } 337 338 @NotNull 339 public JsExpression callableRefForExtensionFunctionReference() { 340 return kotlin(callableRefForExtensionFunctionName); 341 } 342 343 @NotNull 344 public JsExpression callableRefForLocalExtensionFunctionReference() { 345 return kotlin(callableRefForLocalExtensionFunctionName); 346 } 347 348 @NotNull 349 public JsExpression callableRefForConstructorReference() { 350 return kotlin(callableRefForConstructorName); 351 } 352 353 @NotNull 354 public JsExpression callableRefForTopLevelPropertyReference() { 355 return kotlin(callableRefForTopLevelProperty); 356 } 357 358 @NotNull 359 public JsExpression callableRefForMemberPropertyReference() { 360 return kotlin(callableRefForMemberProperty); 361 } 362 363 @NotNull 364 public JsExpression callableRefForExtensionPropertyReference() { 365 return kotlin(callableRefForExtensionProperty); 366 } 367 368 @NotNull 369 public static JsExpression throwNPEFunctionRef() { 370 return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject()); 371 } 372 373 @NotNull 374 public JsExpression throwClassCastExceptionFunRef() { 375 return new JsNameRef(THROW_CLASS_CAST_EXCEPTION_FUN_NAME, kotlinObject()); 376 } 377 378 @NotNull 379 public static JsNameRef kotlin(@NotNull JsName name) { 380 return pureFqn(name, kotlinObject()); 381 } 382 383 @NotNull 384 public JsNameRef kotlin(@NotNull String name) { 385 return kotlin(kotlinScope.declareName(name)); 386 } 387 388 @NotNull 389 public static JsNameRef kotlinObject() { 390 return pureFqn(KOTLIN_NAME, null); 391 } 392 393 @NotNull 394 public JsExpression isTypeOf(@NotNull JsExpression type) { 395 return invokeFunctionAndSetTypeCheckMetadata("isTypeOf", type, TypeCheck.TYPEOF); 396 } 397 398 @NotNull 399 public JsExpression isInstanceOf(@NotNull JsExpression type) { 400 return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.INSTANCEOF); 401 } 402 403 @NotNull 404 public JsExpression orNull(@NotNull JsExpression callable) { 405 return invokeFunctionAndSetTypeCheckMetadata("orNull", callable, TypeCheck.OR_NULL); 406 } 407 408 @NotNull 409 public JsExpression andPredicate(@NotNull JsExpression a, @NotNull JsExpression b) { 410 return invokeFunctionAndSetTypeCheckMetadata("andPredicate", Arrays.asList(a, b), TypeCheck.AND_PREDICATE); 411 } 412 413 @NotNull 414 public JsExpression isAny() { 415 return invokeFunctionAndSetTypeCheckMetadata("isAny", Collections.<JsExpression>emptyList(), TypeCheck.IS_ANY); 416 } 417 418 @NotNull 419 public JsExpression isComparable() { 420 return kotlin("isComparable"); 421 } 422 423 @NotNull 424 public JsExpression isCharSequence() { 425 return kotlin(IS_CHAR_SEQUENCE); 426 } 427 428 @NotNull 429 private JsExpression invokeFunctionAndSetTypeCheckMetadata( 430 @NotNull String functionName, 431 @Nullable JsExpression argument, 432 @NotNull TypeCheck metadata 433 ) { 434 List<JsExpression> arguments = argument != null ? Collections.singletonList(argument) : Collections.<JsExpression>emptyList(); 435 return invokeFunctionAndSetTypeCheckMetadata(functionName, arguments, metadata); 436 } 437 438 @NotNull 439 private JsExpression invokeFunctionAndSetTypeCheckMetadata( 440 @NotNull String functionName, 441 @NotNull List<JsExpression> arguments, 442 @NotNull TypeCheck metadata 443 ) { 444 JsInvocation invocation = new JsInvocation(kotlin(functionName)); 445 invocation.getArguments().addAll(arguments); 446 MetadataProperties.setTypeCheck(invocation, metadata); 447 MetadataProperties.setSideEffects(invocation, SideEffectKind.PURE); 448 return invocation; 449 } 450 451 @NotNull 452 public JsExpression isInstanceOf(@NotNull JsExpression instance, @NotNull JsExpression type) { 453 JsInvocation result = new JsInvocation(kotlin(isTypeName), instance, type); 454 MetadataProperties.setSideEffects(result, SideEffectKind.PURE); 455 return result; 456 } 457 458 @NotNull 459 /*package*/ JsObjectScope getKotlinScope() { 460 return kotlinScope; 461 } 462 463 @NotNull 464 static String generatePackageName(@NotNull FqName packageFqName) { 465 return packageFqName.isRoot() ? getRootPackageName() : packageFqName.shortName().asString(); 466 } 467 468 @NotNull 469 public JsExpression classCreateInvocation(@NotNull ClassDescriptor descriptor) { 470 switch (descriptor.getKind()) { 471 case INTERFACE: 472 return traitCreationMethodReference(); 473 case ENUM_CLASS: 474 return enumClassCreationMethodReference(); 475 case ENUM_ENTRY: 476 case OBJECT: 477 return objectCreationMethodReference(); 478 case ANNOTATION_CLASS: 479 case CLASS: 480 return classCreationMethodReference(); 481 default: 482 throw new UnsupportedOperationException("Unsupported class kind: " + descriptor); 483 } 484 } 485 486 @NotNull 487 public static JsExpression getUndefinedExpression() { 488 return new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO); 489 } 490 491 @NotNull 492 public JsExpression getCallGetProperty() { 493 return callGetProperty; 494 } 495 496 @NotNull 497 public JsExpression getCallSetProperty() { 498 return callSetProperty; 499 } 500 501 @NotNull 502 public JsExpression getModuleReference(@NotNull JsStringLiteral moduleName) { 503 JsArrayAccess result = new JsArrayAccess(modulesMap, moduleName); 504 MetadataProperties.setSideEffects(result, SideEffectKind.PURE); 505 return result; 506 } 507 508 public static JsNameRef kotlinLong() { 509 return pureFqn("Long", kotlinObject()); 510 } 511 512 @NotNull 513 public static JsNameRef createInlineFunction() { 514 return pureFqn(DEFINE_INLINE_FUNCTION, kotlinObject()); 515 } 516 517 @NotNull 518 public static String suggestedModuleName(@NotNull String id) { 519 if (id.isEmpty()) { 520 return "_"; 521 } 522 523 StringBuilder sb = new StringBuilder(id.length()); 524 char c = id.charAt(0); 525 if (Character.isJavaIdentifierStart(c)) { 526 sb.append(c); 527 } 528 else { 529 sb.append('_'); 530 if (Character.isJavaIdentifierPart(c)) { 531 sb.append(c); 532 } 533 } 534 535 for (int i = 1; i < id.length(); ++i) { 536 c = id.charAt(i); 537 sb.append(Character.isJavaIdentifierPart(c) ? c : '_'); 538 } 539 540 return sb.toString(); 541 } 542 543 public static boolean requiresEscaping(@NotNull String name) { 544 // TODO: remove if there is existing implementation of this method 545 // TODO: handle JavaScript keywords 546 if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) return true; 547 for (int i = 1; i < name.length(); ++i) { 548 if (!Character.isJavaIdentifierPart(name.charAt(i))) return true; 549 } 550 return false; 551 } 552 }