KT-3577 Stack overflow when resolving SAM adapter (from completion, show parameters, etc)
#KT-3577
This commit is contained in:
+10
-6
@@ -23,6 +23,7 @@ import org.jetbrains.jet.lang.descriptors.ConstructorDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor;
|
||||
import org.jetbrains.jet.lang.descriptors.impl.MutableClassDescriptorLite;
|
||||
import org.jetbrains.jet.lang.resolve.java.scope.JavaClassNonStaticMembersScope;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -30,18 +31,16 @@ import java.util.Collection;
|
||||
* @see org.jetbrains.jet.lang.resolve.lazy.descriptors.LazyClassDescriptor
|
||||
*/
|
||||
public class ClassDescriptorFromJvmBytecode extends MutableClassDescriptorLite {
|
||||
private final boolean isSamInterface;
|
||||
private JetType functionTypeForSamInterface;
|
||||
|
||||
private JavaClassNonStaticMembersScope scopeForConstructorResolve;
|
||||
|
||||
public ClassDescriptorFromJvmBytecode(
|
||||
@NotNull DeclarationDescriptor containingDeclaration,
|
||||
@NotNull ClassKind kind,
|
||||
boolean isInner,
|
||||
boolean isSamInterface
|
||||
boolean isInner
|
||||
) {
|
||||
super(containingDeclaration, kind, isInner);
|
||||
this.isSamInterface = isSamInterface;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +61,12 @@ public class ClassDescriptorFromJvmBytecode extends MutableClassDescriptorLite {
|
||||
this.scopeForConstructorResolve = scopeForConstructorResolve;
|
||||
}
|
||||
|
||||
public boolean isSamInterface() {
|
||||
return isSamInterface;
|
||||
@Nullable
|
||||
public JetType getFunctionTypeForSamInterface() {
|
||||
return functionTypeForSamInterface;
|
||||
}
|
||||
|
||||
public void setFunctionTypeForSamInterface(@NotNull JetType functionTypeForSamInterface) {
|
||||
this.functionTypeForSamInterface = functionTypeForSamInterface;
|
||||
}
|
||||
}
|
||||
|
||||
+17
-7
@@ -18,6 +18,7 @@ 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.Lists;
|
||||
import com.google.common.collect.Multimap;
|
||||
import com.intellij.psi.*;
|
||||
import com.intellij.psi.util.PsiFormatUtil;
|
||||
@@ -402,28 +403,37 @@ public final class MembersCache {
|
||||
}
|
||||
|
||||
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) {
|
||||
if (psiClass.hasModifierProperty(PsiModifier.PRIVATE)) { // TODO hacky
|
||||
return null;
|
||||
}
|
||||
if (DescriptorResolverUtils.isKotlinClass(psiClass)) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
String qualifiedName = psiClass.getQualifiedName();
|
||||
if (qualifiedName == null || qualifiedName.startsWith(KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME.getFqName() + ".")) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
if (!psiClass.isInterface() || psiClass.isAnnotationType()) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
|
||||
int foundAbstractMethods = 0;
|
||||
List<PsiMethod> methods = Lists.newArrayList();
|
||||
for (PsiMethod method : psiClass.getAllMethods()) {
|
||||
if (!isObjectMethod(method) && method.hasModifierProperty(PsiModifier.ABSTRACT)) {
|
||||
foundAbstractMethods++;
|
||||
methods.add(method);
|
||||
|
||||
if (method.hasTypeParameters()) {
|
||||
return false;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return foundAbstractMethods == 1;
|
||||
return methods.size() == 1 ? methods.get(0) : null;
|
||||
}
|
||||
|
||||
private static abstract class RunOnce implements Runnable {
|
||||
|
||||
+2
-2
@@ -100,7 +100,7 @@ public final class JavaClassObjectResolver {
|
||||
FqName fqName = new FqName(qualifiedName);
|
||||
ClassPsiDeclarationProvider classObjectData = semanticServices.getPsiDeclarationProviderFactory().createBinaryClassData(classObjectPsiClass);
|
||||
ClassDescriptorFromJvmBytecode classObjectDescriptor
|
||||
= new ClassDescriptorFromJvmBytecode(containing, ClassKind.CLASS_OBJECT, false, false);
|
||||
= new ClassDescriptorFromJvmBytecode(containing, ClassKind.CLASS_OBJECT, false);
|
||||
classObjectDescriptor.setSupertypes(supertypesResolver.getSupertypes(classObjectDescriptor,
|
||||
new PsiClassWrapper(classObjectPsiClass),
|
||||
classObjectData,
|
||||
@@ -129,7 +129,7 @@ public final class JavaClassObjectResolver {
|
||||
) {
|
||||
FqNameUnsafe fqName = DescriptorResolverUtils.getFqNameForClassObject(psiClass);
|
||||
ClassDescriptorFromJvmBytecode classObjectDescriptor =
|
||||
new ClassDescriptorFromJvmBytecode(containing, ClassKind.CLASS_OBJECT, false, false);
|
||||
new ClassDescriptorFromJvmBytecode(containing, ClassKind.CLASS_OBJECT, false);
|
||||
ClassPsiDeclarationProvider data = semanticServices.getPsiDeclarationProviderFactory().createSyntheticClassObjectClassData(psiClass);
|
||||
setUpClassObjectDescriptor(classObjectDescriptor, containing, fqName, data, getClassObjectName(containing.getName().getName()));
|
||||
return classObjectDescriptor;
|
||||
|
||||
+12
-1
@@ -20,6 +20,7 @@ import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.PsiMethod;
|
||||
import com.intellij.psi.PsiModifier;
|
||||
import gnu.trove.THashMap;
|
||||
import gnu.trove.TObjectHashingStrategy;
|
||||
@@ -33,8 +34,11 @@ import org.jetbrains.jet.lang.resolve.java.*;
|
||||
import org.jetbrains.jet.lang.resolve.java.descriptor.ClassDescriptorFromJvmBytecode;
|
||||
import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation;
|
||||
import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
|
||||
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.PsiClassWrapper;
|
||||
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqName;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqNameBase;
|
||||
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
|
||||
@@ -234,7 +238,7 @@ public final class JavaClassResolver {
|
||||
ClassKind kind = getClassKind(psiClass, jetClassAnnotation);
|
||||
ClassPsiDeclarationProvider classData = semanticServices.getPsiDeclarationProviderFactory().createBinaryClassData(psiClass);
|
||||
ClassDescriptorFromJvmBytecode classDescriptor = new ClassDescriptorFromJvmBytecode(
|
||||
containingDeclaration, kind, isInnerClass(psiClass), MembersCache.isSamInterface(psiClass));
|
||||
containingDeclaration, kind, isInnerClass(psiClass));
|
||||
|
||||
cache(javaClassToKotlinFqName(fqName), classDescriptor);
|
||||
classDescriptor.setName(Name.identifier(psiClass.getName()));
|
||||
@@ -269,6 +273,13 @@ public final class JavaClassResolver {
|
||||
|
||||
trace.record(BindingContext.CLASS, psiClass, classDescriptor);
|
||||
|
||||
PsiMethod samInterfaceMethod = MembersCache.getSamInterfaceMethod(psiClass);
|
||||
if (samInterfaceMethod != null) {
|
||||
SimpleFunctionDescriptor abstractMethod = functionResolver.resolveFunctionMutely(
|
||||
psiClass, new PsiMethodWrapper(samInterfaceMethod), classDescriptor);
|
||||
classDescriptor.setFunctionTypeForSamInterface(SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(abstractMethod));
|
||||
}
|
||||
|
||||
return classDescriptor;
|
||||
}
|
||||
|
||||
|
||||
+17
-10
@@ -34,10 +34,7 @@ import org.jetbrains.jet.lang.resolve.java.*;
|
||||
import org.jetbrains.jet.lang.resolve.java.kotlinSignature.AlternativeMethodSignatureData;
|
||||
import org.jetbrains.jet.lang.resolve.java.kotlinSignature.SignaturesPropagationData;
|
||||
import org.jetbrains.jet.lang.resolve.java.kt.DescriptorKindUtils;
|
||||
import org.jetbrains.jet.lang.resolve.java.provider.ClassPsiDeclarationProvider;
|
||||
import org.jetbrains.jet.lang.resolve.java.provider.NamedMembers;
|
||||
import org.jetbrains.jet.lang.resolve.java.provider.PackagePsiDeclarationProvider;
|
||||
import org.jetbrains.jet.lang.resolve.java.provider.PsiDeclarationProvider;
|
||||
import org.jetbrains.jet.lang.resolve.java.provider.*;
|
||||
import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils;
|
||||
import org.jetbrains.jet.lang.resolve.java.wrapper.PsiMethodWrapper;
|
||||
import org.jetbrains.jet.lang.resolve.name.Name;
|
||||
@@ -93,10 +90,18 @@ public final class JavaFunctionResolver {
|
||||
this.annotationResolver = annotationResolver;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
SimpleFunctionDescriptor resolveFunctionMutely(
|
||||
@NotNull PsiClass psiClass, PsiMethodWrapper method,
|
||||
@NotNull ClassOrNamespaceDescriptor ownerDescriptor
|
||||
) {
|
||||
return resolveMethodToFunctionDescriptor(psiClass, method, DeclarationOrigin.JAVA, ownerDescriptor, false);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private SimpleFunctionDescriptor resolveMethodToFunctionDescriptor(
|
||||
@NotNull PsiClass psiClass, PsiMethodWrapper method,
|
||||
@NotNull PsiDeclarationProvider scopeData, @NotNull ClassOrNamespaceDescriptor ownerDescriptor
|
||||
@NotNull DeclarationOrigin declarationOrigin, @NotNull ClassOrNamespaceDescriptor ownerDescriptor, boolean record
|
||||
) {
|
||||
if (!DescriptorResolverUtils.isCorrectOwnerForEnumMember(ownerDescriptor, method.getPsiMember())) {
|
||||
return null;
|
||||
@@ -114,7 +119,7 @@ public final class JavaFunctionResolver {
|
||||
|
||||
PsiMethod psiMethod = method.getPsiMethod();
|
||||
PsiClass containingClass = psiMethod.getContainingClass();
|
||||
if (scopeData.getDeclarationOrigin() == KOTLIN) {
|
||||
if (declarationOrigin == KOTLIN) {
|
||||
// TODO: unless maybe class explicitly extends Object
|
||||
assert containingClass != null;
|
||||
String ownerClassName = containingClass.getQualifiedName();
|
||||
@@ -189,11 +194,11 @@ public final class JavaFunctionResolver {
|
||||
/*isInline = */ false
|
||||
);
|
||||
|
||||
if (functionDescriptorImpl.getKind() == CallableMemberDescriptor.Kind.DECLARATION) {
|
||||
if (functionDescriptorImpl.getKind() == CallableMemberDescriptor.Kind.DECLARATION && record) {
|
||||
BindingContextUtils.recordFunctionDeclarationToDescriptor(trace, psiMethod, functionDescriptorImpl);
|
||||
}
|
||||
|
||||
if (scopeData.getDeclarationOrigin() == JAVA) {
|
||||
if (declarationOrigin == JAVA && record) {
|
||||
trace.record(BindingContext.IS_DECLARED_IN_JAVA, functionDescriptorImpl);
|
||||
}
|
||||
|
||||
@@ -208,7 +213,9 @@ public final class JavaFunctionResolver {
|
||||
checkFunctionsOverrideCorrectly(method, superFunctions, functionDescriptorImpl);
|
||||
}
|
||||
else {
|
||||
trace.record(BindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, functionDescriptorImpl, signatureErrors);
|
||||
if (record) {
|
||||
trace.record(BindingContext.LOAD_FROM_JAVA_SIGNATURE_ERRORS, functionDescriptorImpl, signatureErrors);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,7 +274,7 @@ public final class JavaFunctionResolver {
|
||||
|
||||
Set<SimpleFunctionDescriptor> functionsFromCurrent = Sets.newHashSet();
|
||||
for (PsiMethodWrapper method : namedMembers.getMethods()) {
|
||||
SimpleFunctionDescriptor function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData, owner);
|
||||
SimpleFunctionDescriptor function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData.getDeclarationOrigin(), owner, true);
|
||||
if (function != null) {
|
||||
functionsFromCurrent.add(function);
|
||||
|
||||
|
||||
+62
-14
@@ -27,10 +27,7 @@ import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
|
||||
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;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.TypeSubstitutor;
|
||||
import org.jetbrains.jet.lang.types.TypeUtils;
|
||||
import org.jetbrains.jet.lang.types.Variance;
|
||||
import org.jetbrains.jet.lang.types.*;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
|
||||
import java.util.Arrays;
|
||||
@@ -38,7 +35,10 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
|
||||
|
||||
public class SingleAbstractMethodUtils {
|
||||
|
||||
@NotNull
|
||||
private static List<CallableMemberDescriptor> getAbstractMembers(@NotNull JetType type) {
|
||||
List<CallableMemberDescriptor> abstractMembers = Lists.newArrayList();
|
||||
@@ -52,18 +52,67 @@ public class SingleAbstractMethodUtils {
|
||||
return abstractMembers;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
// TODO maybe do this in substitutor?
|
||||
private static JetType fixProjections(@NotNull JetType functionType) {
|
||||
//removes redundant projection kinds and detects conflicts
|
||||
|
||||
List<TypeProjection> arguments = Lists.newArrayList();
|
||||
for (TypeParameterDescriptor typeParameter : functionType.getConstructor().getParameters()) {
|
||||
Variance variance = typeParameter.getVariance();
|
||||
TypeProjection argument = functionType.getArguments().get(typeParameter.getIndex());
|
||||
Variance kind = argument.getProjectionKind();
|
||||
if (kind != INVARIANT && variance != INVARIANT) {
|
||||
if (kind == variance) {
|
||||
arguments.add(new TypeProjection(argument.getType()));
|
||||
}
|
||||
else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
else {
|
||||
arguments.add(argument);
|
||||
}
|
||||
}
|
||||
ClassifierDescriptor classifier = functionType.getConstructor().getDeclarationDescriptor();
|
||||
assert classifier instanceof ClassDescriptor : "Not class: " + classifier;
|
||||
return new JetTypeImpl(
|
||||
functionType.getAnnotations(),
|
||||
functionType.getConstructor(),
|
||||
functionType.isNullable(),
|
||||
arguments,
|
||||
((ClassDescriptor) classifier).getMemberScope(arguments)
|
||||
);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private static JetType getFunctionTypeForSamType(@NotNull JetType samType) {
|
||||
FunctionDescriptor function = getAbstractMethodOfSamType(samType);
|
||||
// e.g. samType == Comparator<String>?
|
||||
|
||||
ClassifierDescriptor classifier = samType.getConstructor().getDeclarationDescriptor();
|
||||
if (classifier instanceof ClassDescriptorFromJvmBytecode) {
|
||||
// Function2<T, T, Int>
|
||||
JetType functionTypeDefault = ((ClassDescriptorFromJvmBytecode) classifier).getFunctionTypeForSamInterface();
|
||||
|
||||
if (functionTypeDefault != null) {
|
||||
// Function2<String, String, Int>?
|
||||
JetType substitute = TypeSubstitutor.create(samType).substitute(functionTypeDefault, Variance.INVARIANT);
|
||||
|
||||
return substitute == null ? null : fixProjections(TypeUtils.makeNullableAsSpecified(substitute, samType.isNullable()));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetType getFunctionTypeForAbstractMethod(@NotNull FunctionDescriptor function) {
|
||||
JetType returnType = function.getReturnType();
|
||||
assert returnType != null : "function is not initialized: " + function;
|
||||
List<JetType> parameterTypes = Lists.newArrayList();
|
||||
for (ValueParameterDescriptor parameter : function.getValueParameters()) {
|
||||
parameterTypes.add(parameter.getType());
|
||||
}
|
||||
JetType functionType = KotlinBuiltIns.getInstance()
|
||||
.getFunctionType(Collections.<AnnotationDescriptor>emptyList(), null, parameterTypes, returnType);
|
||||
return TypeUtils.makeNullableAsSpecified(functionType, samType.isNullable());
|
||||
return KotlinBuiltIns.getInstance().getFunctionType(
|
||||
Collections.<AnnotationDescriptor>emptyList(), null, parameterTypes, returnType);
|
||||
}
|
||||
|
||||
private static boolean isSamInterface(@NotNull ClassDescriptor klass) {
|
||||
@@ -98,6 +147,7 @@ public class SingleAbstractMethodUtils {
|
||||
TypeParameters typeParameters = recreateAndInitializeTypeParameters(samInterface.getTypeConstructor().getParameters(), result);
|
||||
|
||||
JetType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType());
|
||||
assert parameterTypeUnsubstituted != null : "couldn't get function type for SAM type" + samInterface.getDefaultType();
|
||||
JetType parameterType = typeParameters.substitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE);
|
||||
assert parameterType != null : "couldn't substitute type: " + parameterType + ", substitutor = " + typeParameters.substitutor;
|
||||
ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl(
|
||||
@@ -121,10 +171,7 @@ public class SingleAbstractMethodUtils {
|
||||
}
|
||||
|
||||
public static boolean isSamType(@NotNull JetType type) {
|
||||
ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor();
|
||||
return classifier instanceof ClassDescriptorFromJvmBytecode &&
|
||||
((ClassDescriptorFromJvmBytecode) classifier).isSamInterface() &&
|
||||
getAbstractMembers(type).size() == 1; // Comparator<*> is not a SAM type, because substituted compare() method doesn't exist
|
||||
return getFunctionTypeForSamType(type) != null;
|
||||
}
|
||||
|
||||
public static boolean isSamAdapterNecessary(@NotNull SimpleFunctionDescriptor fun) {
|
||||
@@ -155,7 +202,8 @@ public class SingleAbstractMethodUtils {
|
||||
List<ValueParameterDescriptor> valueParameters = Lists.newArrayList();
|
||||
for (ValueParameterDescriptor originalParam : original.getValueParameters()) {
|
||||
JetType originalType = originalParam.getType();
|
||||
JetType newTypeUnsubstituted = isSamType(originalType) ? getFunctionTypeForSamType(originalType) : originalType;
|
||||
JetType functionType = getFunctionTypeForSamType(originalType);
|
||||
JetType newTypeUnsubstituted = functionType != null ? functionType : originalType;
|
||||
JetType newType = typeParameters.substitutor.substitute(newTypeUnsubstituted, Variance.IN_VARIANCE);
|
||||
assert newType != null : "couldn't substitute type: " + newTypeUnsubstituted + ", substitutor = " + typeParameters.substitutor;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user