KT-3577 Stack overflow when resolving SAM adapter (from completion, show parameters, etc)

#KT-3577
This commit is contained in:
Evgeny Gerashchenko
2013-05-17 20:54:36 +04:00
parent 6f7d42185f
commit 5fc7c885bb
11 changed files with 174 additions and 40 deletions
@@ -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;
}
}
@@ -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 {
@@ -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;
@@ -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;
}
@@ -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);
@@ -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;