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    }