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    }