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