From b891d6d87fcddf8c429b348d8b53e7b646ae1a43 Mon Sep 17 00:00:00 2001 From: Evgeny Gerashchenko Date: Thu, 21 Mar 2013 23:51:04 +0400 Subject: [PATCH] Loading SAM constructor functions for nested interfaces. --- .../resolve/java/provider/MembersCache.java | 40 +++++++++++- .../java/resolver/JavaFunctionResolver.java | 61 ++++++++++++++++--- .../java/resolver/JavaNamespaceResolver.java | 4 ++ .../java/sam/SingleAbstractMethodUtils.java | 4 +- .../singleAbstractMethod/Nested.java | 22 +++++++ .../singleAbstractMethod/Nested.txt | 31 ++++++++++ .../jvm/compiler/LoadJavaTestGenerated.java | 7 ++- 7 files changed, 155 insertions(+), 14 deletions(-) create mode 100644 compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.java create mode 100644 compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.txt diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/provider/MembersCache.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/provider/MembersCache.java index f74b5d21538..18e1d6bd9ba 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/provider/MembersCache.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/provider/MembersCache.java @@ -94,7 +94,7 @@ public final class MembersCache { if (JetClassAnnotation.get(psiClass).kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT) { processObjectClass(psiClass); } - if (!DescriptorResolverUtils.isKotlinClass(psiClass) && psiClass.isInterface()) { + if (!DescriptorResolverUtils.isKotlinClass(psiClass) && isFunctionalInterface(psiClass)) { processFunctionalInterface(psiClass); } } @@ -132,6 +132,7 @@ public final class MembersCache { public void process() { processFields(); processMethods(); + processNestedClasses(); } private boolean includeMember(PsiMemberWrapper member) { @@ -300,6 +301,18 @@ public final class MembersCache { private void createEmptyEntry(@NotNull Name identifier) { getOrCreateEmpty(identifier); } + + private void processNestedClasses() { + if (!staticMembers) { + return; + } + for (PsiClass nested : psiClass.getPsiClass().getInnerClasses()) { + if (isFunctionalInterface(nested)) { + NamedMembers namedMembers = getOrCreateEmpty(Name.identifier(nested.getName())); + namedMembers.setFunctionalInterface(nested); + } + } + } } private static boolean isObjectMethodInInterface(@NotNull PsiMember member) { @@ -313,8 +326,31 @@ public final class MembersCache { return false; } + return isObjectMethod((PsiMethod) member); + } + + private static boolean isObjectMethod(PsiMethod method) { String formattedMethod = PsiFormatUtil.formatMethod( - (PsiMethod) member, PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES); + method, PsiSubstitutor.EMPTY, SHOW_NAME | SHOW_PARAMETERS, SHOW_TYPE | SHOW_FQ_CLASS_NAMES); return OBJECT_METHODS.contains(formattedMethod); } + + public static boolean isFunctionalInterface(@NotNull PsiClass psiClass) { + if (!psiClass.isInterface()) { + return false; + } + + int foundAbstractMethods = 0; + for (PsiMethod method : psiClass.getAllMethods()) { + if (!isObjectMethod(method) && method.hasModifierProperty(PsiModifier.ABSTRACT)) { + foundAbstractMethods++; + + if (method.hasTypeParameters()) { + return false; + } + } + } + return foundAbstractMethods == 1; + } + } diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaFunctionResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaFunctionResolver.java index b7261b37f43..a7311f451f8 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaFunctionResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaFunctionResolver.java @@ -27,6 +27,7 @@ import com.intellij.psi.util.PsiFormatUtil; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.impl.NamespaceDescriptorParent; import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; import org.jetbrains.jet.lang.resolve.*; import org.jetbrains.jet.lang.resolve.java.*; @@ -40,6 +41,7 @@ import org.jetbrains.jet.lang.resolve.java.provider.PsiDeclarationProvider; 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; +import org.jetbrains.jet.lang.resolve.scopes.JetScope; import org.jetbrains.jet.lang.types.*; import org.jetbrains.jet.lang.types.checker.JetTypeChecker; @@ -279,6 +281,13 @@ public final class JavaFunctionResolver { } } + if (owner instanceof NamespaceDescriptor) { + SimpleFunctionDescriptor samConstructor = resolveSamConstructor((NamespaceDescriptor) owner, namedMembers); + if (samConstructor != null) { + functionsFromCurrent.add(samConstructor); + } + } + if (owner instanceof ClassDescriptor) { ClassDescriptor classDescriptor = (ClassDescriptor) owner; @@ -313,6 +322,44 @@ public final class JavaFunctionResolver { return functions; } + @Nullable + private static ClassDescriptor findClassInScope(@NotNull JetScope memberScope, @NotNull Name name) { + ClassifierDescriptor classifier = memberScope.getClassifier(name); + if (classifier instanceof ClassDescriptor) { + return (ClassDescriptor) classifier; + } + return null; + } + + // E.g. we have foo.Bar.Baz class declared in Java. It will produce the following descriptors structure: + // namespace foo + // +-- class Bar + // | +-- class Baz + // +-- namespace Bar + // We need to find class 'Baz' in namespace 'foo.Bar'. + @Nullable + private static ClassDescriptor findClassInNamespace(@NotNull NamespaceDescriptor namespace, @NotNull Name name) { + // First, try to find in namespace directly + ClassDescriptor found = findClassInScope(namespace.getMemberScope(), name); + if (found != null) { + return found; + } + + // If unsuccessful, try to find class of the same name as current (class 'foo.Bar') + NamespaceDescriptorParent parent = namespace.getContainingDeclaration(); + if (parent instanceof NamespaceDescriptor) { + // Calling recursively, looking for 'Bar' in 'foo' + ClassDescriptor classForCurrentNamespace = findClassInNamespace((NamespaceDescriptor) parent, namespace.getName()); + if (classForCurrentNamespace == null) { + return null; + } + + // Try to find nested class 'Baz' in class 'foo.Bar' + return findClassInScope(DescriptorUtils.getStaticNestedClassesScope(classForCurrentNamespace), name); + } + return null; + } + @Nullable private SimpleFunctionDescriptor resolveSamConstructor( @NotNull NamespaceDescriptor ownerDescriptor, @@ -320,15 +367,11 @@ public final class JavaFunctionResolver { ) { PsiClass functionalInterface = namedMembers.getFunctionalInterface(); if (functionalInterface != null) { - ClassifierDescriptor classifier = ownerDescriptor.getMemberScope().getClassifier(namedMembers.getName()); - if (classifier instanceof ClassDescriptor) { - ClassDescriptor klass = (ClassDescriptor) classifier; - - if (SingleAbstractMethodUtils.isFunctionalInterface(klass)) { - SimpleFunctionDescriptor constructorFunction = SingleAbstractMethodUtils.createConstructorFunction(klass); - trace.record(BindingContext.SAM_CONSTRUCTOR_TO_TRAIT, constructorFunction, klass); - return constructorFunction; - } + ClassDescriptor klass = findClassInNamespace(ownerDescriptor, namedMembers.getName()); + if (klass != null && SingleAbstractMethodUtils.isFunctionalInterface(klass)) { + SimpleFunctionDescriptor constructorFunction = SingleAbstractMethodUtils.createConstructorFunction(ownerDescriptor, klass); + trace.record(BindingContext.SAM_CONSTRUCTOR_TO_TRAIT, constructorFunction, klass); + return constructorFunction; } } return null; diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java index d8b3794a54c..ae972e7feb8 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/resolver/JavaNamespaceResolver.java @@ -35,6 +35,7 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils; import org.jetbrains.jet.lang.resolve.java.*; import org.jetbrains.jet.lang.resolve.java.descriptor.JavaNamespaceDescriptor; import org.jetbrains.jet.lang.resolve.java.kt.JetPackageClassAnnotation; +import org.jetbrains.jet.lang.resolve.java.provider.MembersCache; import org.jetbrains.jet.lang.resolve.java.scope.JavaBaseScope; import org.jetbrains.jet.lang.resolve.java.scope.JavaClassStaticMembersScope; import org.jetbrains.jet.lang.resolve.java.scope.JavaPackageScopeWithoutMembers; @@ -220,6 +221,9 @@ public final class JavaNamespaceResolver { } for (PsiClass nestedClass : psiClass.getInnerClasses()) { + if (MembersCache.isFunctionalInterface(nestedClass)) { + return true; + } if (nestedClass.hasModifierProperty(PsiModifier.STATIC) && hasStaticMembers(nestedClass)) { return true; } 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 5e9b8f937be..24bbcbfd4f1 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 @@ -73,9 +73,9 @@ public class SingleAbstractMethodUtils { return KotlinBuiltIns.getInstance().getFunctionType(Collections.emptyList(), null, parameterTypes, returnType); } - public static SimpleFunctionDescriptor createConstructorFunction(@NotNull ClassDescriptor klass) { + public static SimpleFunctionDescriptor createConstructorFunction(@NotNull ClassOrNamespaceDescriptor owner, @NotNull ClassDescriptor klass) { SimpleFunctionDescriptorImpl result = new SimpleFunctionDescriptorImpl( - klass.getContainingDeclaration(), + owner, klass.getAnnotations(), klass.getName(), CallableMemberDescriptor.Kind.SYNTHESIZED diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.java b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.java new file mode 100644 index 00000000000..e3055db9cf7 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.java @@ -0,0 +1,22 @@ +package test; + +public interface Nested { + + public interface Runnable { + void run(); + } + + public interface Deeper1 { + public interface Runnable { + void run(); + void run2(); + } + } + + public interface Deeper2 { + public interface Runnable { + void run(); + String toString(); + } + } +} diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.txt b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.txt new file mode 100644 index 00000000000..0846c48a143 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.txt @@ -0,0 +1,31 @@ +package test + +public trait Nested : java.lang.Object { + + public trait Deeper1 : java.lang.Object { + + public trait Runnable : java.lang.Object { + public abstract fun run() : jet.Unit + public abstract fun run2() : jet.Unit + } + } + + public trait Deeper2 : java.lang.Object { + + public trait Runnable : java.lang.Object { + public abstract fun run() : jet.Unit + } + } + + public trait Runnable : java.lang.Object { + public abstract fun run() : jet.Unit + } +} + +package Nested { + public /*synthesized*/ fun Runnable(/*0*/ function : () -> jet.Unit) : test.Nested.Runnable + + package Deeper2 { + public /*synthesized*/ fun Runnable(/*0*/ function : () -> jet.Unit) : test.Nested.Deeper2.Runnable + } +} diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java index 57f08fbf4d7..748d2219c06 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java @@ -1160,6 +1160,11 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/InterfaceWithObjectMethod.java"); } + @TestMetadata("Nested.java") + public void testNested() throws Exception { + doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/Nested.java"); + } + @TestMetadata("Runnable.java") public void testRunnable() throws Exception { doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java"); @@ -1177,7 +1182,7 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { public void testDeeplyInnerClass() throws Exception { doTestCompiledJava("compiler/testData/loadJava/compiledJava/static/DeeplyInnerClass.java"); } - + @TestMetadata("DeeplyNestedStatic.java") public void testDeeplyNestedStatic() throws Exception { doTestCompiledJava("compiler/testData/loadJava/compiledJava/static/DeeplyNestedStatic.java");