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 org.jetbrains.annotations.NotNull;
020    import org.jetbrains.annotations.Nullable;
021    import org.jetbrains.jet.lang.descriptors.*;
022    import org.jetbrains.jet.lang.resolve.DescriptorFactory;
023    import org.jetbrains.jet.lang.resolve.DescriptorUtils;
024    import org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule;
025    import org.jetbrains.jet.lang.resolve.java.JavaClassFinder;
026    import org.jetbrains.jet.lang.resolve.java.JvmAbi;
027    import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
028    import org.jetbrains.jet.lang.resolve.java.descriptor.JavaEnumClassObjectDescriptor;
029    import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
030    import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope;
031    import org.jetbrains.jet.lang.resolve.java.scope.JavaEnumClassObjectScope;
032    import org.jetbrains.jet.lang.resolve.java.structure.JavaClass;
033    import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
034    import org.jetbrains.jet.lang.resolve.kotlin.DeserializedDescriptorResolver;
035    import org.jetbrains.jet.lang.resolve.kotlin.KotlinClassFinder;
036    import org.jetbrains.jet.lang.resolve.kotlin.KotlinJvmBinaryClass;
037    import org.jetbrains.jet.lang.resolve.name.FqName;
038    import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
039    import org.jetbrains.jet.lang.resolve.name.Name;
040    import org.jetbrains.jet.lang.resolve.scopes.JetScope;
041    import org.jetbrains.jet.lang.resolve.scopes.RedeclarationHandler;
042    import org.jetbrains.jet.lang.resolve.scopes.WritableScope;
043    import org.jetbrains.jet.lang.resolve.scopes.WritableScopeImpl;
044    import org.jetbrains.jet.lang.types.JetType;
045    import org.jetbrains.jet.lang.types.TypeUtils;
046    import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
047    import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
048    
049    import javax.inject.Inject;
050    import java.util.*;
051    
052    import static org.jetbrains.jet.lang.resolve.DescriptorUtils.getClassObjectName;
053    import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.INCLUDE_KOTLIN_SOURCES;
054    
055    public final class JavaClassResolver {
056        @NotNull
057        private final Map<FqNameUnsafe, ClassDescriptor> classDescriptorCache = new HashMap<FqNameUnsafe, ClassDescriptor>();
058    
059        @NotNull
060        private final Set<FqNameUnsafe> unresolvedCache = new HashSet<FqNameUnsafe>();
061    
062        private JavaResolverCache cache;
063        private JavaTypeParameterResolver typeParameterResolver;
064        private JavaMemberResolver memberResolver;
065        private JavaAnnotationResolver annotationResolver;
066        private JavaClassFinder javaClassFinder;
067        private JavaNamespaceResolver namespaceResolver;
068        private JavaSupertypeResolver supertypesResolver;
069        private JavaFunctionResolver functionResolver;
070        private DeserializedDescriptorResolver deserializedDescriptorResolver;
071        private KotlinClassFinder kotlinClassFinder;
072    
073        public JavaClassResolver() {
074        }
075    
076        @Inject
077        public void setCache(JavaResolverCache cache) {
078            this.cache = cache;
079        }
080    
081        @Inject
082        public void setDeserializedDescriptorResolver(DeserializedDescriptorResolver deserializedDescriptorResolver) {
083            this.deserializedDescriptorResolver = deserializedDescriptorResolver;
084        }
085    
086        @Inject
087        public void setTypeParameterResolver(JavaTypeParameterResolver typeParameterResolver) {
088            this.typeParameterResolver = typeParameterResolver;
089        }
090    
091        @Inject
092        public void setMemberResolver(JavaMemberResolver memberResolver) {
093            this.memberResolver = memberResolver;
094        }
095    
096        @Inject
097        public void setAnnotationResolver(JavaAnnotationResolver annotationResolver) {
098            this.annotationResolver = annotationResolver;
099        }
100    
101        @Inject
102        public void setJavaClassFinder(JavaClassFinder javaClassFinder) {
103            this.javaClassFinder = javaClassFinder;
104        }
105    
106        @Inject
107        public void setNamespaceResolver(JavaNamespaceResolver namespaceResolver) {
108            this.namespaceResolver = namespaceResolver;
109        }
110    
111        @Inject
112        public void setSupertypesResolver(JavaSupertypeResolver supertypesResolver) {
113            this.supertypesResolver = supertypesResolver;
114        }
115    
116        @Inject
117        public void setFunctionResolver(JavaFunctionResolver functionResolver) {
118            this.functionResolver = functionResolver;
119        }
120    
121        @Inject
122        public void setKotlinClassFinder(KotlinClassFinder kotlinClassFinder) {
123            this.kotlinClassFinder = kotlinClassFinder;
124        }
125    
126        @Nullable
127        public ClassDescriptor resolveClass(@NotNull FqName qualifiedName, @NotNull DescriptorSearchRule searchRule) {
128            PostponedTasks postponedTasks = new PostponedTasks();
129            ClassDescriptor classDescriptor = resolveClass(qualifiedName, searchRule, postponedTasks);
130            postponedTasks.performTasks();
131            return classDescriptor;
132        }
133    
134        @Nullable
135        public ClassDescriptor resolveClass(
136                @NotNull FqName qualifiedName,
137                @NotNull DescriptorSearchRule searchRule,
138                @NotNull PostponedTasks tasks
139        ) {
140            if (isTraitImplementation(qualifiedName)) {
141                return null;
142            }
143    
144            ClassDescriptor builtinClassDescriptor = getKotlinBuiltinClassDescriptor(qualifiedName);
145            if (builtinClassDescriptor != null) {
146                return builtinClassDescriptor;
147            }
148    
149            if (searchRule == INCLUDE_KOTLIN_SOURCES) {
150                ClassDescriptor kotlinClassDescriptor = cache.getClassResolvedFromSource(qualifiedName);
151                if (kotlinClassDescriptor != null) {
152                    return kotlinClassDescriptor;
153                }
154            }
155    
156            FqNameUnsafe fqName = javaClassToKotlinFqName(qualifiedName);
157            ClassDescriptor cachedDescriptor = classDescriptorCache.get(fqName);
158            if (cachedDescriptor != null) {
159                return cachedDescriptor;
160            }
161    
162            if (unresolvedCache.contains(fqName)) {
163                return null;
164            }
165    
166            return doResolveClass(qualifiedName, tasks);
167        }
168    
169        @Nullable
170        private static ClassDescriptor getKotlinBuiltinClassDescriptor(@NotNull FqName qualifiedName) {
171            if (!qualifiedName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) return null;
172    
173            List<Name> segments = qualifiedName.pathSegments();
174            if (segments.size() < 2) return null;
175    
176            JetScope scope = KotlinBuiltIns.getInstance().getBuiltInsScope();
177            for (int i = 1, size = segments.size(); i < size; i++) {
178                ClassifierDescriptor classifier = scope.getClassifier(segments.get(i));
179                if (classifier == null) return null;
180                assert classifier instanceof ClassDescriptor : "Unexpected classifier in built-ins: " + classifier;
181                scope = ((ClassDescriptor) classifier).getUnsubstitutedInnerClassesScope();
182            }
183    
184            return (ClassDescriptor) scope.getContainingDeclaration();
185        }
186    
187        private ClassDescriptor doResolveClass(@NotNull FqName qualifiedName, @NotNull PostponedTasks tasks) {
188            //TODO: correct scope
189            KotlinJvmBinaryClass kotlinClass = kotlinClassFinder.find(qualifiedName);
190            if (kotlinClass != null) {
191                ClassDescriptor deserializedDescriptor = deserializedDescriptorResolver.resolveClass(kotlinClass);
192                if (deserializedDescriptor != null) {
193                    cache(javaClassToKotlinFqName(qualifiedName), deserializedDescriptor);
194                    return deserializedDescriptor;
195                }
196            }
197    
198            JavaClass javaClass = javaClassFinder.findClass(qualifiedName);
199            if (javaClass == null) {
200                cacheNegativeValue(javaClassToKotlinFqName(qualifiedName));
201                return null;
202            }
203    
204            // Class may have been resolved previously by different Java resolver instance, and we are reusing its trace
205            ClassDescriptor alreadyResolved = cache.getClass(javaClass);
206            if (alreadyResolved != null) {
207                return alreadyResolved;
208            }
209    
210            ClassOrNamespaceDescriptor containingDeclaration = resolveParentDescriptor(qualifiedName, javaClass.getOuterClass() != null);
211            // class may be resolved during resolution of parent
212            ClassDescriptor cachedDescriptor = classDescriptorCache.get(javaClassToKotlinFqName(qualifiedName));
213            if (cachedDescriptor != null) {
214                return cachedDescriptor;
215            }
216            assert !unresolvedCache.contains(qualifiedName.toUnsafe())
217                    : "We can resolve the class, so it can't be 'unresolved' during parent resolution";
218    
219            checkFqNamesAreConsistent(javaClass, qualifiedName);
220    
221            assert javaClass.getOriginKind() != JavaClass.OriginKind.KOTLIN_LIGHT_CLASS :
222                    "Trying to resolve a light class as a regular PsiClass: " + javaClass.getFqName();
223    
224            return doCreateClassDescriptor(qualifiedName, javaClass, tasks, containingDeclaration);
225        }
226    
227        private void cacheNegativeValue(@NotNull FqNameUnsafe fqNameUnsafe) {
228            if (unresolvedCache.contains(fqNameUnsafe) || classDescriptorCache.containsKey(fqNameUnsafe)) {
229                throw new IllegalStateException("rewrite at " + fqNameUnsafe);
230            }
231            unresolvedCache.add(fqNameUnsafe);
232        }
233    
234        private static boolean isTraitImplementation(@NotNull FqName qualifiedName) {
235            // TODO: only if -$$TImpl class is created by Kotlin
236            return qualifiedName.asString().endsWith(JvmAbi.TRAIT_IMPL_SUFFIX);
237        }
238    
239        @NotNull
240        private ClassDescriptorFromJvmBytecode doCreateClassDescriptor(
241                @NotNull FqName fqName,
242                @NotNull JavaClass javaClass,
243                @NotNull PostponedTasks taskList,
244                @NotNull ClassOrNamespaceDescriptor containingDeclaration
245        ) {
246            ClassDescriptorFromJvmBytecode classDescriptor =
247                    new ClassDescriptorFromJvmBytecode(containingDeclaration, javaClass.getName(), determineClassKind(javaClass), isInnerClass(javaClass));
248    
249            cache(javaClassToKotlinFqName(fqName), classDescriptor);
250    
251    
252            JavaTypeParameterResolver.Initializer typeParameterInitializer = typeParameterResolver.resolveTypeParameters(classDescriptor, javaClass);
253            classDescriptor.setTypeParameterDescriptors(typeParameterInitializer.getDescriptors());
254    
255            List<JetType> supertypes = new ArrayList<JetType>();
256            classDescriptor.setSupertypes(supertypes);
257            classDescriptor.setVisibility(javaClass.getVisibility());
258            classDescriptor.setModality(determineClassModality(javaClass));
259            classDescriptor.createTypeConstructor();
260    
261            JavaClassNonStaticMembersScope scope = new JavaClassNonStaticMembersScope(classDescriptor, javaClass, memberResolver);
262            classDescriptor.setScopeForMemberLookup(scope);
263            classDescriptor.setScopeForConstructorResolve(scope);
264    
265            typeParameterInitializer.initialize();
266    
267            // TODO: ugly hack: tests crash if initializeTypeParameters called with class containing proper supertypes
268            List<TypeParameterDescriptor> classTypeParameters = classDescriptor.getTypeConstructor().getParameters();
269            supertypes.addAll(supertypesResolver.getSupertypes(classDescriptor, javaClass, classTypeParameters));
270    
271            if (javaClass.isEnum()) {
272                JavaEnumClassObjectDescriptor enumClassObject = createEnumClassObject(classDescriptor, javaClass);
273                createEnumSyntheticMethods(enumClassObject, classDescriptor.getDefaultType());
274                cache(getFqNameForClassObject(javaClass), enumClassObject);
275                classDescriptor.getBuilder().setClassObjectDescriptor(enumClassObject);
276            }
277    
278            classDescriptor.setAnnotations(annotationResolver.resolveAnnotations(javaClass, taskList));
279    
280            cache.recordClass(javaClass, classDescriptor);
281    
282            JavaMethod samInterfaceMethod = SingleAbstractMethodUtils.getSamInterfaceMethod(javaClass);
283            if (samInterfaceMethod != null) {
284                SimpleFunctionDescriptor abstractMethod = resolveFunctionOfSamInterface(samInterfaceMethod, classDescriptor);
285                classDescriptor.setFunctionTypeForSamInterface(SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(abstractMethod));
286            }
287    
288            return classDescriptor;
289        }
290    
291        @NotNull
292        private static ClassKind determineClassKind(@NotNull JavaClass klass) {
293            if (klass.isInterface()) {
294                return klass.isAnnotationType() ? ClassKind.ANNOTATION_CLASS : ClassKind.TRAIT;
295            }
296            return klass.isEnum() ? ClassKind.ENUM_CLASS : ClassKind.CLASS;
297        }
298    
299        @NotNull
300        private static Modality determineClassModality(@NotNull JavaClass klass) {
301            return klass.isAnnotationType()
302                   ? Modality.FINAL
303                   : Modality.convertFromFlags(klass.isAbstract() || klass.isInterface(), !klass.isFinal());
304        }
305    
306        @NotNull
307        private static FqNameUnsafe getFqNameForClassObject(@NotNull JavaClass javaClass) {
308            FqName fqName = javaClass.getFqName();
309            assert fqName != null : "Reading java class with no qualified name";
310            return fqName.toUnsafe().child(getClassObjectName(javaClass.getName()));
311        }
312    
313        @NotNull
314        private SimpleFunctionDescriptor resolveFunctionOfSamInterface(
315                @NotNull JavaMethod samInterfaceMethod,
316                @NotNull ClassDescriptorFromJvmBytecode samInterface
317        ) {
318            JavaClass methodContainer = samInterfaceMethod.getContainingClass();
319            FqName containerFqName = methodContainer.getFqName();
320            assert containerFqName != null : "qualified name is null for " + methodContainer;
321    
322            if (DescriptorUtils.getFQName(samInterface).equalsTo(containerFqName)) {
323                SimpleFunctionDescriptor abstractMethod = functionResolver.resolveFunctionMutely(samInterfaceMethod, samInterface);
324                assert abstractMethod != null : "couldn't resolve method " + samInterfaceMethod;
325                return abstractMethod;
326            }
327            else {
328                return findFunctionWithMostSpecificReturnType(TypeUtils.getAllSupertypes(samInterface.getDefaultType()));
329            }
330        }
331    
332        @NotNull
333        private static SimpleFunctionDescriptor findFunctionWithMostSpecificReturnType(@NotNull Set<JetType> supertypes) {
334            List<SimpleFunctionDescriptor> candidates = new ArrayList<SimpleFunctionDescriptor>(supertypes.size());
335            for (JetType supertype : supertypes) {
336                List<CallableMemberDescriptor> abstractMembers = SingleAbstractMethodUtils.getAbstractMembers(supertype);
337                if (!abstractMembers.isEmpty()) {
338                    candidates.add((SimpleFunctionDescriptor) abstractMembers.get(0));
339                }
340            }
341            if (candidates.isEmpty()) {
342                throw new IllegalStateException("Couldn't find abstract method in supertypes " + supertypes);
343            }
344            SimpleFunctionDescriptor currentMostSpecificType = candidates.get(0);
345            for (SimpleFunctionDescriptor candidate : candidates) {
346                JetType candidateReturnType = candidate.getReturnType();
347                JetType currentMostSpecificReturnType = currentMostSpecificType.getReturnType();
348                assert candidateReturnType != null && currentMostSpecificReturnType != null : candidate + ", " + currentMostSpecificReturnType;
349                if (JetTypeChecker.INSTANCE.isSubtypeOf(candidateReturnType, currentMostSpecificReturnType)) {
350                    currentMostSpecificType = candidate;
351                }
352            }
353            return currentMostSpecificType;
354        }
355    
356        private void cache(@NotNull FqNameUnsafe fqName, @Nullable ClassDescriptor classDescriptor) {
357            if (classDescriptor == null) {
358                cacheNegativeValue(fqName);
359            }
360            else {
361                ClassDescriptor oldValue = classDescriptorCache.put(fqName, classDescriptor);
362                assert oldValue == null;
363            }
364        }
365    
366        private void checkFqNamesAreConsistent(@NotNull JavaClass javaClass, @NotNull FqName desiredFqName) {
367            FqName fqName = javaClass.getFqName();
368            assert desiredFqName.equals(fqName) : "Inconsistent FQ names: " + fqName + ", " + desiredFqName;
369            FqNameUnsafe correctedName = javaClassToKotlinFqName(fqName);
370            if (classDescriptorCache.containsKey(correctedName) || unresolvedCache.contains(correctedName)) {
371                throw new IllegalStateException("Cache already contains FQ name: " + fqName.asString());
372            }
373        }
374    
375        @NotNull
376        private ClassOrNamespaceDescriptor resolveParentDescriptor(@NotNull FqName childClassFQName, boolean isInnerClass) {
377            FqName parentFqName = childClassFQName.parent();
378            if (isInnerClass) {
379                ClassDescriptor parentClass = resolveClass(parentFqName, INCLUDE_KOTLIN_SOURCES);
380                if (parentClass == null) {
381                    throw new IllegalStateException("Could not resolve " + parentFqName + " required to be parent for " + childClassFQName);
382                }
383                return parentClass;
384            }
385            else {
386                NamespaceDescriptor parentNamespace = namespaceResolver.resolveNamespace(parentFqName, INCLUDE_KOTLIN_SOURCES);
387                if (parentNamespace == null) {
388                    throw new IllegalStateException("Could not resolve " + parentFqName + " required to be parent for " + childClassFQName);
389                }
390                return parentNamespace;
391            }
392        }
393    
394        // This method replaces "object" segments of FQ name to "<class-object-for-...>"
395        @NotNull
396        private static FqNameUnsafe javaClassToKotlinFqName(@NotNull FqName rawFqName) {
397            List<Name> correctedSegments = new ArrayList<Name>();
398            for (Name segment : rawFqName.pathSegments()) {
399                if (JvmAbi.CLASS_OBJECT_CLASS_NAME.equals(segment.asString())) {
400                    assert !correctedSegments.isEmpty();
401                    Name previous = correctedSegments.get(correctedSegments.size() - 1);
402                    correctedSegments.add(DescriptorUtils.getClassObjectName(previous));
403                }
404                else {
405                    correctedSegments.add(segment);
406                }
407            }
408            return FqNameUnsafe.fromSegments(correctedSegments);
409        }
410    
411        private static boolean isInnerClass(@NotNull JavaClass javaClass) {
412            return javaClass.getOuterClass() != null && !javaClass.isStatic();
413        }
414    
415        private static void createEnumSyntheticMethods(@NotNull JavaEnumClassObjectDescriptor classObject, @NotNull JetType enumType) {
416            JetType valuesReturnType = KotlinBuiltIns.getInstance().getArrayType(enumType);
417            SimpleFunctionDescriptor valuesMethod = DescriptorFactory.createEnumClassObjectValuesMethod(classObject, valuesReturnType);
418            classObject.getBuilder().addFunctionDescriptor(valuesMethod);
419    
420            SimpleFunctionDescriptor valueOfMethod = DescriptorFactory.createEnumClassObjectValueOfMethod(classObject, enumType);
421            classObject.getBuilder().addFunctionDescriptor(valueOfMethod);
422        }
423    
424        @NotNull
425        private JavaEnumClassObjectDescriptor createEnumClassObject(@NotNull ClassDescriptor enumClass, @NotNull JavaClass javaClass) {
426            JavaEnumClassObjectDescriptor classObject = new JavaEnumClassObjectDescriptor(enumClass);
427    
428            classObject.setModality(Modality.FINAL);
429            classObject.setVisibility(enumClass.getVisibility());
430            classObject.setTypeParameterDescriptors(Collections.<TypeParameterDescriptor>emptyList());
431            classObject.createTypeConstructor();
432    
433            JavaEnumClassObjectScope scope = new JavaEnumClassObjectScope(classObject, javaClass, memberResolver);
434            WritableScopeImpl writableScope =
435                    new WritableScopeImpl(scope, classObject, RedeclarationHandler.THROW_EXCEPTION, "Enum class object scope");
436            writableScope.changeLockLevel(WritableScope.LockLevel.BOTH);
437            classObject.setScopeForMemberLookup(writableScope);
438    
439            return classObject;
440        }
441    }