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    }