001    /*
002     * Copyright 2010-2013 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.jet.lang.resolve.java.resolver;
018    
019    import com.google.common.collect.Lists;
020    import com.google.common.collect.Sets;
021    import com.intellij.openapi.util.text.StringUtil;
022    import com.intellij.psi.PsiClass;
023    import com.intellij.psi.PsiMethod;
024    import com.intellij.psi.PsiModifier;
025    import gnu.trove.THashMap;
026    import gnu.trove.TObjectHashingStrategy;
027    import org.jetbrains.annotations.NotNull;
028    import org.jetbrains.annotations.Nullable;
029    import org.jetbrains.jet.lang.descriptors.*;
030    import org.jetbrains.jet.lang.resolve.BindingContext;
031    import org.jetbrains.jet.lang.resolve.BindingTrace;
032    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
033    import org.jetbrains.jet.lang.resolve.java.*;
034    import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
035    import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation;
036    import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
037    import org.jetbrains.jet.lang.resolve.java.provider.MembersCache;
038    import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
039    import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope;
040    import org.jetbrains.jet.lang.resolve.java.wrapper.PsiClassWrapper;
041    import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
042    import org.jetbrains.jet.lang.resolve.name.FqName;
043    import org.jetbrains.jet.lang.resolve.name.FqNameBase;
044    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
045    import org.jetbrains.jet.lang.resolve.name.Name;
046    import org.jetbrains.jet.lang.types.JetType;
047    import org.jetbrains.jet.lang.types.TypeUtils;
048    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
049    
050    import javax.inject.Inject;
051    import java.util.ArrayList;
052    import java.util.List;
053    import java.util.Map;
054    import java.util.Set;
055    
056    public final class JavaClassResolver {
057    
058        // NOTE: this complexity is introduced because class descriptors do not always have valid fqnames (class objects)
059        @NotNull
060        private final Map<FqNameBase, ClassDescriptor> classDescriptorCache =
061                new THashMap<FqNameBase, ClassDescriptor>(new TObjectHashingStrategy<FqNameBase>() {
062                    @Override
063                    public int computeHashCode(FqNameBase o) {
064                        if (o instanceof FqName) {
065                            return ((FqName) o).toUnsafe().hashCode();
066                        }
067                        assert o instanceof FqNameUnsafe;
068                        return o.hashCode();
069                    }
070    
071                    @Override
072                    public boolean equals(FqNameBase n1, FqNameBase n2) {
073                        return n1.equalsTo(n2.toString()) && n2.equalsTo(n1.toString());
074                    }
075                });
076    
077        @NotNull
078        private final Set<FqNameBase> unresolvedCache = Sets.newHashSet();
079    
080        private BindingTrace trace;
081        private JavaSignatureResolver signatureResolver;
082        private JavaSemanticServices semanticServices;
083        private JavaAnnotationResolver annotationResolver;
084        private PsiClassFinder psiClassFinder;
085        private JavaNamespaceResolver namespaceResolver;
086        private JavaClassObjectResolver classObjectResolver;
087        private JavaSupertypeResolver supertypesResolver;
088        private JavaFunctionResolver functionResolver;
089    
090        public JavaClassResolver() {
091        }
092    
093        @Inject
094        public void setTrace(BindingTrace trace) {
095            this.trace = trace;
096        }
097    
098        @Inject
099        public void setSignatureResolver(JavaSignatureResolver signatureResolver) {
100            this.signatureResolver = signatureResolver;
101        }
102    
103        @Inject
104        public void setClassObjectResolver(JavaClassObjectResolver classObjectResolver) {
105            this.classObjectResolver = classObjectResolver;
106        }
107    
108        @Inject
109        public void setSemanticServices(JavaSemanticServices semanticServices) {
110            this.semanticServices = semanticServices;
111        }
112    
113        @Inject
114        public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
115            this.annotationResolver = annotationResolver;
116        }
117    
118        @Inject
119        public void setPsiClassFinder(PsiClassFinder psiClassFinder) {
120            this.psiClassFinder = psiClassFinder;
121        }
122    
123        @Inject
124        public void setNamespaceResolver(JavaNamespaceResolver namespaceResolver) {
125            this.namespaceResolver = namespaceResolver;
126        }
127    
128        @Inject
129        public void setSupertypesResolver(JavaSupertypeResolver supertypesResolver) {
130            this.supertypesResolver = supertypesResolver;
131        }
132    
133        @Inject
134        public void setFunctionResolver(JavaFunctionResolver functionResolver) {
135            this.functionResolver = functionResolver;
136        }
137    
138        @Nullable
139        public ClassDescriptor resolveClass(@NotNull FqName qualifiedName, @NotNull DescriptorSearchRule searchRule) {
140            PostponedTasks postponedTasks = new PostponedTasks();
141            ClassDescriptor classDescriptor = resolveClass(qualifiedName, searchRule, postponedTasks);
142            postponedTasks.performTasks();
143            return classDescriptor;
144        }
145    
146        @Nullable
147        public ClassDescriptor resolveClass(
148                @NotNull FqName qualifiedName,
149                @NotNull DescriptorSearchRule searchRule,
150                @NotNull PostponedTasks tasks
151        ) {
152            if (isTraitImplementation(qualifiedName)) {
153                return null;
154            }
155    
156            ClassDescriptor builtinClassDescriptor = semanticServices.getKotlinBuiltinClassDescriptor(qualifiedName);
157            if (builtinClassDescriptor != null) {
158                return builtinClassDescriptor;
159            }
160    
161            // First, let's check that this is a real Java class, not a Java's view on a Kotlin class:
162            ClassDescriptor kotlinClassDescriptor = semanticServices.getKotlinClassDescriptor(qualifiedName);
163            if (kotlinClassDescriptor != null) {
164                return searchRule.processFoundInKotlin(kotlinClassDescriptor);
165            }
166    
167            // Not let's take a descriptor of a Java class
168            FqNameUnsafe fqName = javaClassToKotlinFqName(qualifiedName);
169            ClassDescriptor cachedDescriptor = classDescriptorCache.get(fqName);
170            if (cachedDescriptor != null) {
171                return cachedDescriptor;
172            }
173    
174            if (unresolvedCache.contains(fqName)) {
175                return null;
176            }
177    
178            return doResolveClass(qualifiedName, tasks);
179        }
180    
181        private ClassDescriptor doResolveClass(@NotNull FqName qualifiedName, @NotNull PostponedTasks tasks) {
182            PsiClass psiClass = psiClassFinder.findPsiClass(qualifiedName, PsiClassFinder.RuntimeClassesHandleMode.REPORT_ERROR);
183            if (psiClass == null) {
184                cacheNegativeValue(javaClassToKotlinFqName(qualifiedName));
185                return null;
186            }
187    
188            // Class may have been resolved previously by different Java resolver instance, and we are reusing its trace
189            ClassDescriptor alreadyResolved = trace.get(BindingContext.CLASS, psiClass);
190            if (alreadyResolved != null) {
191                return alreadyResolved;
192            }
193    
194            return createJavaClassDescriptor(qualifiedName, psiClass, tasks);
195        }
196    
197        private void cacheNegativeValue(@NotNull FqNameBase qualifiedName) {
198            if (unresolvedCache.contains(qualifiedName) || classDescriptorCache.containsKey(qualifiedName)) {
199                throw new IllegalStateException("rewrite at " + qualifiedName);
200            }
201            unresolvedCache.add(qualifiedName);
202        }
203    
204        private static boolean isTraitImplementation(@NotNull FqName qualifiedName) {
205            // TODO: only if -$$TImpl class is created by Kotlin
206            return qualifiedName.asString().endsWith(JvmAbi.TRAIT_IMPL_SUFFIX);
207        }
208    
209        @NotNull
210        private ClassDescriptor createJavaClassDescriptor(
211                @NotNull FqName fqName, @NotNull PsiClass psiClass,
212                @NotNull PostponedTasks taskList
213        ) {
214    
215            checkFqNamesAreConsistent(psiClass, fqName);
216            DescriptorResolverUtils.checkPsiClassIsNotJet(psiClass);
217    
218            ClassOrNamespaceDescriptor containingDeclaration = resolveParentDescriptor(psiClass);
219            // class may be resolved during resolution of parent
220            ClassDescriptor cachedDescriptor = classDescriptorCache.get(javaClassToKotlinFqName(fqName));
221            if (cachedDescriptor != null) {
222                return cachedDescriptor;
223            }
224    
225            assert (!unresolvedCache.contains(fqName)) : "We can resolve the class, so it can't be 'unresolved' during parent resolution";
226    
227            return doCreateClassDescriptor(fqName, psiClass, taskList, containingDeclaration);
228        }
229    
230        @NotNull
231        private ClassDescriptorFromJvmBytecode doCreateClassDescriptor(
232                @NotNull FqName fqName,
233                @NotNull PsiClass psiClass,
234                @NotNull PostponedTasks taskList,
235                @NotNull ClassOrNamespaceDescriptor containingDeclaration
236        ) {
237            JetClassAnnotation jetClassAnnotation = JetClassAnnotation.get(psiClass);
238            AbiVersionUtil.checkAbiVersion(psiClass, jetClassAnnotation, trace);
239    
240            ClassKind kind = getClassKind(psiClass, jetClassAnnotation);
241            ClassPsiDeclarationProvider classData = semanticServices.getPsiDeclarationProviderFactory().createBinaryClassData(psiClass);
242            ClassDescriptorFromJvmBytecode classDescriptor = new ClassDescriptorFromJvmBytecode(
243                    containingDeclaration, kind, isInnerClass(psiClass));
244    
245            cache(javaClassToKotlinFqName(fqName), classDescriptor);
246            classDescriptor.setName(Name.identifier(psiClass.getName()));
247    
248            List<JavaSignatureResolver.TypeParameterDescriptorInitialization> typeParameterDescriptorInitializations
249                    = signatureResolver.createUninitializedClassTypeParameters(psiClass, classDescriptor);
250    
251            classDescriptor.setTypeParameterDescriptors(getTypeParametersDescriptors(typeParameterDescriptorInitializations));
252            List<JetType> supertypes = Lists.newArrayList();
253            classDescriptor.setSupertypes(supertypes);
254            classDescriptor.setVisibility(DescriptorResolverUtils.resolveVisibility(psiClass, jetClassAnnotation));
255            classDescriptor.setModality(resolveModality(psiClass, classDescriptor));
256            classDescriptor.createTypeConstructor();
257            JavaClassNonStaticMembersScope membersScope = new JavaClassNonStaticMembersScope(classDescriptor, classData, semanticServices);
258            classDescriptor.setScopeForMemberLookup(membersScope);
259            classDescriptor.setScopeForConstructorResolve(membersScope);
260    
261            String context = "class " + psiClass.getQualifiedName();
262            signatureResolver.initializeTypeParameters(typeParameterDescriptorInitializations, classDescriptor, context);
263    
264            // TODO: ugly hack: tests crash if initializeTypeParameters called with class containing proper supertypes
265            List<TypeParameterDescriptor> classTypeParameters = classDescriptor.getTypeConstructor().getParameters();
266            supertypes.addAll(supertypesResolver.getSupertypes(classDescriptor, new PsiClassWrapper(psiClass), classData, classTypeParameters));
267    
268            ClassDescriptorFromJvmBytecode classObjectDescriptor = classObjectResolver.createClassObjectDescriptor(classDescriptor, psiClass);
269            cache(DescriptorResolverUtils.getFqNameForClassObject(psiClass), classObjectDescriptor);
270            if (classObjectDescriptor != null) {
271                classDescriptor.getBuilder().setClassObjectDescriptor(classObjectDescriptor);
272            }
273    
274            classDescriptor.setAnnotations(annotationResolver.resolveAnnotations(psiClass, taskList));
275    
276            trace.record(BindingContext.CLASS, psiClass, classDescriptor);
277    
278            PsiMethod samInterfaceMethod = MembersCache.getSamInterfaceMethod(psiClass);
279            if (samInterfaceMethod != null) {
280                SimpleFunctionDescriptor abstractMethod = resolveFunctionOfSamInterface(samInterfaceMethod, classDescriptor);
281                classDescriptor.setFunctionTypeForSamInterface(SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(abstractMethod));
282            }
283    
284            return classDescriptor;
285        }
286    
287        @NotNull
288        private SimpleFunctionDescriptor resolveFunctionOfSamInterface(
289                @NotNull PsiMethod samInterfaceMethod,
290                @NotNull ClassDescriptorFromJvmBytecode samInterface
291        ) {
292            PsiClass methodContainer = samInterfaceMethod.getContainingClass();
293            assert methodContainer != null : "method container is null for " + samInterfaceMethod;
294            String containerQualifiedName = methodContainer.getQualifiedName();
295            assert containerQualifiedName != null : "qualified name is null for " + methodContainer;
296    
297            if (DescriptorUtils.getFQName(samInterface).asString().equals(containerQualifiedName)) {
298                SimpleFunctionDescriptor abstractMethod =
299                        functionResolver.resolveFunctionMutely(new PsiMethodWrapper(samInterfaceMethod), samInterface);
300                assert abstractMethod != null : "couldn't resolve method " + samInterfaceMethod;
301                return abstractMethod;
302            }
303            else {
304                return findFunctionWithMostSpecificReturnType(TypeUtils.getAllSupertypes(samInterface.getDefaultType()));
305            }
306        }
307    
308        private static SimpleFunctionDescriptor findFunctionWithMostSpecificReturnType(@NotNull Set<JetType> supertypes) {
309            List<SimpleFunctionDescriptor> candidates = Lists.newArrayList();
310            for (JetType supertype : supertypes) {
311                List<CallableMemberDescriptor> abstractMembers = SingleAbstractMethodUtils.getAbstractMembers(supertype);
312                if (!abstractMembers.isEmpty()) {
313                    candidates.add((SimpleFunctionDescriptor) abstractMembers.get(0));
314                }
315            }
316            if (candidates.isEmpty()) {
317                throw new IllegalStateException("Couldn't find abstract method in supertypes " + supertypes);
318            }
319            SimpleFunctionDescriptor currentMostSpecificType = candidates.get(0);
320            for (SimpleFunctionDescriptor candidate : candidates) {
321                if (JetTypeChecker.INSTANCE.isSubtypeOf(candidate.getReturnType(), currentMostSpecificType.getReturnType())) {
322                    currentMostSpecificType = candidate;
323                }
324            }
325            return currentMostSpecificType;
326        }
327    
328        private void cache(@NotNull FqNameBase fqName, @Nullable ClassDescriptor classDescriptor) {
329            if (classDescriptor == null) {
330                cacheNegativeValue(fqName);
331            }
332            else {
333                ClassDescriptor oldValue = classDescriptorCache.put(fqName, classDescriptor);
334                assert oldValue == null;
335            }
336        }
337    
338        @NotNull
339        private static List<TypeParameterDescriptor> getTypeParametersDescriptors(
340                @NotNull List<JavaSignatureResolver.TypeParameterDescriptorInitialization> typeParameterDescriptorInitializations
341        ) {
342            List<TypeParameterDescriptor> typeParameters = Lists.newArrayList();
343            for (JavaSignatureResolver.TypeParameterDescriptorInitialization typeParameter : typeParameterDescriptorInitializations) {
344                typeParameters.add(typeParameter.getDescriptor());
345            }
346            return typeParameters;
347        }
348    
349        @NotNull
350        private static Modality resolveModality(@NotNull PsiClass psiClass, @NotNull ClassDescriptor classDescriptor) {
351            if (classDescriptor.getKind() == ClassKind.ANNOTATION_CLASS) {
352                return Modality.FINAL;
353            }
354            return Modality.convertFromFlags(
355                    psiClass.hasModifierProperty(PsiModifier.ABSTRACT) || psiClass.isInterface(),
356                    !psiClass.hasModifierProperty(PsiModifier.FINAL));
357        }
358    
359        void checkFqNamesAreConsistent(@NotNull PsiClass psiClass, @NotNull FqName desiredFqName) {
360            String qualifiedName = psiClass.getQualifiedName();
361            assert qualifiedName != null;
362    
363            FqName fqName = new FqName(qualifiedName);
364            assert fqName.equals(desiredFqName);
365            FqNameUnsafe correctedName = javaClassToKotlinFqName(fqName);
366            if (classDescriptorCache.containsKey(correctedName) || unresolvedCache.contains(correctedName)) {
367                throw new IllegalStateException(qualifiedName);
368            }
369        }
370    
371        @NotNull
372        private ClassOrNamespaceDescriptor resolveParentDescriptor(@NotNull PsiClass psiClass) {
373            if (isContainedInClass(psiClass)) {
374                return resolveParentClass(psiClass);
375            }
376            else {
377                return resolveParentNamespace(psiClass);
378            }
379        }
380    
381        @NotNull
382        private static FqName getFqName(@NotNull PsiClass psiClass) {
383            String qualifiedName = psiClass.getQualifiedName();
384            assert qualifiedName != null;
385            return new FqName(qualifiedName);
386        }
387    
388        // This method replaces "object" segments of FQ name to "<class-object-for-...>"
389        @NotNull
390        private static FqNameUnsafe javaClassToKotlinFqName(@NotNull FqName rawFqName) {
391            List<Name> correctedSegments = new ArrayList<Name>();
392            for (Name segment : rawFqName.pathSegments()) {
393                if (JvmAbi.CLASS_OBJECT_CLASS_NAME.equals(segment.asString())) {
394                    assert !correctedSegments.isEmpty();
395                    Name previous = correctedSegments.get(correctedSegments.size() - 1);
396                    correctedSegments.add(DescriptorUtils.getClassObjectName(previous));
397                }
398                else {
399                    correctedSegments.add(segment);
400                }
401            }
402            return new FqNameUnsafe(StringUtil.join(correctedSegments, "."));
403        }
404    
405        private static boolean isContainedInClass(@NotNull PsiClass psiClass) {
406            return psiClass.getContainingClass() != null;
407        }
408    
409        private static boolean isInnerClass(@NotNull PsiClass psiClass) {
410            return isContainedInClass(psiClass) && !psiClass.hasModifierProperty(PsiModifier.STATIC);
411        }
412    
413        @NotNull
414        private ClassOrNamespaceDescriptor resolveParentClass(@NotNull PsiClass psiClass) {
415            PsiClass containingClass = psiClass.getContainingClass();
416            assert containingClass != null;
417            FqName containerFqName = getFqName(containingClass);
418            ClassDescriptor parentClass = resolveClass(containerFqName, DescriptorSearchRule.INCLUDE_KOTLIN);
419            if (parentClass == null) {
420                throw new IllegalStateException(
421                        "PsiClass not found by name " + containerFqName + ", required to be container declaration of " + getFqName(psiClass));
422            }
423            return parentClass;
424        }
425    
426        @NotNull
427        private ClassOrNamespaceDescriptor resolveParentNamespace(@NotNull PsiClass psiClass) {
428            FqName namespaceFqName = getFqName(psiClass).parent();
429            NamespaceDescriptor parentNamespace = namespaceResolver.resolveNamespace(namespaceFqName, DescriptorSearchRule.INCLUDE_KOTLIN);
430            if (parentNamespace == null) {
431                throw new IllegalStateException("cannot resolve namespace " + namespaceFqName +
432                                                ", required to be container for " + getFqName(psiClass));
433            }
434            return parentNamespace;
435        }
436    
437        @NotNull
438        private static ClassKind getClassKind(@NotNull PsiClass psiClass, @NotNull JetClassAnnotation jetClassAnnotation) {
439            if (psiClass.isInterface()) {
440                return (psiClass.isAnnotationType() ? ClassKind.ANNOTATION_CLASS : ClassKind.TRAIT);
441            }
442            if (psiClass.isEnum()) {
443                return ClassKind.ENUM_CLASS;
444            }
445            else {
446                return jetClassAnnotation.kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT ? ClassKind.OBJECT : ClassKind.CLASS;
447            }
448        }
449    
450        @Nullable
451        public ClassDescriptor resolveClass(FqName name) {
452            return resolveClass(name, DescriptorSearchRule.ERROR_IF_FOUND_IN_KOTLIN);
453        }
454    }