Use descriptors for building SAM constructors
It helps to get rid of semantics duplicating and fixes known bugs - SOE in OnlyAbstractMethodFinder.find - type enhancement for SAM constructors #KT-11287 Fixed #KT-11322 Fixed EA-77989 Fixed
This commit is contained in:
+2
-42
@@ -19,20 +19,13 @@ package org.jetbrains.kotlin.load.java.sam
|
||||
import org.jetbrains.kotlin.descriptors.ClassifierDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.load.java.components.SamConversionResolver
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaConstructorDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.JavaMethodDescriptor
|
||||
import org.jetbrains.kotlin.load.java.descriptors.SamConstructorDescriptor
|
||||
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.JavaMethod
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.checker.KotlinTypeChecker
|
||||
import java.util.*
|
||||
|
||||
object SamConversionResolverImpl : SamConversionResolver {
|
||||
override fun resolveSamConstructor(constructorOwner: DeclarationDescriptor, classifier: () -> ClassifierDescriptor?): SamConstructorDescriptor? {
|
||||
@@ -51,41 +44,8 @@ object SamConversionResolverImpl : SamConversionResolver {
|
||||
}
|
||||
}
|
||||
|
||||
override fun resolveFunctionTypeIfSamInterface(
|
||||
classDescriptor: JavaClassDescriptor,
|
||||
resolveMethod: (JavaMethod) -> FunctionDescriptor
|
||||
): KotlinType? {
|
||||
val jClass = (classDescriptor.source as? JavaSourceElement)?.javaElement as? JavaClass ?: return null
|
||||
val samInterfaceMethod = SingleAbstractMethodUtils.getSamInterfaceMethod(jClass) ?: return null
|
||||
val abstractMethod = if (jClass.fqName == samInterfaceMethod.containingClass.fqName) {
|
||||
resolveMethod(samInterfaceMethod)
|
||||
}
|
||||
else {
|
||||
findFunctionWithMostSpecificReturnType(TypeUtils.getAllSupertypes(classDescriptor.defaultType))
|
||||
}
|
||||
override fun resolveFunctionTypeIfSamInterface(classDescriptor: JavaClassDescriptor): KotlinType? {
|
||||
val abstractMethod = SingleAbstractMethodUtils.getSingleAbstractMethodOrNull(classDescriptor) ?: return null
|
||||
return SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(abstractMethod)
|
||||
}
|
||||
|
||||
private fun findFunctionWithMostSpecificReturnType(supertypes: Set<KotlinType>): SimpleFunctionDescriptor {
|
||||
val candidates = ArrayList<SimpleFunctionDescriptor>(supertypes.size)
|
||||
for (supertype in supertypes) {
|
||||
val abstractMembers = SingleAbstractMethodUtils.getAbstractMembers(supertype)
|
||||
if (!abstractMembers.isEmpty()) {
|
||||
candidates.add((abstractMembers[0] as SimpleFunctionDescriptor))
|
||||
}
|
||||
}
|
||||
if (candidates.isEmpty()) {
|
||||
throw IllegalStateException("Couldn't find abstract method in supertypes " + supertypes)
|
||||
}
|
||||
var currentMostSpecificType = candidates[0]
|
||||
for (candidate in candidates) {
|
||||
val candidateReturnType = candidate.returnType
|
||||
val currentMostSpecificReturnType = currentMostSpecificType.returnType
|
||||
assert(candidateReturnType != null && currentMostSpecificReturnType != null) { "$candidate, $currentMostSpecificReturnType" }
|
||||
if (KotlinTypeChecker.DEFAULT.isSubtypeOf(candidateReturnType!!, currentMostSpecificReturnType!!)) {
|
||||
currentMostSpecificType = candidate
|
||||
}
|
||||
}
|
||||
return currentMostSpecificType
|
||||
}
|
||||
}
|
||||
|
||||
+13
-118
@@ -18,26 +18,23 @@ package org.jetbrains.kotlin.load.java.sam;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns;
|
||||
import org.jetbrains.kotlin.descriptors.*;
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations;
|
||||
import org.jetbrains.kotlin.descriptors.impl.TypeParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl;
|
||||
import org.jetbrains.kotlin.load.java.components.DescriptorResolverUtils;
|
||||
import org.jetbrains.kotlin.load.java.descriptors.*;
|
||||
import org.jetbrains.kotlin.load.java.lazy.types.LazyJavaTypeResolver;
|
||||
import org.jetbrains.kotlin.load.java.structure.*;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.name.Name;
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils;
|
||||
import org.jetbrains.kotlin.resolve.calls.inference.CapturedTypeConstructorKt;
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.DescriptorUtilsKt;
|
||||
import org.jetbrains.kotlin.resolve.jvm.JavaResolverUtils;
|
||||
import org.jetbrains.kotlin.types.*;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.kotlin.types.Variance.INVARIANT;
|
||||
import static org.jetbrains.kotlin.types.Variance.IN_VARIANCE;
|
||||
|
||||
public class SingleAbstractMethodUtils {
|
||||
@@ -95,19 +92,23 @@ public class SingleAbstractMethodUtils {
|
||||
return DescriptorUtilsKt.getBuiltIns(function).getFunctionType(Annotations.Companion.getEMPTY(), null, parameterTypes, returnType);
|
||||
}
|
||||
|
||||
private static boolean isSamInterface(@NotNull ClassDescriptor klass) {
|
||||
@Nullable
|
||||
public static FunctionDescriptor getSingleAbstractMethodOrNull(@NotNull ClassDescriptor klass) {
|
||||
if (klass.getKind() != ClassKind.INTERFACE) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
List<CallableMemberDescriptor> abstractMembers = getAbstractMembers(klass.getDefaultType());
|
||||
if (abstractMembers.size() == 1) {
|
||||
CallableMemberDescriptor member = abstractMembers.get(0);
|
||||
if (member instanceof SimpleFunctionDescriptor) {
|
||||
return member.getTypeParameters().isEmpty();
|
||||
return member.getTypeParameters().isEmpty()
|
||||
? (FunctionDescriptor) member
|
||||
: null;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@@ -115,7 +116,7 @@ public class SingleAbstractMethodUtils {
|
||||
@NotNull DeclarationDescriptor owner,
|
||||
@NotNull JavaClassDescriptor samInterface
|
||||
) {
|
||||
assert isSamInterface(samInterface) : samInterface;
|
||||
assert getSingleAbstractMethodOrNull(samInterface) != null : samInterface;
|
||||
|
||||
SamConstructorDescriptor result = new SamConstructorDescriptor(owner, samInterface);
|
||||
|
||||
@@ -282,29 +283,6 @@ public class SingleAbstractMethodUtils {
|
||||
return new TypeParameters(typeParameters, typeParametersSubstitutor);
|
||||
}
|
||||
|
||||
// Returns null if not SAM interface
|
||||
@Nullable
|
||||
public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) {
|
||||
FqName fqName = javaClass.getFqName();
|
||||
if (fqName == null || fqName.toUnsafe().startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) {
|
||||
return null;
|
||||
}
|
||||
if (!javaClass.isInterface() || javaClass.isAnnotationType()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return findOnlyAbstractMethod(javaClass);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) {
|
||||
OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder();
|
||||
if (finder.find(javaClass.getDefaultType())) {
|
||||
return finder.getFoundMethod();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static class TypeParameters {
|
||||
public final List<TypeParameterDescriptor> descriptors;
|
||||
public final TypeSubstitutor substitutor;
|
||||
@@ -323,88 +301,5 @@ public class SingleAbstractMethodUtils {
|
||||
);
|
||||
}
|
||||
|
||||
private static class OnlyAbstractMethodFinder {
|
||||
private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object");
|
||||
|
||||
private JavaMethod foundMethod;
|
||||
private JavaTypeSubstitutor foundClassSubstitutor;
|
||||
|
||||
private boolean find(@NotNull JavaClassifierType classifierType) {
|
||||
JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor();
|
||||
JavaClassifier classifier = classifierType.getClassifier();
|
||||
if (classifier == null) {
|
||||
return false; // can't resolve class -> not a SAM interface
|
||||
}
|
||||
assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier;
|
||||
JavaClass javaClass = (JavaClass) classifier;
|
||||
if (OBJECT_FQ_NAME.equals(javaClass.getFqName())) {
|
||||
return true;
|
||||
}
|
||||
for (JavaMethod method : javaClass.getMethods()) {
|
||||
|
||||
//skip java 8 default methods
|
||||
if (!method.isAbstract()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (DescriptorResolverUtils.isObjectMethod(method)) { // e.g., ignore toString() declared in interface
|
||||
continue;
|
||||
}
|
||||
if (!method.getTypeParameters().isEmpty()) {
|
||||
return false; // if interface has generic methods, it is not a SAM interface
|
||||
}
|
||||
|
||||
if (foundMethod == null) {
|
||||
foundMethod = method;
|
||||
foundClassSubstitutor = classSubstitutor;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) {
|
||||
return false; // different signatures
|
||||
}
|
||||
}
|
||||
|
||||
for (JavaClassifierType t : classifierType.getSupertypes()) {
|
||||
if (!find(t)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.intellij.psi.util.MethodSignatureUtil#areSignaturesErasureEqual
|
||||
*/
|
||||
private static boolean areSignaturesErasureEqual(
|
||||
@NotNull JavaMethod method1,
|
||||
@NotNull JavaTypeSubstitutor substitutor1,
|
||||
@NotNull JavaMethod method2,
|
||||
@NotNull JavaTypeSubstitutor substitutor2
|
||||
) {
|
||||
if (!method1.getName().equals(method2.getName())) return false;
|
||||
|
||||
Collection<JavaValueParameter> parameters1 = method1.getValueParameters();
|
||||
Collection<JavaValueParameter> parameters2 = method2.getValueParameters();
|
||||
if (parameters1.size() != parameters2.size()) return false;
|
||||
|
||||
for (Iterator<JavaValueParameter> it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) {
|
||||
JavaValueParameter param1 = it1.next();
|
||||
JavaValueParameter param2 = it2.next();
|
||||
if (param1.isVararg() != param2.isVararg()) return false;
|
||||
|
||||
JavaType type1 = JavaResolverUtils.erasure(substitutor1.substitute(param1.getType()), substitutor1);
|
||||
JavaType type2 = JavaResolverUtils.erasure(substitutor2.substitute(param2.getType()), substitutor2);
|
||||
if (!(type1 == null ? type2 == null : type1.equals(type2))) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JavaMethod getFoundMethod() {
|
||||
return foundMethod;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,81 +34,6 @@ public class JavaResolverUtils {
|
||||
private JavaResolverUtils() {
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType)
|
||||
*/
|
||||
@Nullable
|
||||
public static JavaType erasure(@NotNull JavaType type) {
|
||||
return erasure(type, JavaTypeSubstitutor.EMPTY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.intellij.psi.util.TypeConversionUtil#erasure(com.intellij.psi.PsiType, com.intellij.psi.PsiSubstitutor)
|
||||
*/
|
||||
@Nullable
|
||||
public static JavaType erasure(@NotNull JavaType type, @NotNull JavaTypeSubstitutor substitutor) {
|
||||
if (type instanceof JavaClassifierType) {
|
||||
JavaClassifier classifier = ((JavaClassifierType) type).getClassifier();
|
||||
if (classifier instanceof JavaClass) {
|
||||
return ((JavaClass) classifier).getDefaultType();
|
||||
}
|
||||
else if (classifier instanceof JavaTypeParameter) {
|
||||
JavaTypeParameter typeParameter = (JavaTypeParameter) classifier;
|
||||
return typeParameterErasure(typeParameter, new HashSet<JavaTypeParameter>(), substitutor);
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else if (type instanceof JavaPrimitiveType) {
|
||||
return type;
|
||||
}
|
||||
else if (type instanceof JavaArrayType) {
|
||||
JavaType erasure = erasure(((JavaArrayType) type).getComponentType(), substitutor);
|
||||
return erasure == null ? null : erasure.createArrayType();
|
||||
}
|
||||
else if (type instanceof JavaWildcardType) {
|
||||
JavaWildcardType wildcardType = (JavaWildcardType) type;
|
||||
JavaType bound = wildcardType.getBound();
|
||||
if (bound != null && wildcardType.isExtends()) {
|
||||
return erasure(bound, substitutor);
|
||||
}
|
||||
return wildcardType.getTypeProvider().createJavaLangObjectType();
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("Unsupported type: " + type);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @see com.intellij.psi.util.TypeConversionUtil#typeParameterErasure(com.intellij.psi.PsiTypeParameter)
|
||||
*/
|
||||
@Nullable
|
||||
private static JavaType typeParameterErasure(
|
||||
@NotNull JavaTypeParameter typeParameter,
|
||||
@NotNull HashSet<JavaTypeParameter> visited,
|
||||
@NotNull JavaTypeSubstitutor substitutor
|
||||
) {
|
||||
Collection<JavaClassifierType> upperBounds = typeParameter.getUpperBounds();
|
||||
if (!upperBounds.isEmpty()) {
|
||||
JavaClassifier classifier = upperBounds.iterator().next().getClassifier();
|
||||
if (classifier instanceof JavaTypeParameter && !visited.contains(classifier)) {
|
||||
JavaTypeParameter typeParameterBound = (JavaTypeParameter) classifier;
|
||||
visited.add(typeParameterBound);
|
||||
JavaType substitutedType = substitutor.substitute(typeParameterBound);
|
||||
if (substitutedType != null) {
|
||||
return erasure(substitutedType);
|
||||
}
|
||||
return typeParameterErasure(typeParameterBound, visited, substitutor);
|
||||
}
|
||||
else if (classifier instanceof JavaClass) {
|
||||
return ((JavaClass) classifier).getDefaultType();
|
||||
}
|
||||
}
|
||||
return typeParameter.getTypeProvider().createJavaLangObjectType();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static Map<TypeParameterDescriptor, TypeParameterDescriptorImpl> recreateTypeParametersAndReturnMapping(
|
||||
@NotNull List<TypeParameterDescriptor> originalParameters,
|
||||
@Nullable DeclarationDescriptor newOwner
|
||||
|
||||
Reference in New Issue
Block a user