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