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 af415ebaae0..89621ecb9d3 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 @@ -19,10 +19,7 @@ package org.jetbrains.jet.lang.resolve.java.provider; import com.intellij.psi.*; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import org.jetbrains.jet.lang.resolve.java.JvmAbi; -import org.jetbrains.jet.lang.resolve.java.JvmStdlibNames; -import org.jetbrains.jet.lang.resolve.java.PsiClassFinder; -import org.jetbrains.jet.lang.resolve.java.TypeSource; +import org.jetbrains.jet.lang.resolve.java.*; import org.jetbrains.jet.lang.resolve.java.kt.JetClassAnnotation; import org.jetbrains.jet.lang.resolve.java.prop.PropertyNameUtils; import org.jetbrains.jet.lang.resolve.java.prop.PropertyParseResult; @@ -73,27 +70,28 @@ public final class MembersCache { //TODO: List classes = psiPackage != null ? finder.findPsiClasses(psiPackage) : finder.findInnerPsiClasses(psiClass); - membersCache.new ObjectClassProcessor(classes).process(); + membersCache.new ExtraPackageMembersProcessor(classes).process(); return membersCache; } - private class ObjectClassProcessor { + private class ExtraPackageMembersProcessor { // 'extra' means that PSI elements for these members are not just top-level classes @NotNull private final List psiClasses; - private ObjectClassProcessor(@NotNull List classes) { + private ExtraPackageMembersProcessor(@NotNull List classes) { psiClasses = classes; } private void process() { for (PsiClass psiClass : psiClasses) { - if (!psiClass.isPhysical()) { // to filter out JetLightClasses - continue; + if (!(psiClass instanceof JetJavaMirrorMarker)) { // to filter out JetLightClasses + if (JetClassAnnotation.get(psiClass).kind() == JvmStdlibNames.FLAG_CLASS_KIND_OBJECT) { + processObjectClass(psiClass); + } + if (!DescriptorResolverUtils.isKotlinClass(psiClass) && psiClass.isInterface()) { + processFunctionalInterface(psiClass); + } } - if (JetClassAnnotation.get(psiClass).kind() != JvmStdlibNames.FLAG_CLASS_KIND_OBJECT) { - continue; - } - processObjectClass(psiClass); } } @@ -106,6 +104,10 @@ public final class MembersCache { namedMembers.addPropertyAccessor(new PropertyPsiDataElement(new PsiFieldWrapper(instanceField), type, null)); } } + + private void processFunctionalInterface(@NotNull PsiClass psiClass) { + getOrCreateEmpty(Name.identifier(psiClass.getName())); + } } private class ClassMemberProcessor { 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 new file mode 100644 index 00000000000..dbce610633f --- /dev/null +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/sam/SingleAbstractMethodUtils.java @@ -0,0 +1,95 @@ +/* + * 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.sam; + +import com.google.common.collect.Lists; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.jet.lang.descriptors.*; +import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; +import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; +import org.jetbrains.jet.lang.descriptors.impl.ValueParameterDescriptorImpl; +import org.jetbrains.jet.lang.resolve.name.Name; +import org.jetbrains.jet.lang.types.JetType; +import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class SingleAbstractMethodUtils { + public static boolean isFunctionalInterface(@NotNull ClassDescriptor klass) { + if (klass.getKind() != ClassKind.TRAIT) { + return false; + } + + List abstractMembers = getAbstractMembers(klass); + return abstractMembers.size() == 1 && abstractMembers.get(0) instanceof SimpleFunctionDescriptor; + } + + @NotNull + private static List getAbstractMembers(@NotNull ClassDescriptor klass) { + List abstractMembers = Lists.newArrayList(); + for (DeclarationDescriptor member : klass.getDefaultType().getMemberScope().getAllDescriptors()) { + if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) { + abstractMembers.add((CallableMemberDescriptor) member); + } + } + return abstractMembers; + } + + @NotNull + private static JetType getFunctionalTypeForFunction(@NotNull FunctionDescriptor function) { + // TODO substitute type parameters of class + // TODO substitute type parameters of function with star projections + JetType returnType = function.getReturnType(); + assert returnType != null : "function is not initialized: " + function; + List parameterTypes = Lists.newArrayList(); + for (ValueParameterDescriptor parameter : function.getValueParameters()) { + parameterTypes.add(parameter.getType()); + } + return KotlinBuiltIns.getInstance().getFunctionType(Collections.emptyList(), null, parameterTypes, returnType); + } + + public static SimpleFunctionDescriptor createConstructorFunction(@NotNull ClassDescriptor klass) { + SimpleFunctionDescriptorImpl result = new SimpleFunctionDescriptorImpl( + klass.getContainingDeclaration(), + klass.getAnnotations(), + klass.getName(), + CallableMemberDescriptor.Kind.SYNTHESIZED + ); + + JetType parameterType = getFunctionalTypeForFunction((SimpleFunctionDescriptor) getAbstractMembers(klass).get(0)); + ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( + result, 0, Collections.emptyList(), Name.identifier("function"), parameterType, false, null); + + result.initialize( + null, + null, + Lists.newArrayList(), // TODO recreate type parameters of class + Arrays.asList(parameter), + klass.getDefaultType(), // TODO substitute type parameters of class + Modality.FINAL, + klass.getVisibility(), + false + ); + + return result; + } + + private SingleAbstractMethodUtils() { + } +} diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/scope/JavaPackageScopeWithoutMembers.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/scope/JavaPackageScopeWithoutMembers.java index 2e4a1bdc231..5b4c2992b58 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/scope/JavaPackageScopeWithoutMembers.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/scope/JavaPackageScopeWithoutMembers.java @@ -18,10 +18,12 @@ package org.jetbrains.jet.lang.resolve.java.scope; import org.jetbrains.annotations.NotNull; import org.jetbrains.jet.lang.descriptors.ClassDescriptor; +import org.jetbrains.jet.lang.descriptors.ClassifierDescriptor; import org.jetbrains.jet.lang.descriptors.FunctionDescriptor; import org.jetbrains.jet.lang.descriptors.NamespaceDescriptor; import org.jetbrains.jet.lang.resolve.java.JavaSemanticServices; import org.jetbrains.jet.lang.resolve.java.provider.PackagePsiDeclarationProvider; +import org.jetbrains.jet.lang.resolve.java.sam.SingleAbstractMethodUtils; import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.Name; @@ -42,7 +44,15 @@ public final class JavaPackageScopeWithoutMembers extends JavaPackageScope { @Override @NotNull protected Set computeFunctionDescriptor(@NotNull Name name) { - return Collections.emptySet(); + ClassifierDescriptor classifier = getClassifier(name); + if (classifier instanceof ClassDescriptor && SingleAbstractMethodUtils.isFunctionalInterface((ClassDescriptor) classifier)) { + + FunctionDescriptor constructorFunction = SingleAbstractMethodUtils.createConstructorFunction((ClassDescriptor) classifier); + + return Collections.singleton(constructorFunction); + } + + return Collections.emptySet(); } @NotNull diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java new file mode 100644 index 00000000000..94d6e1921a1 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java @@ -0,0 +1,5 @@ +package test; + +public interface Runnable { + void run(); +} diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.txt b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.txt new file mode 100644 index 00000000000..afe6b191f68 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.txt @@ -0,0 +1,7 @@ +package test + +public /*synthesized*/ fun Runnable(/*0*/ function : () -> jet.Unit) : test.Runnable + +public trait Runnable : java.lang.Object { + public abstract fun run() : jet.Unit +} diff --git a/compiler/testData/loadKotlin/class/SingleAbstractMethod.kt b/compiler/testData/loadKotlin/class/SingleAbstractMethod.kt new file mode 100644 index 00000000000..71f52f15c73 --- /dev/null +++ b/compiler/testData/loadKotlin/class/SingleAbstractMethod.kt @@ -0,0 +1,5 @@ +package test + +public trait SingleAbstractMethod { + fun foo() +} \ No newline at end of file diff --git a/compiler/testData/loadKotlin/class/SingleAbstractMethod.txt b/compiler/testData/loadKotlin/class/SingleAbstractMethod.txt new file mode 100644 index 00000000000..0a1306ff607 --- /dev/null +++ b/compiler/testData/loadKotlin/class/SingleAbstractMethod.txt @@ -0,0 +1,5 @@ +package test + +public trait SingleAbstractMethod { + internal abstract fun foo() : jet.Unit +} diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java index a05b057f960..babf01c3e3c 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadCompiledKotlinTestGenerated.java @@ -178,6 +178,11 @@ public class LoadCompiledKotlinTestGenerated extends AbstractLoadCompiledKotlinT doTestWithAccessors("compiler/testData/loadKotlin/class/NestedGenericClass.kt"); } + @TestMetadata("SingleAbstractMethod.kt") + public void testSingleAbstractMethod() throws Exception { + doTestWithAccessors("compiler/testData/loadKotlin/class/SingleAbstractMethod.kt"); + } + @TestMetadata("Trait.kt") public void testTrait() throws Exception { doTestWithAccessors("compiler/testData/loadKotlin/class/Trait.kt"); diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java index 84ef06611da..3d8320c39bc 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java @@ -971,7 +971,7 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { } @TestMetadata("compiler/testData/loadJava/compiledJava") - @InnerTestClasses({CompiledJava.Annotations.class, CompiledJava.ProtectedPackage.class, CompiledJava.ProtectedStatic.class, CompiledJava.SignaturePropagation.class, CompiledJava.Static.class}) + @InnerTestClasses({CompiledJava.Annotations.class, CompiledJava.ProtectedPackage.class, CompiledJava.ProtectedStatic.class, CompiledJava.SignaturePropagation.class, CompiledJava.SingleAbstractMethod.class, CompiledJava.Static.class}) public static class CompiledJava extends AbstractLoadJavaTest { public void testAllFilesPresentInCompiledJava() throws Exception { JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/compiledJava"), Pattern.compile("^(.+)\\.java$"), true); @@ -1124,6 +1124,19 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { } + @TestMetadata("compiler/testData/loadJava/compiledJava/singleAbstractMethod") + public static class SingleAbstractMethod extends AbstractLoadJavaTest { + public void testAllFilesPresentInSingleAbstractMethod() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/compiledJava/singleAbstractMethod"), Pattern.compile("^(.+)\\.java$"), true); + } + + @TestMetadata("Runnable.java") + public void testRunnable() throws Exception { + doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java"); + } + + } + @TestMetadata("compiler/testData/loadJava/compiledJava/static") public static class Static extends AbstractLoadJavaTest { public void testAllFilesPresentInStatic() throws Exception { @@ -1169,6 +1182,7 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { suite.addTestSuite(ProtectedPackage.class); suite.addTestSuite(ProtectedStatic.class); suite.addTestSuite(SignaturePropagation.class); + suite.addTestSuite(SingleAbstractMethod.class); suite.addTestSuite(Static.class); return suite; } diff --git a/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java b/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java index 9a1d56bc9f8..aa39f5c5a44 100644 --- a/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/lang/resolve/lazy/LazyResolveNamespaceComparingTestGenerated.java @@ -180,6 +180,11 @@ public class LazyResolveNamespaceComparingTestGenerated extends AbstractLazyReso doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/class/NestedGenericClass.kt"); } + @TestMetadata("SingleAbstractMethod.kt") + public void testSingleAbstractMethod() throws Exception { + doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/class/SingleAbstractMethod.kt"); + } + @TestMetadata("Trait.kt") public void testTrait() throws Exception { doTestCheckingPrimaryConstructorsAndAccessors("compiler/testData/loadKotlin/class/Trait.kt");