Signature comparison used to find super functions in SignaturesPropagationData

#KT-4509 Fixed
This commit is contained in:
Andrey Breslav
2014-05-20 19:25:01 +04:00
parent 44849f8504
commit 773cb9efbe
15 changed files with 168 additions and 260 deletions
@@ -0,0 +1,35 @@
/*
* Copyright 2010-2014 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.jet.lang.resolve.java.jvmSignature
import org.jetbrains.jet.lang.descriptors.FunctionDescriptor
trait KotlinToJvmSignatureMapper {
fun mapToJvmMethodSignature(function: FunctionDescriptor): JvmMethodSignature
}
fun erasedSignaturesEqualIgnoringReturnTypes(subFunction: JvmMethodSignature, superFunction: JvmMethodSignature): Boolean {
val subParams = subFunction.getValueParameters()
val superParams = superFunction.getValueParameters()
if (subParams.size() != superParams.size()) return false
return subParams.zip(superParams).all {
p -> val (subParam, superParam) = p
subParam.getAsmType() == superParam.getAsmType()
}
}
@@ -17,21 +17,12 @@
package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.intellij.openapi.util.Comparing;
import com.intellij.openapi.util.Condition;
import com.intellij.util.containers.ContainerUtil;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor;
import org.jetbrains.jet.lang.descriptors.TypeParameterDescriptor;
import org.jetbrains.jet.lang.descriptors.Visibilities;
import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils;
import org.jetbrains.jet.lang.resolve.java.structure.*;
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaMethodImpl;
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaTypeSubstitutorImpl;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
import org.jetbrains.jet.lang.types.JetType;
import org.jetbrains.jet.lang.types.JetTypeImpl;
@@ -41,9 +32,8 @@ import org.jetbrains.jet.lang.types.checker.JetTypeChecker;
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
import org.jetbrains.jet.renderer.DescriptorRenderer;
import java.util.*;
import static org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils.erasure;
import java.util.Arrays;
import java.util.List;
// This class contains heuristics for processing corner cases in propagation
class PropagationHeuristics {
@@ -124,117 +114,6 @@ class PropagationHeuristics {
return null;
}
@SuppressWarnings("unchecked")
@NotNull
static List<JavaMethodImpl> getSuperMethods(@NotNull JavaMethod method) {
return (List) new SuperMethodCollector(method).collect();
}
private PropagationHeuristics() {
}
private static class SuperMethodCollector {
private final JavaMethod initialMethod;
private final Name initialMethodName;
private final List<JavaType> initialParametersErasure;
private final Set<JavaClass> visitedSuperclasses = Sets.newHashSet();
private final List<JavaMethod> collectedMethods = Lists.newArrayList();
private SuperMethodCollector(@NotNull JavaMethod initialMethod) {
this.initialMethod = initialMethod;
initialMethodName = initialMethod.getName();
Collection<JavaValueParameter> valueParameters = initialMethod.getValueParameters();
initialParametersErasure = Lists.newArrayListWithExpectedSize(valueParameters.size());
for (JavaValueParameter parameter : valueParameters) {
initialParametersErasure.add(erasure(varargToArray(parameter.getType(), parameter.isVararg())));
}
}
@NotNull
public List<JavaMethod> collect() {
if (!canHaveSuperMethod(initialMethod)) {
return Collections.emptyList();
}
for (JavaClassifierType supertype : initialMethod.getContainingClass().getSupertypes()) {
collectFromSupertype(supertype);
}
return collectedMethods;
}
private void collectFromSupertype(@NotNull JavaClassifierType type) {
JavaClassifier classifier = type.getClassifier();
if (!(classifier instanceof JavaClass)) return;
JavaClass klass = (JavaClass) classifier;
if (!visitedSuperclasses.add(klass)) return;
JavaTypeSubstitutor supertypeSubstitutor = getErasedSubstitutor(type);
for (JavaMethod methodFromSuper : klass.getMethods()) {
if (isSubMethodOf(methodFromSuper, supertypeSubstitutor)) {
collectedMethods.add(methodFromSuper);
return;
}
}
for (JavaClassifierType supertype : type.getSupertypes()) {
collectFromSupertype(supertype);
}
}
private boolean isSubMethodOf(@NotNull JavaMethod methodFromSuper, @NotNull JavaTypeSubstitutor supertypeSubstitutor) {
if (!methodFromSuper.getName().equals(initialMethodName)) {
return false;
}
Collection<JavaValueParameter> fromSuperParameters = methodFromSuper.getValueParameters();
if (fromSuperParameters.size() != initialParametersErasure.size()) {
return false;
}
Iterator<JavaType> originalIterator = initialParametersErasure.iterator();
Iterator<JavaValueParameter> superIterator = fromSuperParameters.iterator();
while (originalIterator.hasNext()) {
JavaType originalType = originalIterator.next();
JavaValueParameter parameterFromSuper = superIterator.next();
JavaType typeFromSuper = erasure(varargToArray(
supertypeSubstitutor.substitute(parameterFromSuper.getType()),
parameterFromSuper.isVararg()
));
if (!Comparing.equal(originalType, typeFromSuper)) {
return false;
}
}
return true;
}
@NotNull
private static JavaType varargToArray(@NotNull JavaType type, boolean isVararg) {
return isVararg ? ((JavaArrayType) type).getComponentType().createArrayType() : type;
}
@NotNull
private static JavaTypeSubstitutor getErasedSubstitutor(@NotNull JavaClassifierType type) {
Map<JavaTypeParameter, JavaType> unerasedMap = type.getSubstitutor().getSubstitutionMap();
Map<JavaTypeParameter, JavaType> erasedMap = Maps.newHashMapWithExpectedSize(unerasedMap.size());
for (Map.Entry<JavaTypeParameter, JavaType> entry : unerasedMap.entrySet()) {
JavaType value = entry.getValue();
erasedMap.put(entry.getKey(), value == null ? null : erasure(value));
}
return JavaTypeSubstitutorImpl.create(erasedMap);
}
private static boolean canHaveSuperMethod(@NotNull JavaMethod method) {
return !method.isConstructor() &&
!method.isStatic() &&
method.getVisibility() != Visibilities.PRIVATE &&
!DescriptorResolverUtils.OBJECT_FQ_NAME.equals(method.getContainingClass().getFqName());
}
}
}
@@ -16,13 +16,9 @@
package org.jetbrains.jet.lang.resolve.java.kotlinSignature;
import com.google.common.collect.*;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.Pair;
import com.intellij.openapi.util.SystemInfo;
import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiManager;
import com.intellij.psi.PsiMethod;
import com.google.common.collect.Lists;
import com.google.common.collect.Multimap;
import com.google.common.collect.Sets;
import com.intellij.util.Function;
import com.intellij.util.containers.ContainerUtil;
import kotlin.Function1;
@@ -30,20 +26,17 @@ import kotlin.KotlinPackage;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.descriptors.annotations.Annotations;
import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl;
import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl;
import org.jetbrains.jet.lang.resolve.BindingContext;
import org.jetbrains.jet.lang.resolve.BindingContextUtils;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.DescriptorUtils;
import org.jetbrains.jet.lang.resolve.java.jetAsJava.KotlinLightMethod;
import org.jetbrains.jet.lang.resolve.java.mapping.JavaToKotlinClassMap;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.KotlinToJvmSignatureMapper;
import org.jetbrains.jet.lang.resolve.java.descriptor.JavaMethodDescriptor;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmMethodSignature;
import org.jetbrains.jet.lang.resolve.java.jvmSignature.JvmSignaturePackage;
import org.jetbrains.jet.lang.resolve.java.resolver.DescriptorResolverUtils;
import org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage;
import org.jetbrains.jet.lang.resolve.java.structure.JavaClass;
import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod;
import org.jetbrains.jet.lang.resolve.java.structure.impl.JavaMethodImpl;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.FqNameUnsafe;
import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.resolve.scopes.JetScope;
@@ -58,7 +51,11 @@ import static org.jetbrains.jet.lang.resolve.java.resolver.TypeUsage.*;
import static org.jetbrains.jet.lang.types.Variance.INVARIANT;
public class SignaturesPropagationData {
private static final Logger LOG = Logger.getInstance(SignaturesPropagationData.class);
private static final KotlinToJvmSignatureMapper SIGNATURE_MAPPER = ServiceLoader.load(
KotlinToJvmSignatureMapper.class,
KotlinToJvmSignatureMapper.class.getClassLoader()
).iterator().next();
private final List<TypeParameterDescriptor> modifiedTypeParameters;
private final ValueParameters modifiedValueParameters;
@@ -75,11 +72,14 @@ public class SignaturesPropagationData {
@Nullable JetType receiverType,
@NotNull List<ValueParameterDescriptor> autoValueParameters, // descriptors built by parameters resolver
@NotNull List<TypeParameterDescriptor> autoTypeParameters, // descriptors built by signature resolver
@NotNull JavaMethod method,
@NotNull BindingTrace trace
@NotNull JavaMethod method
) {
this.containingClass = containingClass;
superFunctions = getSuperFunctionsForMethod(method, trace, containingClass);
JavaMethodDescriptor autoMethodDescriptor =
createAutoMethodDescriptor(containingClass, method, autoReturnType, receiverType, autoValueParameters, autoTypeParameters);
superFunctions = getSuperFunctionsForMethod(method, autoMethodDescriptor, containingClass);
autoTypeParameterToModified = DescriptorResolverUtils.recreateTypeParametersAndReturnMapping(autoTypeParameters, null);
@@ -88,6 +88,31 @@ public class SignaturesPropagationData {
modifiedValueParameters = modifyValueParametersAccordingToSuperMethods(receiverType, autoValueParameters);
}
@NotNull
private static JavaMethodDescriptor createAutoMethodDescriptor(
@NotNull ClassDescriptor containingClass,
@NotNull JavaMethod method, JetType autoReturnType,
@Nullable JetType receiverType,
@NotNull List<ValueParameterDescriptor> autoValueParameters,
@NotNull List<TypeParameterDescriptor> autoTypeParameters
) {
JavaMethodDescriptor autoMethodDescriptor = JavaMethodDescriptor.createJavaMethod(
containingClass,
Annotations.EMPTY,
method.getName()
);
autoMethodDescriptor.initialize(
receiverType,
containingClass.getThisAsReceiverParameter(),
autoTypeParameters,
autoValueParameters,
autoReturnType,
Modality.OPEN,
Visibilities.PUBLIC
);
return autoMethodDescriptor;
}
public List<TypeParameterDescriptor> getModifiedTypeParameters() {
return modifiedTypeParameters;
}
@@ -255,42 +280,21 @@ public class SignaturesPropagationData {
private static List<FunctionDescriptor> getSuperFunctionsForMethod(
@NotNull JavaMethod method,
@NotNull BindingTrace trace,
@NotNull JavaMethodDescriptor autoMethodDescriptor,
@NotNull ClassDescriptor containingClass
) {
List<FunctionDescriptor> superFunctions = Lists.newArrayList();
Map<ClassDescriptor, JetType> superclassToSupertype = getSuperclassToSupertypeMap(containingClass);
Multimap<FqName, Pair<FunctionDescriptor, JavaMethodImpl>> superclassToFunctions =
getSuperclassToFunctionsMultimap(method, trace.getBindingContext(), containingClass);
for (JavaMethodImpl superMethod : PropagationHeuristics.getSuperMethods(method)) {
JavaClass javaClass = superMethod.getContainingClass();
FqName classFqName = javaClass.getFqName();
assert classFqName != null : "Class FQ name should not be null: " + javaClass;
if (!JavaToKotlinClassMap.getInstance().mapPlatformClass(classFqName).isEmpty()) {
for (FunctionDescriptor superFun : JavaToKotlinMethodMap.INSTANCE.getFunctions(superMethod, classFqName, containingClass)) {
superFunctions.add(substituteSuperFunction(superclassToSupertype, superFun));
// TODO: Add propagation for other kotlin descriptors (KT-3621)
Name name = method.getName();
JvmMethodSignature autoSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(autoMethodDescriptor);
for (JetType supertype : containingClass.getTypeConstructor().getSupertypes()) {
Collection<FunctionDescriptor> superFunctionCandidates = supertype.getMemberScope().getFunctions(name);
for (FunctionDescriptor candidate : superFunctionCandidates) {
JvmMethodSignature candidateSignature = SIGNATURE_MAPPER.mapToJvmMethodSignature(candidate);
if (JvmSignaturePackage.erasedSignaturesEqualIgnoringReturnTypes(autoSignature, candidateSignature)) {
superFunctions.add(candidate);
}
continue;
}
DeclarationDescriptor superFun = superMethod.getPsi() instanceof KotlinLightMethod
? trace.get(BindingContext.DECLARATION_TO_DESCRIPTOR, ((KotlinLightMethod) superMethod.getPsi()).getOrigin())
: findSuperFunction(superclassToFunctions.get(classFqName), superMethod);
if (superFun == null) {
// Super methods which are Object methods in interfaces are not loaded by JDR.
if (!DescriptorResolverUtils.isObjectMethodInInterface(superMethod)) {
reportCantFindSuperFunction(method);
}
continue;
}
// TODO: Add propagation for other kotlin descriptors (KT-3621)
if (superFun instanceof FunctionDescriptor) {
superFunctions.add(substituteSuperFunction(superclassToSupertype, (FunctionDescriptor) superFun));
}
}
@@ -306,50 +310,6 @@ public class SignaturesPropagationData {
return superFunctions;
}
@NotNull
private static Multimap<FqName, Pair<FunctionDescriptor, JavaMethodImpl>> getSuperclassToFunctionsMultimap(
@NotNull JavaMethod method,
@NotNull BindingContext bindingContext,
@NotNull ClassDescriptor containingClass
) {
Multimap<FqName, Pair<FunctionDescriptor, JavaMethodImpl>> result = HashMultimap.create();
Name functionName = method.getName();
int parameterCount = method.getValueParameters().size();
for (JetType supertype : TypeUtils.getAllSupertypes(containingClass.getDefaultType())) {
ClassifierDescriptor klass = supertype.getConstructor().getDeclarationDescriptor();
assert klass != null;
FqName fqName = DescriptorUtils.getFqNameSafe(klass);
for (FunctionDescriptor fun : klass.getDefaultType().getMemberScope().getFunctions(functionName)) {
CallableMemberDescriptor.Kind kind = fun.getKind();
if ((kind == CallableMemberDescriptor.Kind.DECLARATION || kind == CallableMemberDescriptor.Kind.DELEGATION) &&
fun.getValueParameters().size() + (fun.getReceiverParameter() != null ? 1 : 0) == parameterCount) {
PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, fun);
if (declaration instanceof PsiMethod) {
result.put(fqName, Pair.create(fun, new JavaMethodImpl((PsiMethod) declaration)));
} // else declaration is null or JetNamedFunction: both cases are processed later
}
}
}
return result;
}
@Nullable
private static DeclarationDescriptor findSuperFunction(
@NotNull Collection<Pair<FunctionDescriptor, JavaMethodImpl>> superFunctionCandidates,
@NotNull JavaMethodImpl superMethod
) {
PsiManager psiManager = PsiManager.getInstance(superMethod.getPsi().getProject());
for (Pair<FunctionDescriptor, JavaMethodImpl> candidate : superFunctionCandidates) {
if (psiManager.areElementsEquivalent(candidate.second.getPsi(), superMethod.getPsi())) {
return candidate.first;
}
}
return null;
}
private boolean checkIfShouldBeExtension() {
boolean someSupersExtension = false;
boolean someSupersNotExtension = false;
@@ -705,48 +665,11 @@ public class SignaturesPropagationData {
return fixed != null ? fixed : classifier;
}
private static Map<ClassDescriptor, JetType> getSuperclassToSupertypeMap(ClassDescriptor containingClass) {
Map<ClassDescriptor, JetType> superclassToSupertype = Maps.newHashMap();
for (JetType supertype : TypeUtils.getAllSupertypes(containingClass.getDefaultType())) {
ClassifierDescriptor superclass = supertype.getConstructor().getDeclarationDescriptor();
assert superclass instanceof ClassDescriptor;
superclassToSupertype.put((ClassDescriptor) superclass, supertype);
}
return superclassToSupertype;
}
@NotNull
private static FunctionDescriptor substituteSuperFunction(
@NotNull Map<ClassDescriptor, JetType> superclassToSupertype,
@NotNull FunctionDescriptor superFun
) {
DeclarationDescriptor superFunContainer = superFun.getContainingDeclaration();
assert superFunContainer instanceof ClassDescriptor: superFunContainer;
JetType supertype = superclassToSupertype.get(superFunContainer);
assert supertype != null : "Couldn't find super type for super function: " + superFun;
TypeSubstitutor supertypeSubstitutor = TypeSubstitutor.create(supertype);
FunctionDescriptor substitutedSuperFun = superFun.substitute(supertypeSubstitutor);
assert substitutedSuperFun != null;
return substitutedSuperFun;
}
private static boolean isArrayType(@NotNull JetType type) {
KotlinBuiltIns builtIns = KotlinBuiltIns.getInstance();
return builtIns.isArray(type) || builtIns.isPrimitiveArray(type);
}
private static void reportCantFindSuperFunction(@NotNull JavaMethod javaMethod) {
String errorMessage = "Can't find super function for " + javaMethod + " defined in " + javaMethod.getContainingClass();
if (SystemInfo.isMac) {
LOG.error("Remove duplicates from your JDK definition\n" + errorMessage);
}
else {
LOG.error(errorMessage);
}
}
private static class VarargCheckResult {
public final JetType parameterType;
public final boolean isVararg;
@@ -67,8 +67,7 @@ public class TraceBasedExternalSignatureResolver implements ExternalSignatureRes
@NotNull List<TypeParameterDescriptor> typeParameters
) {
SignaturesPropagationData data =
new SignaturesPropagationData(owner, returnType, receiverType, valueParameters, typeParameters, method,
trace);
new SignaturesPropagationData(owner, returnType, receiverType, valueParameters, typeParameters, method);
return new PropagatedMethodSignature(data.getModifiedReturnType(), data.getModifiedReceiverType(),
data.getModifiedValueParameters(), data.getModifiedTypeParameters(), data.getSignatureErrors(),
data.getModifiedHasStableParameterNames(), data.getSuperFunctions());