diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java index ed865688e25..6ce6ec408ee 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaClassResolver.java @@ -331,7 +331,7 @@ public final class JavaClassResolver { trace.record(BindingContext.CLASS, javaClass.getPsi(), classDescriptor); - JavaMethod samInterfaceMethod = SingleAbstractMethodUtils.getSamInterfaceMethod(javaClass, javaClass.getPsi().getProject()); + JavaMethod samInterfaceMethod = SingleAbstractMethodUtils.getSamInterfaceMethod(javaClass); if (samInterfaceMethod != null) { SimpleFunctionDescriptor abstractMethod = resolveFunctionOfSamInterface(samInterfaceMethod, classDescriptor); classDescriptor.setFunctionTypeForSamInterface(SingleAbstractMethodUtils.getFunctionTypeForAbstractMethod(abstractMethod)); diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaSupertypeResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaSupertypeResolver.java index d400bef8b37..186cf1b0b70 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaSupertypeResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaSupertypeResolver.java @@ -25,8 +25,8 @@ import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.java.JvmAbi; import org.jetbrains.jet.lang.resolve.java.TypeUsage; import org.jetbrains.jet.lang.resolve.java.structure.JavaClass; -import org.jetbrains.jet.lang.resolve.java.structure.JavaClassifierType; import org.jetbrains.jet.lang.resolve.java.structure.JavaClassifier; +import org.jetbrains.jet.lang.resolve.java.structure.JavaClassifierType; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.types.ErrorUtils; import org.jetbrains.jet.lang.types.JetType; @@ -42,7 +42,7 @@ import java.util.List; import static org.jetbrains.jet.lang.resolve.java.DescriptorSearchRule.IGNORE_KOTLIN_SOURCES; public final class JavaSupertypeResolver { - private static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object"); + public static final FqName OBJECT_FQ_NAME = new FqName("java.lang.Object"); private BindingTrace trace; private JavaTypeTransformer typeTransformer; @@ -116,7 +116,7 @@ public final class JavaSupertypeResolver { ) { List result = new ArrayList(supertypes.size()); for (JavaClassifierType type : supertypes) { - JavaClassifier resolved = type.resolve(); + JavaClassifier resolved = type.getClassifier(); if (resolved != null) { assert resolved instanceof JavaClass : "Supertype should be a class: " + resolved; FqName fqName = ((JavaClass) resolved).getFqName(); diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaTypeTransformer.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaTypeTransformer.java index 5a905a90e62..d2a25bbe4ab 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaTypeTransformer.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaTypeTransformer.java @@ -111,7 +111,7 @@ public class JavaTypeTransformer { @NotNull TypeUsage howThisTypeIsUsed, @NotNull TypeVariableResolver typeVariableResolver ) { - JavaClassifier javaClassifier = classifierType.resolve(); + JavaClassifier javaClassifier = classifierType.getClassifier(); if (javaClassifier == null) { return null; } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java index d4d419e7736..e4cfed65e39 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java @@ -17,10 +17,7 @@ package org.jetbrains.jet.lang.resolve.java.sam; import com.google.common.collect.Lists; -import com.intellij.openapi.project.Project; -import com.intellij.psi.*; -import com.intellij.psi.util.MethodSignatureBackedByPsiMethod; -import com.intellij.util.ArrayUtil; +import com.intellij.psi.util.TypeConversionUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; @@ -32,24 +29,20 @@ import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl; 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.java.structure.JavaClass; -import org.jetbrains.jet.lang.resolve.java.structure.JavaClassifier; -import org.jetbrains.jet.lang.resolve.java.structure.JavaClassifierType; -import org.jetbrains.jet.lang.resolve.java.structure.JavaMethod; +import org.jetbrains.jet.lang.resolve.java.resolver.JavaSupertypeResolver; +import org.jetbrains.jet.lang.resolve.java.structure.*; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; -import static com.intellij.psi.util.MethodSignatureUtil.areSignaturesErasureEqual; import static org.jetbrains.jet.lang.types.Variance.INVARIANT; public class SingleAbstractMethodUtils { + private SingleAbstractMethodUtils() { + } @NotNull public static List getAbstractMembers(@NotNull JetType type) { @@ -158,12 +151,14 @@ public class SingleAbstractMethodUtils { 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; + assert parameterType != null : "couldn't substitute type: " + parameterTypeUnsubstituted + + ", substitutor = " + typeParameters.substitutor; ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( result, 0, Collections.emptyList(), Name.identifier("function"), parameterType, false, null); JetType returnType = typeParameters.substitutor.substitute(samInterface.getDefaultType(), Variance.OUT_VARIANCE); - assert returnType != null : "couldn't substitute type: " + returnType + ", substitutor = " + typeParameters.substitutor; + assert returnType != null : "couldn't substitute type: " + samInterface.getDefaultType() + + ", substitutor = " + typeParameters.substitutor; result.initialize( null, @@ -263,7 +258,8 @@ public class SingleAbstractMethodUtils { } else { returnType = typeParameters.substitutor.substitute(returnTypeUnsubstituted, Variance.OUT_VARIANCE); - assert returnType != null : "couldn't substitute type: " + returnType + ", substitutor = " + typeParameters.substitutor; + assert returnType != null : "couldn't substitute type: " + returnTypeUnsubstituted + + ", substitutor = " + typeParameters.substitutor; } List valueParameters = Lists.newArrayList(); @@ -318,16 +314,13 @@ public class SingleAbstractMethodUtils { return getAbstractMethodOfSamType(samInterface.getDefaultType()); } - private SingleAbstractMethodUtils() { - } - public static boolean isSamInterface(@NotNull JavaClass javaClass) { - return getSamInterfaceMethod(javaClass, javaClass.getPsi().getProject()) != null; + return getSamInterfaceMethod(javaClass) != null; } // Returns null if not SAM interface @Nullable - public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass, @NotNull Project project) { + public static JavaMethod getSamInterfaceMethod(@NotNull JavaClass javaClass) { FqName fqName = javaClass.getFqName(); if (fqName == null || fqName.firstSegmentIs(KotlinBuiltIns.BUILT_INS_PACKAGE_NAME)) { return null; @@ -336,25 +329,18 @@ public class SingleAbstractMethodUtils { return null; } - return findOnlyAbstractMethod(javaClass, project); + return findOnlyAbstractMethod(javaClass); } @Nullable - private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass, @NotNull Project project) { - JavaClassifierType classifierType = new JavaClassifierType(JavaPsiFacade.getElementFactory(project).createType(javaClass.getPsi())); - + private static JavaMethod findOnlyAbstractMethod(@NotNull JavaClass javaClass) { OnlyAbstractMethodFinder finder = new OnlyAbstractMethodFinder(); - if (finder.find(classifierType)) { + if (finder.find(javaClass.getDefaultType())) { 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 descriptors; public final TypeSubstitutor substitutor; @@ -374,38 +360,35 @@ public class SingleAbstractMethodUtils { } private static class OnlyAbstractMethodFinder { - private MethodSignatureBackedByPsiMethod found; + private JavaMethod foundMethod; + private JavaTypeSubstitutor foundClassSubstitutor; private boolean find(@NotNull JavaClassifierType classifierType) { - PsiClassType.ClassResolveResult classResolveResult = classifierType.getPsi().resolveGenerics(); - PsiSubstitutor classSubstitutor = classResolveResult.getSubstitutor(); - JavaClassifier javaClassifier = classifierType.resolve(); - if (javaClassifier == null) { + JavaTypeSubstitutor classSubstitutor = classifierType.getSubstitutor(); + JavaClassifier classifier = classifierType.getClassifier(); + if (classifier == null) { return false; // can't resolve class -> not a SAM interface } - assert javaClassifier instanceof JavaClass : "Classifier should be a class here: " + javaClassifier; - JavaClass javaClass = (JavaClass) javaClassifier; - if (new FqName(CommonClassNames.JAVA_LANG_OBJECT).equals(javaClass.getFqName())) { + assert classifier instanceof JavaClass : "Classifier should be a class here: " + classifier; + JavaClass javaClass = (JavaClass) classifier; + if (JavaSupertypeResolver.OBJECT_FQ_NAME.equals(javaClass.getFqName())) { return true; } for (JavaMethod method : javaClass.getMethods()) { - PsiMethod psiMethod = method.getPsi(); - if (DescriptorResolverUtils.isObjectMethod(psiMethod)) { // e.g., ignore toString() declared in interface + if (DescriptorResolverUtils.isObjectMethod(method.getPsi())) { // 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 (found == null) { - found = (MethodSignatureBackedByPsiMethod) psiMethod.getSignature(classSubstitutor); + if (foundMethod == null) { + foundMethod = method; + foundClassSubstitutor = classSubstitutor; continue; } - if (!found.getName().equals(method.getName().asString())) { - return false; // optimizing heuristic - } - MethodSignatureBackedByPsiMethod current = (MethodSignatureBackedByPsiMethod) psiMethod.getSignature(classSubstitutor); - if (!areSignaturesErasureEqual(current, found) || isVarargMethod(psiMethod) != isVarargMethod(found.getMethod())) { + + if (!areSignaturesErasureEqual(method, classSubstitutor, foundMethod, foundClassSubstitutor)) { return false; // different signatures } } @@ -419,9 +402,41 @@ public class SingleAbstractMethodUtils { 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.isConstructor() != method2.isConstructor()) return false; + if (!method1.isConstructor() && !method1.getName().equals(method2.getName())) return false; + + if (method1.isVararg() != method2.isVararg()) return false; + + Collection parameters1 = method1.getValueParameters(); + Collection parameters2 = method2.getValueParameters(); + if (parameters1.size() != parameters2.size()) return false; + + for (Iterator it1 = parameters1.iterator(), it2 = parameters2.iterator(); it1.hasNext(); ) { + JavaType type1 = erasure(substitutor1.substitute(it1.next().getType()), substitutor1); + JavaType type2 = erasure(substitutor2.substitute(it2.next().getType()), substitutor2); + if (!type1.equals(type2)) return false; + } + + return true; + } + + @NotNull + private static JavaType erasure(@NotNull JavaType type, @NotNull JavaTypeSubstitutor substitutor) { + return JavaType.create(TypeConversionUtil.erasure(type.getPsi(), substitutor.getPsi())); + } + @Nullable private JavaMethod getFoundMethod() { - return found == null ? null : new JavaMethod(found.getMethod()); + return foundMethod; } } } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClass.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClass.java index db9f02f8c35..9c52124deab 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClass.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClass.java @@ -16,10 +16,7 @@ package org.jetbrains.jet.lang.resolve.java.structure; -import com.intellij.psi.PsiAnnotation; -import com.intellij.psi.PsiClass; -import com.intellij.psi.PsiModifierList; -import com.intellij.psi.PsiTypeParameter; +import com.intellij.psi.*; import com.intellij.util.containers.ContainerUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -168,4 +165,9 @@ public class JavaClass extends JavaClassifier public JavaAnnotation findAnnotation(@NotNull FqName fqName) { return JavaElementUtil.findAnnotation(this, fqName); } + + @NotNull + public JavaClassifierType getDefaultType() { + return new JavaClassifierType(JavaPsiFacade.getElementFactory(getPsi().getProject()).createType(getPsi())); + } } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClassifierType.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClassifierType.java index 51d96d9bd41..19115fbd2b3 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClassifierType.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaClassifierType.java @@ -30,6 +30,18 @@ import java.util.List; import static org.jetbrains.jet.lang.resolve.java.structure.JavaElementCollectionFromPsiArrayUtil.types; public class JavaClassifierType extends JavaType { + private static class ResolutionResult { + private final JavaClassifier classifier; + private final JavaTypeSubstitutor substitutor; + + private ResolutionResult(@Nullable JavaClassifier classifier, @NotNull JavaTypeSubstitutor substitutor) { + this.classifier = classifier; + this.substitutor = substitutor; + } + } + + private ResolutionResult resolutionResult; + public JavaClassifierType(@NotNull PsiClassType psiClassType) { super(psiClassType); } @@ -41,9 +53,26 @@ public class JavaClassifierType extends JavaType { } @Nullable - public JavaClassifier resolve() { - PsiClass psiClass = getPsi().resolve(); - return psiClass == null ? null : JavaClassifier.create(psiClass); + public JavaClassifier getClassifier() { + resolve(); + return resolutionResult.classifier; + } + + @NotNull + public JavaTypeSubstitutor getSubstitutor() { + resolve(); + return resolutionResult.substitutor; + } + + private void resolve() { + if (resolutionResult == null) { + PsiClassType.ClassResolveResult result = getPsi().resolveGenerics(); + PsiClass psiClass = result.getElement(); + resolutionResult = new ResolutionResult( + psiClass == null ? null : JavaClassifier.create(psiClass), + new JavaTypeSubstitutor(result.getSubstitutor()) + ); + } } @NotNull diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaType.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaType.java index 7462905417d..757fdb679a0 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaType.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaType.java @@ -66,4 +66,9 @@ public abstract class JavaType { } }); } + + @Override + public boolean equals(Object obj) { + return obj instanceof JavaType && getPsi().equals(((JavaType) obj).getPsi()); + } } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaTypeSubstitutor.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaTypeSubstitutor.java new file mode 100644 index 00000000000..09e36abfb03 --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/structure/JavaTypeSubstitutor.java @@ -0,0 +1,38 @@ +/* + * Copyright 2010-2013 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.structure; + +import com.intellij.psi.PsiSubstitutor; +import org.jetbrains.annotations.NotNull; + +public class JavaTypeSubstitutor { + private final PsiSubstitutor psiSubstitutor; + + public JavaTypeSubstitutor(@NotNull PsiSubstitutor psiSubstitutor) { + this.psiSubstitutor = psiSubstitutor; + } + + @NotNull + public PsiSubstitutor getPsi() { + return psiSubstitutor; + } + + @NotNull + public JavaType substitute(@NotNull JavaType type) { + return JavaType.create(psiSubstitutor.substitute(type.getPsi())); + } +}