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 }