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 017package org.jetbrains.jet.lang.resolve.java.resolver; 018 019import com.google.common.collect.Lists; 020import com.google.common.collect.Sets; 021import com.intellij.openapi.util.text.StringUtil; 022import com.intellij.psi.PsiClass; 023import com.intellij.psi.PsiMethod; 024import com.intellij.psi.PsiModifier; 025import gnu.trove.THashMap; 026import gnu.trove.TObjectHashingStrategy; 027import org.jetbrains.annotations.NotNull; 028import org.jetbrains.annotations.Nullable; 029import org.jetbrains.jet.lang.descriptors.*; 030import org.jetbrains.jet.lang.resolve.BindingContext; 031import org.jetbrains.jet.lang.resolve.BindingTrace; 032import org.jetbrains.jet.lang.resolve.DescriptorUtils; 033import org.jetbrains.jet.lang.resolve.java.*; 034import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode; 035import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation; 036import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider; 037import org.jetbrains.jet.lang.resolve.java.provider.MembersCache; 038import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils; 039import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope; 040import org.jetbrains.jet.lang.resolve.java.wrapper.PsiClassWrapper; 041import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper; 042import org.jetbrains.jet.lang.resolve.name.FqName; 043import org.jetbrains.jet.lang.resolve.name.FqNameBase; 044import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe; 045import org.jetbrains.jet.lang.resolve.name.Name; 046import org.jetbrains.jet.lang.types.JetType; 047import org.jetbrains.jet.lang.types.TypeUtils; 048import org.jetbrains.jet.lang.types.checker.JetTypeChecker; 049 050import javax.inject.Inject; 051import java.util.ArrayList; 052import java.util.List; 053import java.util.Map; 054import java.util.Set; 055 056public 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}