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 }