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 public static boolean isUndefined(@NotNull JsExpression expr) {
118 if (expr instanceof JsPrefixOperation) {
119 JsUnaryOperator op = ((JsPrefixOperation) expr).getOperator();
120
121 return op == JsUnaryOperator.VOID;
122 }
123
124 return false;
125 }
126
127 @NotNull
128 public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor) {
129 String moduleName = getModuleName(functionDescriptor);
130 FqNameUnsafe fqNameParent = DescriptorUtils.getFqName(functionDescriptor).parent();
131 String qualifier = null;
132
133 if (!fqNameParent.isRoot()) {
134 qualifier = fqNameParent.asString();
135 }
136
137 String mangledName = new NameSuggestion().suggest(functionDescriptor).getNames().get(0);
138 return StringUtil.join(Arrays.asList(moduleName, qualifier, mangledName), ".");
139 }
140
141 @NotNull
142 public static String getReceiverParameterName() {
143 return RECEIVER_PARAMETER_NAME;
144 }
145
146 @NotNull
147 public static String getRootPackageName() {
148 return ROOT_PACKAGE;
149 }
150
151 @NotNull
152 public static JsNameRef superMethodNameRef(@NotNull JsName superClassJsName) {
153 return pureFqn(SUPER_METHOD_NAME, superClassJsName.makeRef());
154 }
155
156 @NotNull
157 public static String getNameForAccessor(@NotNull String propertyName, boolean isGetter, boolean useNativeAccessor) {
158 if (useNativeAccessor) {
159 return propertyName;
160 }
161
162 if (isGetter) {
163 return getNameForGetter(propertyName);
164 }
165 else {
166 return getNameForSetter(propertyName);
167 }
168 }
169
170 @NotNull
171 public static String getKotlinBackingFieldName(@NotNull String propertyName) {
172 return getNameWithPrefix(propertyName, BACKING_FIELD_PREFIX);
173 }
174
175 @NotNull
176 private static String getNameForGetter(@NotNull String propertyName) {
177 return getNameWithPrefix(propertyName, GETTER_PREFIX);
178 }
179
180 @NotNull
181 private static String getNameForSetter(@NotNull String propertyName) {
182 return getNameWithPrefix(propertyName, SETTER_PREFIX);
183 }
184
185 @NotNull
186 public static String getPrototypeName() {
187 return PROTOTYPE_NAME;
188 }
189
190 @NotNull
191 private static JsNameRef getRefToPrototype(@NotNull JsExpression classOrTraitExpression) {
192 return pureFqn(getPrototypeName(), classOrTraitExpression);
193 }
194
195 @NotNull
196 public static String getDelegatePrefix() {
197 return DELEGATE;
198 }
199
200 @NotNull
201 public static String getDelegateName(@NotNull String propertyName) {
202 return propertyName + DELEGATE;
203 }
204
205 @NotNull
206 public static JsNameRef getDelegateNameRef(String propertyName) {
207 return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS);
208 }
209
210 @NotNull
211 private static String getNameWithPrefix(@NotNull String name, @NotNull String prefix) {
212 return prefix + name;
213 }
214
215 @NotNull
216 public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) {
217 return pureFqn(CALL_FUNCTION, functionExpression);
218 }
219
220 @NotNull
221 public static JsNameRef getFunctionApplyRef(@NotNull JsExpression functionExpression) {
222 return pureFqn(APPLY_FUNCTION, functionExpression);
223 }
224
225 @NotNull
226 public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) {
227 return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, getRefToPrototype(referenceToClass));
228 }
229
230 @NotNull
231 public static JsNameRef getCapturedVarAccessor(@NotNull JsExpression ref) {
232 return pureFqn(CAPTURED_VAR_FIELD, ref);
233 }
234
235 @NotNull
236 public static String isInstanceSuggestedName(@NotNull TypeParameterDescriptor descriptor) {
237 return "is" + descriptor.getName().getIdentifier();
238 }
239
240 @NotNull
241 public static Namer newInstance(@NotNull JsScope rootScope) {
242 return new Namer(rootScope);
243 }
244
245 @NotNull
246 private final JsObjectScope kotlinScope;
247 @NotNull
248 private final JsName classCreationMethodReference;
249 @NotNull
250 private final JsName enumClassCreationMethodName;
251 @NotNull
252 private final JsName interfaceCreationMethodName;
253 @NotNull
254 private final JsExpression definePackage;
255 @NotNull
256 private final JsExpression defineRootPackage;
257 @NotNull
258 private final JsName objectCreationMethodName;
259 @NotNull
260 private final JsName callableRefForMemberFunctionName;
261 @NotNull
262 private final JsName callableRefForExtensionFunctionName;
263 @NotNull
264 private final JsName callableRefForLocalExtensionFunctionName;
265 @NotNull
266 private final JsName callableRefForConstructorName;
267 @NotNull
268 private final JsName callableRefForTopLevelProperty;
269 @NotNull
270 private final JsName callableRefForMemberProperty;
271 @NotNull
272 private final JsName callableRefForExtensionProperty;
273 @NotNull
274 private final JsExpression callGetProperty;
275 @NotNull
276 private final JsExpression callSetProperty;
277
278 @NotNull
279 private final JsName isTypeName;
280
281 private Namer(@NotNull JsScope rootScope) {
282 kotlinScope = JsObjectScope(rootScope, "Kotlin standard object");
283 interfaceCreationMethodName = kotlinScope.declareName(TRAIT_OBJECT_NAME);
284
285 definePackage = kotlin("definePackage");
286 defineRootPackage = kotlin("defineRootPackage");
287
288 callGetProperty = kotlin("callGetter");
289 callSetProperty = kotlin("callSetter");
290
291 classCreationMethodReference = kotlinScope.declareName(CLASS_OBJECT_NAME);
292 enumClassCreationMethodName = kotlinScope.declareName(ENUM_CLASS_OBJECT_NAME);
293 objectCreationMethodName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
294 callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
295 callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
296 callableRefForLocalExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME);
297 callableRefForConstructorName = kotlinScope.declareName(CALLABLE_REF_FOR_CONSTRUCTOR_NAME);
298 callableRefForTopLevelProperty = kotlinScope.declareName(CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY);
299 callableRefForMemberProperty = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_PROPERTY);
300 callableRefForExtensionProperty = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_PROPERTY);
301
302 isTypeName = kotlinScope.declareName("isType");
303 }
304
305 // TODO: get rid of this function
306 @NotNull
307 public static String getStableMangledNameForDescriptor(@NotNull ClassDescriptor descriptor, @NotNull String functionName) {
308 Collection<SimpleFunctionDescriptor> functions = descriptor.getDefaultType().getMemberScope().getContributedFunctions(
309 Name.identifier(functionName), NoLookupLocation.FROM_BACKEND);
310 assert functions.size() == 1 : "Can't select a single function: " + functionName + " in " + descriptor;
311 SuggestedName suggested = new NameSuggestion().suggest(functions.iterator().next());
312 assert suggested != null : "Suggested name for class members is always non-null: " + functions.iterator().next();
313 return suggested.getNames().get(0);
314 }
315
316 @NotNull
317 public JsExpression packageDefinitionMethodReference() {
318 return definePackage;
319 }
320
321 @NotNull
322 public JsExpression rootPackageDefinitionMethodReference() {
323 return defineRootPackage;
324 }
325
326 @NotNull
327 public JsExpression callableRefForMemberFunctionReference() {
328 return kotlin(callableRefForMemberFunctionName);
329 }
330
331 @NotNull
332 public JsExpression callableRefForExtensionFunctionReference() {
333 return kotlin(callableRefForExtensionFunctionName);
334 }
335
336 @NotNull
337 public JsExpression callableRefForLocalExtensionFunctionReference() {
338 return kotlin(callableRefForLocalExtensionFunctionName);
339 }
340
341 @NotNull
342 public JsExpression callableRefForConstructorReference() {
343 return kotlin(callableRefForConstructorName);
344 }
345
346 @NotNull
347 public JsExpression callableRefForTopLevelPropertyReference() {
348 return kotlin(callableRefForTopLevelProperty);
349 }
350
351 @NotNull
352 public JsExpression callableRefForMemberPropertyReference() {
353 return kotlin(callableRefForMemberProperty);
354 }
355
356 @NotNull
357 public JsExpression callableRefForExtensionPropertyReference() {
358 return kotlin(callableRefForExtensionProperty);
359 }
360
361 @NotNull
362 public static JsExpression throwNPEFunctionRef() {
363 return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
364 }
365
366 @NotNull
367 public static JsExpression throwClassCastExceptionFunRef() {
368 return new JsNameRef(THROW_CLASS_CAST_EXCEPTION_FUN_NAME, kotlinObject());
369 }
370
371 @NotNull
372 public static JsNameRef kotlin(@NotNull JsName name) {
373 return pureFqn(name, kotlinObject());
374 }
375
376 @NotNull
377 public JsNameRef kotlin(@NotNull String name) {
378 return kotlin(kotlinScope.declareName(name));
379 }
380
381 @NotNull
382 public static JsNameRef kotlinObject() {
383 return pureFqn(KOTLIN_NAME, null);
384 }
385
386 @NotNull
387 public JsExpression isTypeOf(@NotNull JsExpression type) {
388 return invokeFunctionAndSetTypeCheckMetadata("isTypeOf", type, TypeCheck.TYPEOF);
389 }
390
391 @NotNull
392 public JsExpression isInstanceOf(@NotNull JsExpression type) {
393 return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.INSTANCEOF);
394 }
395
396 @NotNull
397 public JsExpression isInstanceOfObject(@NotNull JsExpression type) {
398 return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.SAME_AS);
399 }
400
401 @NotNull
402 public JsExpression orNull(@NotNull JsExpression callable) {
403 return invokeFunctionAndSetTypeCheckMetadata("orNull", callable, TypeCheck.OR_NULL);
404 }
405
406 @NotNull
407 public JsExpression andPredicate(@NotNull JsExpression a, @NotNull JsExpression b) {
408 return invokeFunctionAndSetTypeCheckMetadata("andPredicate", Arrays.asList(a, b), TypeCheck.AND_PREDICATE);
409 }
410
411 @NotNull
412 public JsExpression isAny() {
413 return invokeFunctionAndSetTypeCheckMetadata("isAny", Collections.<JsExpression>emptyList(), TypeCheck.IS_ANY);
414 }
415
416 @NotNull
417 public JsExpression isComparable() {
418 return kotlin("isComparable");
419 }
420
421 @NotNull
422 public JsExpression isCharSequence() {
423 return kotlin(IS_CHAR_SEQUENCE);
424 }
425
426 @NotNull
427 private JsExpression invokeFunctionAndSetTypeCheckMetadata(
428 @NotNull String functionName,
429 @Nullable JsExpression argument,
430 @NotNull TypeCheck metadata
431 ) {
432 List<JsExpression> arguments = argument != null ? Collections.singletonList(argument) : Collections.<JsExpression>emptyList();
433 return invokeFunctionAndSetTypeCheckMetadata(functionName, arguments, metadata);
434 }
435
436 @NotNull
437 private JsExpression invokeFunctionAndSetTypeCheckMetadata(
438 @NotNull String functionName,
439 @NotNull List<JsExpression> arguments,
440 @NotNull TypeCheck metadata
441 ) {
442 JsInvocation invocation = new JsInvocation(kotlin(functionName));
443 invocation.getArguments().addAll(arguments);
444 MetadataProperties.setTypeCheck(invocation, metadata);
445 MetadataProperties.setSideEffects(invocation, SideEffectKind.PURE);
446 return invocation;
447 }
448
449 @NotNull
450 public JsExpression isInstanceOf(@NotNull JsExpression instance, @NotNull JsExpression type) {
451 JsInvocation result = new JsInvocation(kotlin(isTypeName), instance, type);
452 MetadataProperties.setSideEffects(result, SideEffectKind.PURE);
453 return result;
454 }
455
456 @NotNull
457 /*package*/ JsObjectScope getKotlinScope() {
458 return kotlinScope;
459 }
460
461 @NotNull
462 static String generatePackageName(@NotNull FqName packageFqName) {
463 return packageFqName.isRoot() ? getRootPackageName() : packageFqName.shortName().asString();
464 }
465
466 @NotNull
467 public JsExpression classCreateInvocation(@NotNull ClassDescriptor descriptor) {
468 switch (descriptor.getKind()) {
469 case INTERFACE:
470 return kotlin(interfaceCreationMethodName);
471 case ENUM_CLASS:
472 return kotlin(enumClassCreationMethodName);
473 case ENUM_ENTRY:
474 case OBJECT:
475 return kotlin(objectCreationMethodName);
476 case ANNOTATION_CLASS:
477 case CLASS:
478 return kotlin(classCreationMethodReference);
479 default:
480 throw new UnsupportedOperationException("Unsupported class kind: " + descriptor);
481 }
482 }
483
484 @NotNull
485 public static JsExpression getUndefinedExpression() {
486 return new JsPrefixOperation(JsUnaryOperator.VOID, JsNumberLiteral.ZERO);
487 }
488
489 @NotNull
490 public JsExpression getCallGetProperty() {
491 return callGetProperty;
492 }
493
494 @NotNull
495 public JsExpression getCallSetProperty() {
496 return callSetProperty;
497 }
498
499 public static JsNameRef kotlinLong() {
500 return pureFqn("Long", kotlinObject());
501 }
502
503 @NotNull
504 public static JsNameRef createInlineFunction() {
505 return pureFqn(DEFINE_INLINE_FUNCTION, kotlinObject());
506 }
507
508 @NotNull
509 public static String suggestedModuleName(@NotNull String id) {
510 if (id.isEmpty()) {
511 return "_";
512 }
513
514 StringBuilder sb = new StringBuilder(id.length());
515 char c = id.charAt(0);
516 if (Character.isJavaIdentifierStart(c)) {
517 sb.append(c);
518 }
519 else {
520 sb.append('_');
521 if (Character.isJavaIdentifierPart(c)) {
522 sb.append(c);
523 }
524 }
525
526 for (int i = 1; i < id.length(); ++i) {
527 c = id.charAt(i);
528 sb.append(Character.isJavaIdentifierPart(c) ? c : '_');
529 }
530
531 return sb.toString();
532 }
533
534 public static boolean requiresEscaping(@NotNull String name) {
535 // TODO: remove if there is existing implementation of this method
536 // TODO: handle JavaScript keywords
537 if (name.isEmpty() || !Character.isJavaIdentifierStart(name.charAt(0))) return true;
538 for (int i = 1; i < name.length(); ++i) {
539 if (!Character.isJavaIdentifierPart(name.charAt(i))) return true;
540 }
541 return false;
542 }
543 }