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 boolean isUndefined(@NotNull JsExpression expr) {
110            if (expr instanceof JsPrefixOperation) {
111                JsUnaryOperator op = ((JsPrefixOperation) expr).getOperator();
112    
113                return op == JsUnaryOperator.VOID;
114            }
115    
116            return false;
117        }
118    
119        @NotNull
120        public static String getFunctionTag(@NotNull CallableDescriptor functionDescriptor) {
121            String moduleName = getModuleName(functionDescriptor);
122            FqNameUnsafe fqNameParent = DescriptorUtils.getFqName(functionDescriptor).parent();
123            String qualifier = null;
124    
125            if (!fqNameParent.isRoot()) {
126                qualifier = fqNameParent.asString();
127            }
128    
129            String mangledName = getSuggestedName(functionDescriptor);
130            return StringUtil.join(Arrays.asList(moduleName, qualifier, mangledName), ".");
131        }
132    
133        @NotNull
134        public static String getReceiverParameterName() {
135            return RECEIVER_PARAMETER_NAME;
136        }
137    
138        @NotNull
139        public static String getRootPackageName() {
140            return ROOT_PACKAGE;
141        }
142    
143        @NotNull
144        public static JsNameRef superMethodNameRef(@NotNull JsName superClassJsName) {
145            return pureFqn(SUPER_METHOD_NAME, superClassJsName.makeRef());
146        }
147    
148        @NotNull
149        public static String getNameForAccessor(@NotNull String propertyName, boolean isGetter, boolean useNativeAccessor) {
150            if (useNativeAccessor) {
151                return propertyName;
152            }
153    
154            if (isGetter) {
155                return getNameForGetter(propertyName);
156            }
157            else {
158                return getNameForSetter(propertyName);
159            }
160        }
161    
162        @NotNull
163        public static String getKotlinBackingFieldName(@NotNull String propertyName) {
164            return getNameWithPrefix(propertyName, BACKING_FIELD_PREFIX);
165        }
166    
167        @NotNull
168        private static String getNameForGetter(@NotNull String propertyName) {
169            return getNameWithPrefix(propertyName, GETTER_PREFIX);
170        }
171    
172        @NotNull
173        private static String getNameForSetter(@NotNull String propertyName) {
174            return getNameWithPrefix(propertyName, SETTER_PREFIX);
175        }
176    
177        @NotNull
178        public static String getPrototypeName() {
179            return PROTOTYPE_NAME;
180        }
181    
182        @NotNull
183        public static JsNameRef getRefToPrototype(@NotNull JsExpression classOrTraitExpression) {
184            return pureFqn(getPrototypeName(), classOrTraitExpression);
185        }
186    
187        @NotNull
188        public static String getDelegatePrefix() {
189            return DELEGATE;
190        }
191    
192        @NotNull
193        public static String getDelegateName(@NotNull String propertyName) {
194            return propertyName + DELEGATE;
195        }
196    
197        @NotNull
198        public static JsNameRef getDelegateNameRef(String propertyName) {
199            return new JsNameRef(getDelegateName(propertyName), JsLiteral.THIS);
200        }
201    
202        @NotNull
203        private static String getNameWithPrefix(@NotNull String name, @NotNull String prefix) {
204            return prefix + name;
205        }
206    
207        @NotNull
208        public static JsNameRef getFunctionCallRef(@NotNull JsExpression functionExpression) {
209            return pureFqn(CALL_FUNCTION, functionExpression);
210        }
211    
212        @NotNull
213        public static JsNameRef getFunctionApplyRef(@NotNull JsExpression functionExpression) {
214            return pureFqn(APPLY_FUNCTION, functionExpression);
215        }
216    
217        @NotNull
218        public static JsInvocation createObjectWithPrototypeFrom(JsNameRef referenceToClass) {
219            return new JsInvocation(JS_OBJECT_CREATE_FUNCTION, getRefToPrototype(referenceToClass));
220        }
221    
222        @NotNull
223        public static JsNameRef getCapturedVarAccessor(@NotNull JsExpression ref) {
224            return pureFqn(CAPTURED_VAR_FIELD, ref);
225        }
226    
227        @NotNull
228        public static String isInstanceSuggestedName(@NotNull TypeParameterDescriptor descriptor) {
229            return "is" + descriptor.getName().getIdentifier();
230        }
231    
232        @NotNull
233        public static Namer newInstance(@NotNull JsScope rootScope) {
234            return new Namer(rootScope);
235        }
236    
237        @NotNull
238        private final JsObjectScope kotlinScope;
239        @NotNull
240        private final JsName className;
241        @NotNull
242        private final JsName enumClassName;
243        @NotNull
244        private final JsName traitName;
245        @NotNull
246        private final JsExpression definePackage;
247        @NotNull
248        private final JsExpression defineRootPackage;
249        @NotNull
250        private final JsName objectName;
251        @NotNull
252        private final JsName callableRefForMemberFunctionName;
253        @NotNull
254        private final JsName callableRefForExtensionFunctionName;
255        @NotNull
256        private final JsName callableRefForLocalExtensionFunctionName;
257        @NotNull
258        private final JsName callableRefForConstructorName;
259        @NotNull
260        private final JsName callableRefForTopLevelProperty;
261        @NotNull
262        private final JsName callableRefForMemberProperty;
263        @NotNull
264        private final JsName callableRefForExtensionProperty;
265        @NotNull
266        private final JsExpression callGetProperty;
267        @NotNull
268        private final JsExpression callSetProperty;
269    
270        @NotNull
271        private final JsName isTypeName;
272    
273        @NotNull
274        private final JsExpression modulesMap;
275    
276        private Namer(@NotNull JsScope rootScope) {
277            kotlinScope = JsObjectScope(rootScope, "Kotlin standard object");
278            traitName = kotlinScope.declareName(TRAIT_OBJECT_NAME);
279    
280            definePackage = kotlin("definePackage");
281            defineRootPackage = kotlin("defineRootPackage");
282    
283            callGetProperty = kotlin("callGetter");
284            callSetProperty = kotlin("callSetter");
285    
286            className = kotlinScope.declareName(CLASS_OBJECT_NAME);
287            enumClassName = kotlinScope.declareName(ENUM_CLASS_OBJECT_NAME);
288            objectName = kotlinScope.declareName(OBJECT_OBJECT_NAME);
289            callableRefForMemberFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_FUNCTION_NAME);
290            callableRefForExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_FUNCTION_NAME);
291            callableRefForLocalExtensionFunctionName = kotlinScope.declareName(CALLABLE_REF_FOR_LOCAL_EXTENSION_FUNCTION_NAME);
292            callableRefForConstructorName = kotlinScope.declareName(CALLABLE_REF_FOR_CONSTRUCTOR_NAME);
293            callableRefForTopLevelProperty = kotlinScope.declareName(CALLABLE_REF_FOR_TOP_LEVEL_PROPERTY);
294            callableRefForMemberProperty = kotlinScope.declareName(CALLABLE_REF_FOR_MEMBER_PROPERTY);
295            callableRefForExtensionProperty = kotlinScope.declareName(CALLABLE_REF_FOR_EXTENSION_PROPERTY);
296    
297            isTypeName = kotlinScope.declareName("isType");
298            modulesMap = kotlin("modules");
299        }
300    
301        @NotNull
302        public JsExpression classCreationMethodReference() {
303            return kotlin(className);
304        }
305    
306        @NotNull
307        public JsExpression enumClassCreationMethodReference() {
308            return kotlin(enumClassName);
309        }
310    
311        @NotNull
312        public JsExpression traitCreationMethodReference() {
313            return kotlin(traitName);
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 objectCreationMethodReference() {
328            return kotlin(objectName);
329        }
330    
331        @NotNull
332        public JsExpression callableRefForMemberFunctionReference() {
333            return kotlin(callableRefForMemberFunctionName);
334        }
335    
336        @NotNull
337        public JsExpression callableRefForExtensionFunctionReference() {
338            return kotlin(callableRefForExtensionFunctionName);
339        }
340    
341        @NotNull
342        public JsExpression callableRefForLocalExtensionFunctionReference() {
343            return kotlin(callableRefForLocalExtensionFunctionName);
344        }
345    
346        @NotNull
347        public JsExpression callableRefForConstructorReference() {
348            return kotlin(callableRefForConstructorName);
349        }
350    
351        @NotNull
352        public JsExpression callableRefForTopLevelPropertyReference() {
353            return kotlin(callableRefForTopLevelProperty);
354        }
355    
356        @NotNull
357        public JsExpression callableRefForMemberPropertyReference() {
358            return kotlin(callableRefForMemberProperty);
359        }
360    
361        @NotNull
362        public JsExpression callableRefForExtensionPropertyReference() {
363            return kotlin(callableRefForExtensionProperty);
364        }
365    
366        @NotNull
367        public static JsExpression throwNPEFunctionRef() {
368            return new JsNameRef(THROW_NPE_FUN_NAME, kotlinObject());
369        }
370    
371        @NotNull
372        public JsExpression throwClassCastExceptionFunRef() {
373            return new JsNameRef(THROW_CLASS_CAST_EXCEPTION_FUN_NAME, kotlinObject());
374        }
375    
376        @NotNull
377        public static JsNameRef kotlin(@NotNull JsName name) {
378            return pureFqn(name, kotlinObject());
379        }
380    
381        @NotNull
382        public JsNameRef kotlin(@NotNull String name) {
383            return kotlin(kotlinScope.declareName(name));
384        }
385    
386        @NotNull
387        public static JsNameRef kotlinObject() {
388            return pureFqn(KOTLIN_NAME, null);
389        }
390    
391        @NotNull
392        public JsExpression isTypeOf(@NotNull JsExpression type) {
393            return invokeFunctionAndSetTypeCheckMetadata("isTypeOf", type, TypeCheck.TYPEOF);
394        }
395    
396        @NotNull
397        public JsExpression isInstanceOf(@NotNull JsExpression type) {
398            return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.INSTANCEOF);
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 traitCreationMethodReference();
471                case ENUM_CLASS:
472                    return enumClassCreationMethodReference();
473                case ENUM_ENTRY:
474                case OBJECT:
475                    return objectCreationMethodReference();
476                case ANNOTATION_CLASS:
477                case CLASS:
478                    return 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        @NotNull
500        public JsExpression getModuleReference(@NotNull JsStringLiteral moduleName) {
501            JsArrayAccess result = new JsArrayAccess(modulesMap, moduleName);
502            MetadataProperties.setSideEffects(result, SideEffectKind.PURE);
503            return result;
504        }
505    
506        public static JsNameRef kotlinLong() {
507            return pureFqn("Long", kotlinObject());
508        }
509    
510        @NotNull
511        public static JsNameRef createInlineFunction() {
512            return pureFqn(DEFINE_INLINE_FUNCTION, kotlinObject());
513        }
514    }