Move SAM utils from MembersCache to SAMUtils

This commit is contained in:
Alexander Udalov
2013-07-25 17:28:26 +04:00
committed by Pavel V. Talanov
parent c5e37cc3de
commit fb07a4c1ab
6 changed files with 120 additions and 123 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.jet.lang.resolve.java;
import com.google.common.collect.ImmutableSet;
import com.intellij.psi.*;
import com.intellij.psi.impl.compiled.ClsClassImpl;
import com.intellij.psi.util.PsiFormatUtil;
@@ -36,6 +37,8 @@ import static com.intellij.psi.util.PsiFormatUtilBase.*;
import static org.jetbrains.jet.lang.resolve.DescriptorUtils.isEnumClassObject;
public final class DescriptorResolverUtils {
private static final ImmutableSet<String> OBJECT_METHODS = ImmutableSet.of("hashCode()", "equals(java.lang.Object)", "toString()");
private DescriptorResolverUtils() {
}
@@ -144,4 +147,24 @@ public final class DescriptorResolverUtils {
}
};
}
public static boolean isObjectMethodInInterface(@NotNull PsiMember member) {
if (!(member instanceof PsiMethod)) {
return false;
}
PsiClass containingClass = member.getContainingClass();
assert containingClass != null : "containing class is null for " + member;
if (!containingClass.isInterface()) {
return false;
}
return isObjectMethod((PsiMethod) member);
}
public static boolean isObjectMethod(@NotNull PsiMethod method) {
String formattedMethod = PsiFormatUtil.formatMethod(
method, PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES);
return OBJECT_METHODS.contains(formattedMethod);
}
}
@@ -37,7 +37,6 @@ import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.*;
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.java.provider.MembersCache;
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
@@ -237,7 +236,7 @@ public class SignaturesPropagationData {
: findSuperFunction(superclassToFunctions.get(classFqName), superMethod);
if (superFun == null) {
// Super methods which are Object methods in interfaces are not loaded by JDR.
if (!MembersCache.isObjectMethodInInterface(superMethod)) {
if (!DescriptorResolverUtils.isObjectMethodInInterface(superMethod)) {
reportCantFindSuperFunction(method);
}
continue;
@@ -17,33 +17,25 @@
package org.jetbrains.jet.lang.resolve.java.provider;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Multimap;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.psi.util.PsiFormatUtil;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
import org.jetbrains.jet.lang.resolve.java.JetJavaMirrorMarker;
import org.jetbrains.jet.lang.resolve.java.PsiClassFinder;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiFieldWrapper;
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMemberWrapper;
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static com.intellij.psi.util.MethodSignatureUtil.areSignaturesErasureEqual;
import static com.intellij.psi.util.PsiFormatUtilBase.*;
public final class MembersCache {
private static final ImmutableSet<String> OBJECT_METHODS = ImmutableSet.of("hashCode()", "equals(java.lang.Object)", "toString()");
private final Multimap<Name, Runnable> memberProcessingTasks = HashMultimap.create();
private final Map<Name, NamedMembers> namedMembersMap = new HashMap<Name, NamedMembers>();
@@ -128,7 +120,7 @@ public final class MembersCache {
private void process() {
for (PsiClass psiClass : psiClasses) {
if (!(psiClass instanceof JetJavaMirrorMarker)) { // to filter out JetLightClasses
if (isSamInterface(psiClass)) {
if (SingleAbstractMethodUtils.isSamInterface(psiClass)) {
processSamInterface(psiClass);
}
}
@@ -221,7 +213,7 @@ public final class MembersCache {
return false;
}
if (isObjectMethodInInterface(member.getPsiMember())) {
if (DescriptorResolverUtils.isObjectMethodInInterface(member.getPsiMember())) {
return false;
}
@@ -253,67 +245,13 @@ public final class MembersCache {
}
private void processNestedClass(PsiClass nested) {
if (isSamInterface(nested)) {
if (SingleAbstractMethodUtils.isSamInterface(nested)) {
NamedMembers namedMembers = getOrCreateEmpty(Name.identifier(nested.getName()));
namedMembers.setSamInterface(nested);
}
}
}
public static boolean isObjectMethodInInterface(@NotNull PsiMember member) {
if (!(member instanceof PsiMethod)) {
return false;
}
PsiClass containingClass = member.getContainingClass();
assert containingClass != null : "containing class is null for " + member;
if (!containingClass.isInterface()) {
return false;
}
return isObjectMethod((PsiMethod) member);
}
private static boolean isObjectMethod(PsiMethod method) {
String formattedMethod = PsiFormatUtil.formatMethod(
method, PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES);
return OBJECT_METHODS.contains(formattedMethod);
}
public static boolean isSamInterface(@NotNull PsiClass psiClass) {
return getSamInterfaceMethod(psiClass) != null;
}
// Returns null if not SAM interface
@Nullable
public static PsiMethod getSamInterfaceMethod(@NotNull PsiClass psiClass) {
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName == null || qualifiedName.startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.asString() + ".")) {
return null;
}
if (!psiClass.isInterface() || psiClass.isAnnotationType()) {
return null;
}
return findOnlyAbstractMethod(psiClass);
}
@Nullable
private static PsiMethod findOnlyAbstractMethod(@NotNull PsiClass psiClass) {
PsiClassType classType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
if (finder.find(classType)) {
return finder.getFoundMethod();
}
return null;
}
private static boolean isVarargMethod(@NotNull PsiMethod method) {
PsiParameter lastParameter = ArrayUtil.getLastElement(method.getParameterList().getParameters());
return lastParameter != null && lastParameter.getType() instanceof PsiEllipsisType;
}
private static abstract class RunOnce implements Runnable {
private boolean hasRun = false;
@@ -326,53 +264,4 @@ public final class MembersCache {
protected abstract void doRun();
}
private static class OnlyAbstractMethodFinder {
private MethodSignatureBackedByPsiMethod found;
private boolean find(@NotNull PsiClassType classType) {
PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
PsiSubstitutor classSubstitutor = classResolveResult.getSubstitutor();
PsiClass psiClass = classResolveResult.getElement();
if (psiClass == null) {
return false; // can't resolve class -> not a SAM interface
}
if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
return true;
}
for (PsiMethod method : psiClass.getMethods()) {
if (isObjectMethod(method)) { // e.g., ignore toString() declared in interface
continue;
}
if (method.hasTypeParameters()) {
return false; // if interface has generic methods, it is not a SAM interface
}
if (found == null) {
found = (MethodSignatureBackedByPsiMethod) method.getSignature(classSubstitutor);
continue;
}
if (!found.getName().equals(method.getName())) {
return false; // optimizing heuristic
}
MethodSignatureBackedByPsiMethod current = (MethodSignatureBackedByPsiMethod) method.getSignature(classSubstitutor);
if (!areSignaturesErasureEqual(current, found) || isVarargMethod(method) != isVarargMethod(found.getMethod())) {
return false; // different signatures
}
}
for (PsiType t : classType.getSuperTypes()) {
if (!find((PsiClassType) t)) {
return false;
}
}
return true;
}
@Nullable
PsiMethod getFoundMethod() {
return found == null ? null : found.getMethod();
}
}
}
@@ -34,7 +34,6 @@ import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.*;
import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
import org.jetbrains.jet.lang.resolve.java.provider.MembersCache;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope;
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
@@ -312,7 +311,7 @@ public final class JavaClassResolver {
trace.record(BindingContext.CLASS, psiClass, classDescriptor);
PsiMethod samInterfaceMethod = MembersCache.getSamInterfaceMethod(psiClass);
PsiMethod samInterfaceMethod = SingleAbstractMethodUtils.getSamInterfaceMethod(psiClass);
if (samInterfaceMethod != null) {
SimpleFunctionDescriptor abstractMethod = resolveFunctionOfSamInterface(samInterfaceMethod, classDescriptor);
classDescriptor.setFunctionTypeForSamInterface(SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(abstractMethod));
@@ -37,7 +37,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.*;
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaNamespaceDescriptor;
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.java.provider.MembersCache;
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
import org.jetbrains.jet.lang.resolve.java.scope.JavaBaseScope;
import org.jetbrains.jet.lang.resolve.java.scope.JavaClassStaticMembersScope;
import org.jetbrains.jet.lang.resolve.java.scope.JavaPackageScope;
@@ -250,7 +250,7 @@ public final class JavaNamespaceResolver {
}
for (PsiClass nestedClass : psiClass.getInnerClasses()) {
if (MembersCache.isSamInterface(nestedClass)) {
if (SingleAbstractMethodUtils.isSamInterface(nestedClass)) {
return true;
}
if (nestedClass.hasModifierProperty(PsiModifier.STATIC) && hasStaticMembers(nestedClass)) {
@@ -17,6 +17,9 @@
package org.jetbrains.jet.lang.resolve.java.sam;
import com.google.common.collect.Lists;
import com.intellij.psi.*;
import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
import com.intellij.util.ArrayUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
@@ -25,7 +28,7 @@ import org.jetbrains.jet.lang.descriptors.impl.ConstructorDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.jet.lang.resolve.calls.CallResolverUtil;
import org.jetbrains.jet.lang.resolve.java.DescriptorResolverUtils;
import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
import org.jetbrains.jet.lang.resolve.java.kotlinSignature.SignaturesUtil;
import org.jetbrains.jet.lang.resolve.name.Name;
@@ -37,6 +40,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import static com.intellij.psi.util.MethodSignatureUtil.areSignaturesErasureEqual;
import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
public class SingleAbstractMethodUtils {
@@ -311,6 +315,40 @@ public class SingleAbstractMethodUtils {
private SingleAbstractMethodUtils() {
}
public static boolean isSamInterface(@NotNull PsiClass psiClass) {
return getSamInterfaceMethod(psiClass) != null;
}
// Returns null if not SAM interface
@Nullable
public static PsiMethod getSamInterfaceMethod(@NotNull PsiClass psiClass) {
String qualifiedName = psiClass.getQualifiedName();
if (qualifiedName == null || qualifiedName.startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.asString() + ".")) {
return null;
}
if (!psiClass.isInterface() || psiClass.isAnnotationType()) {
return null;
}
return findOnlyAbstractMethod(psiClass);
}
@Nullable
private static PsiMethod findOnlyAbstractMethod(@NotNull PsiClass psiClass) {
PsiClassType classType = JavaPsiFacade.getElementFactory(psiClass.getProject()).createType(psiClass);
OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
if (finder.find(classType)) {
return finder.getFoundMethod();
}
return null;
}
private static boolean isVarargMethod(@NotNull PsiMethod method) {
PsiParameter lastParameter = ArrayUtil.getLastElement(method.getParameterList().getParameters());
return lastParameter != null && lastParameter.getType() instanceof PsiEllipsisType;
}
private static class TypeParameters {
public final List<TypeParameterDescriptor> descriptors;
public final TypeSubstitutor substitutor;
@@ -328,4 +366,53 @@ public class SingleAbstractMethodUtils {
@Nullable JetType returnType
);
}
private static class OnlyAbstractMethodFinder {
private MethodSignatureBackedByPsiMethod found;
private boolean find(@NotNull PsiClassType classType) {
PsiClassType.ClassResolveResult classResolveResult = classType.resolveGenerics();
PsiSubstitutor classSubstitutor = classResolveResult.getSubstitutor();
PsiClass psiClass = classResolveResult.getElement();
if (psiClass == null) {
return false; // can't resolve class -> not a SAM interface
}
if (CommonClassNames.JAVA_LANG_OBJECT.equals(psiClass.getQualifiedName())) {
return true;
}
for (PsiMethod method : psiClass.getMethods()) {
if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
continue;
}
if (method.hasTypeParameters()) {
return false; // if interface has generic methods, it is not a SAM interface
}
if (found == null) {
found = (MethodSignatureBackedByPsiMethod) method.getSignature(classSubstitutor);
continue;
}
if (!found.getName().equals(method.getName())) {
return false; // optimizing heuristic
}
MethodSignatureBackedByPsiMethod current = (MethodSignatureBackedByPsiMethod) method.getSignature(classSubstitutor);
if (!areSignaturesErasureEqual(current, found) || isVarargMethod(method) != isVarargMethod(found.getMethod())) {
return false; // different signatures
}
}
for (PsiType t : classType.getSuperTypes()) {
if (!find((PsiClassType) t)) {
return false;
}
}
return true;
}
@Nullable
PsiMethod getFoundMethod() {
return found == null ? null : found.getMethod();
}
}
}