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 }