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