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.utils;
018    
019    import com.intellij.openapi.util.Condition;
020    import com.intellij.psi.PsiElement;
021    import com.intellij.util.containers.ContainerUtil;
022    import org.jetbrains.annotations.NotNull;
023    import org.jetbrains.annotations.Nullable;
024    import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
025    import org.jetbrains.kotlin.descriptors.*;
026    import org.jetbrains.kotlin.js.config.LibrarySourcesConfig;
027    import org.jetbrains.kotlin.js.descriptorUtils.DescriptorUtilsKt;
028    import org.jetbrains.kotlin.js.translate.context.Namer;
029    import org.jetbrains.kotlin.js.translate.context.TranslationContext;
030    import org.jetbrains.kotlin.name.Name;
031    import org.jetbrains.kotlin.psi.KtExpression;
032    import org.jetbrains.kotlin.resolve.DescriptorUtils;
033    import org.jetbrains.kotlin.resolve.calls.tasks.DynamicCallsKt;
034    import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver;
035    import org.jetbrains.kotlin.resolve.scopes.receivers.ReceiverValue;
036    import org.jetbrains.kotlin.types.KotlinType;
037    import org.jetbrains.kotlin.types.typeUtil.TypeUtilsKt;
038    import org.jetbrains.kotlin.util.OperatorNameConventions;
039    
040    import java.util.Collection;
041    import java.util.List;
042    import java.util.Set;
043    
044    import static org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils.isNativeObject;
045    import static org.jetbrains.kotlin.resolve.DescriptorToSourceUtils.descriptorToDeclaration;
046    import static org.jetbrains.kotlin.resolve.DescriptorUtils.*;
047    
048    public final class JsDescriptorUtils {
049        // TODO: maybe we should use external annotations or something else.
050        private static final Set<String> FAKE_CLASSES = ContainerUtil.immutableSet(
051                KotlinBuiltIns.FQ_NAMES.any.asString()
052        );
053    
054        private JsDescriptorUtils() {
055        }
056    
057        private static int valueParametersCount(@NotNull FunctionDescriptor functionDescriptor) {
058            return functionDescriptor.getValueParameters().size();
059        }
060    
061        public static boolean hasParameters(@NotNull FunctionDescriptor functionDescriptor) {
062            return (valueParametersCount(functionDescriptor) > 0);
063        }
064    
065        public static boolean isCompareTo(@NotNull CallableDescriptor descriptor) {
066            return descriptor.getName().equals(OperatorNameConventions.COMPARE_TO);
067        }
068    
069        @Nullable
070        public static ClassDescriptor findAncestorClass(@NotNull List<ClassDescriptor> superclassDescriptors) {
071            for (ClassDescriptor descriptor : superclassDescriptors) {
072                if (descriptor.getKind() == ClassKind.CLASS || descriptor.getKind() == ClassKind.ENUM_CLASS) {
073                    return descriptor;
074                }
075            }
076            return null;
077        }
078    
079        @Nullable
080        public static ClassDescriptor getSuperclass(@NotNull ClassDescriptor classDescriptor) {
081            return findAncestorClass(getSuperclassDescriptors(classDescriptor));
082        }
083    
084        @NotNull
085        public static List<KotlinType> getSupertypesWithoutFakes(ClassDescriptor descriptor) {
086            Collection<KotlinType> supertypes = descriptor.getTypeConstructor().getSupertypes();
087            return ContainerUtil.filter(supertypes, new Condition<KotlinType>() {
088                @Override
089                public boolean value(KotlinType type) {
090                    ClassDescriptor classDescriptor = getClassDescriptorForType(type);
091    
092                    return !FAKE_CLASSES.contains(getFqNameSafe(classDescriptor).asString()) &&
093                           !(classDescriptor.getKind() == ClassKind.INTERFACE && isNativeObject(classDescriptor));
094                }
095            });
096        }
097    
098        @NotNull
099        public static DeclarationDescriptor getContainingDeclaration(@NotNull DeclarationDescriptor descriptor) {
100            DeclarationDescriptor containing = descriptor.getContainingDeclaration();
101            assert containing != null : "Should be called on objects that have containing declaration.";
102            return containing;
103        }
104    
105        @NotNull
106        public static ReceiverParameterDescriptor getReceiverParameterForReceiver(@NotNull ReceiverValue receiverParameter) {
107            DeclarationDescriptor declarationDescriptor = getDeclarationDescriptorForReceiver(receiverParameter);
108            return getReceiverParameterForDeclaration(declarationDescriptor);
109        }
110    
111        @NotNull
112        private static DeclarationDescriptor getDeclarationDescriptorForReceiver(@NotNull ReceiverValue receiverParameter) {
113            if (receiverParameter instanceof ImplicitReceiver) {
114                DeclarationDescriptor declarationDescriptor = ((ImplicitReceiver) receiverParameter).getDeclarationDescriptor();
115                return declarationDescriptor.getOriginal();
116            }
117    
118            throw new UnsupportedOperationException("Unsupported receiver type: " + receiverParameter.getClass() +
119                                                    ", receiverParameter = " + receiverParameter);
120        }
121    
122        @NotNull
123        public static ReceiverParameterDescriptor getReceiverParameterForDeclaration(DeclarationDescriptor declarationDescriptor) {
124            if (declarationDescriptor instanceof ClassDescriptor) {
125                return ((ClassDescriptor) declarationDescriptor).getThisAsReceiverParameter();
126            }
127            else if (declarationDescriptor instanceof CallableMemberDescriptor) {
128                ReceiverParameterDescriptor receiverDescriptor = ((CallableMemberDescriptor) declarationDescriptor).getExtensionReceiverParameter();
129                assert receiverDescriptor != null;
130                return receiverDescriptor;
131            }
132    
133            throw new UnsupportedOperationException("Unsupported declaration type: " + declarationDescriptor.getClass() +
134                                                    ", declarationDescriptor = " + declarationDescriptor);
135        }
136    
137        private static boolean isDefaultAccessor(@Nullable PropertyAccessorDescriptor accessorDescriptor) {
138            return accessorDescriptor == null || accessorDescriptor.isDefault();
139        }
140    
141        public static boolean sideEffectsPossibleOnRead(@NotNull PropertyDescriptor property) {
142            return DynamicCallsKt.isDynamic(property) || !isDefaultAccessor(property.getGetter()) ||
143                   ModalityKt.isOverridableOrOverrides(property) || isStaticInitializationPossible(property);
144        }
145    
146        private static boolean isStaticInitializationPossible(PropertyDescriptor property) {
147            DeclarationDescriptor container = property.getContainingDeclaration();
148            return container instanceof PackageFragmentDescriptor || DescriptorUtils.isObject(container);
149        }
150    
151        public static boolean isSimpleFinalProperty(@NotNull PropertyDescriptor propertyDescriptor) {
152            return !isExtension(propertyDescriptor) &&
153                   isDefaultAccessor(propertyDescriptor.getGetter()) &&
154                   isDefaultAccessor(propertyDescriptor.getSetter()) &&
155                   !TranslationUtils.shouldAccessViaFunctions(propertyDescriptor) &&
156                   !ModalityKt.isOverridableOrOverrides(propertyDescriptor);
157        }
158    
159        @Nullable
160        public static Name getNameIfStandardType(@NotNull KtExpression expression, @NotNull TranslationContext context) {
161            KotlinType type = context.bindingContext().getType(expression);
162            return type != null ? DescriptorUtilsKt.getNameIfStandardType(type) : null;
163        }
164    
165        @NotNull
166        public static String getModuleName(@NotNull DeclarationDescriptor descriptor) {
167            String externalModuleName = getExternalModuleName(descriptor);
168            if (externalModuleName != null) return externalModuleName;
169    
170            return getModuleNameFromDescriptorName(descriptor);
171        }
172    
173        @Nullable
174        public static String getExternalModuleName(@NotNull DeclarationDescriptor descriptor) {
175            if (KotlinBuiltIns.isBuiltIn(descriptor)) return Namer.KOTLIN_LOWER_NAME;
176    
177            PsiElement element = descriptorToDeclaration(descriptor);
178            if (element == null && descriptor instanceof PropertyAccessorDescriptor) {
179                element = descriptorToDeclaration(((PropertyAccessorDescriptor) descriptor).getCorrespondingProperty());
180            }
181    
182            if (element == null) return getModuleNameFromDescriptorName(descriptor);
183    
184            return element.getContainingFile().getUserData(LibrarySourcesConfig.EXTERNAL_MODULE_NAME);
185        }
186    
187        @NotNull
188        public static DeclarationDescriptor findRealInlineDeclaration(@NotNull DeclarationDescriptor descriptor) {
189            if (descriptor instanceof FunctionDescriptor) {
190                FunctionDescriptor d = (FunctionDescriptor) descriptor;
191                if (d.getKind().isReal() || !d.isInline()) return descriptor;
192                CallableMemberDescriptor real = findRealDeclaration(d);
193                assert real != null : "Couldn't find definition of a fake inline descriptor " + descriptor;
194                return real;
195            }
196            return descriptor;
197        }
198    
199        @Nullable
200        private static FunctionDescriptor findRealDeclaration(FunctionDescriptor descriptor) {
201            if (descriptor.getModality() == Modality.ABSTRACT) return null;
202            if (descriptor.getKind().isReal()) return descriptor;
203    
204            for (FunctionDescriptor o : descriptor.getOverriddenDescriptors()) {
205                FunctionDescriptor child = findRealDeclaration(o);
206                if (child != null) {
207                    return child;
208                }
209            }
210            return null;
211        }
212    
213        private static String getModuleNameFromDescriptorName(@NotNull DeclarationDescriptor descriptor) {
214            ModuleDescriptor moduleDescriptor = DescriptorUtils.getContainingModule(findRealInlineDeclaration(descriptor));
215            String moduleName = moduleDescriptor.getName().asString();
216            return moduleName.substring(1, moduleName.length() - 1);
217        }
218    
219    
220        public static boolean isImmediateSubtypeOfError(@NotNull ClassDescriptor descriptor) {
221            if (!isExceptionClass(descriptor)) return false;
222            ClassDescriptor superClass = org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt.getSuperClassOrAny(descriptor);
223            return TypeUtilsKt.isThrowable(superClass.getDefaultType()) || AnnotationsUtils.isNativeObject(superClass);
224        }
225    
226        public static boolean isExceptionClass(@NotNull ClassDescriptor descriptor) {
227            ModuleDescriptor module = DescriptorUtils.getContainingModule(descriptor);
228            return TypeUtilsKt.isSubtypeOf(descriptor.getDefaultType(), module.getBuiltIns().getThrowable().getDefaultType());
229        }
230    }