diff --git a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kotlinSignature/SignaturesPropagationData.java b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kotlinSignature/SignaturesPropagationData.java index 27e2c3eec85..ada24bc4a2e 100644 --- a/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kotlinSignature/SignaturesPropagationData.java +++ b/compiler/frontend.java/src/org/jetbrains/jet/lang/resolve/java/kotlinSignature/SignaturesPropagationData.java @@ -268,7 +268,9 @@ public class SignaturesPropagationData { FqName fqName = DescriptorUtils.getFQName(klass).toSafe(); for (FunctionDescriptor fun : klass.getDefaultType().getMemberScope().getFunctions(functionName)) { - if (fun.getKind().isReal() && fun.getValueParameters().size() == parameterCount) { + CallableMemberDescriptor.Kind kind = fun.getKind(); + if ((kind == CallableMemberDescriptor.Kind.DECLARATION || kind == CallableMemberDescriptor.Kind.DELEGATION) && + fun.getValueParameters().size() == parameterCount) { PsiElement declaration = BindingContextUtils.descriptorToDeclaration(bindingContext, fun); if (declaration instanceof PsiMethod) { result.put(fqName, Pair.create(fun, (PsiMethod) declaration)); 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 3ec5bf7f34c..9e18fa32636 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 @@ -270,6 +270,8 @@ public final class JavaFunctionResolver { SimpleFunctionDescriptor function = resolveMethodToFunctionDescriptor(psiClass, method, scopeData, owner); if (function != null) { functionsFromCurrent.add(function); + + ContainerUtil.addIfNotNull(functionsFromCurrent, resolveSamAdapter(function)); } } @@ -368,6 +370,18 @@ public final class JavaFunctionResolver { return null; } + @Nullable + private SimpleFunctionDescriptor resolveSamAdapter(@NotNull SimpleFunctionDescriptor original) { + if (SingleAbstractMethodUtils.isSamAdapterNecessary(original)) { + SimpleFunctionDescriptor adapterFunction = SingleAbstractMethodUtils.createSamAdapterFunction(original); + + trace.record(BindingContext.SAM_ADAPTER_FUNCTION_TO_ORIGINAL, adapterFunction, original); + trace.record(BindingContext.SOURCE_DESCRIPTOR_FOR_SYNTHESIZED, adapterFunction, original); + return adapterFunction; + } + return null; + } + @NotNull public Set resolveFunctionGroup( @NotNull Name methodName, 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 7d021335ae8..8ff359cde44 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 @@ -23,10 +23,12 @@ import org.jetbrains.jet.lang.descriptors.annotations.AnnotationDescriptor; import org.jetbrains.jet.lang.descriptors.impl.SimpleFunctionDescriptorImpl; import org.jetbrains.jet.lang.descriptors.impl.TypeParameterDescriptorImpl; 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.lang.KotlinBuiltIns; @@ -40,7 +42,9 @@ public class SingleAbstractMethodUtils { private static List getAbstractMembers(@NotNull JetType type) { List abstractMembers = Lists.newArrayList(); for (DeclarationDescriptor member : type.getMemberScope().getAllDescriptors()) { - if (member instanceof CallableMemberDescriptor && ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT) { + if (member instanceof CallableMemberDescriptor && + ((CallableMemberDescriptor) member).getModality() == Modality.ABSTRACT && + ((CallableMemberDescriptor) member).getKind() != CallableMemberDescriptor.Kind.SYNTHESIZED) { abstractMembers.add((CallableMemberDescriptor) member); } } @@ -48,14 +52,17 @@ public class SingleAbstractMethodUtils { } @NotNull - private static JetType getFunctionTypeForFunction(@NotNull FunctionDescriptor function) { + private static JetType getFunctionTypeForSamType(@NotNull JetType samType) { + FunctionDescriptor function = getAbstractMethodOfSamType(samType); 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); + JetType functionType = KotlinBuiltIns.getInstance() + .getFunctionType(Collections.emptyList(), null, parameterTypes, returnType); + return TypeUtils.makeNullableAsSpecified(functionType, samType.isNullable()); } private static boolean isSamInterface(@NotNull ClassDescriptor klass) { @@ -91,7 +98,7 @@ public class SingleAbstractMethodUtils { SignaturesUtil.recreateTypeParametersAndReturnMapping(samInterface.getTypeConstructor().getParameters(), result); TypeSubstitutor typeParametersSubstitutor = SignaturesUtil.createSubstitutorForTypeParameters(traitToFunTypeParameters); - JetType parameterTypeUnsubstituted = getFunctionTypeForFunction(getAbstractMethodOfSamType(samInterface.getDefaultType())); + JetType parameterTypeUnsubstituted = getFunctionTypeForSamType(samInterface.getDefaultType()); JetType parameterType = typeParametersSubstitutor.substitute(parameterTypeUnsubstituted, Variance.IN_VARIANCE); assert parameterType != null : "couldn't substitute type: " + parameterType + ", substitutor = " + typeParametersSubstitutor; ValueParameterDescriptor parameter = new ValueParameterDescriptorImpl( @@ -127,6 +134,56 @@ public class SingleAbstractMethodUtils { return result; } + private static boolean isSamType(@NotNull JetType type) { + ClassifierDescriptor classifier = type.getConstructor().getDeclarationDescriptor(); + return classifier instanceof ClassDescriptorFromJvmBytecode && + ((ClassDescriptorFromJvmBytecode) classifier).isSamInterface(); + } + + public static boolean isSamAdapterNecessary(@NotNull SimpleFunctionDescriptor fun) { + for (ValueParameterDescriptor param : fun.getValueParameters()) { + if (isSamType(param.getType())) { + return true; + } + } + return false; + } + + @NotNull + public static SimpleFunctionDescriptor createSamAdapterFunction(@NotNull SimpleFunctionDescriptor original) { + SimpleFunctionDescriptorImpl result = new SimpleFunctionDescriptorImpl( + original.getContainingDeclaration(), + original.getAnnotations(), + original.getName(), + CallableMemberDescriptor.Kind.SYNTHESIZED + ); + + JetType returnType = original.getReturnType(); + + List valueParameters = Lists.newArrayList(); + for (ValueParameterDescriptor originalParam : original.getValueParameters()) { + JetType originalType = originalParam.getType(); + JetType newType = isSamType(originalType) ? getFunctionTypeForSamType(originalType) : originalType; + + ValueParameterDescriptor newParam = new ValueParameterDescriptorImpl( + result, originalParam.getIndex(), originalParam.getAnnotations(), originalParam.getName(), newType, false, null); + valueParameters.add(newParam); + } + + result.initialize( + null, + original.getExpectedThisObject(), + Collections.emptyList(), // TODO copy type parameters + valueParameters, + returnType, + original.getModality(), + original.getVisibility(), + false + ); + + return result; + } + @NotNull public static SimpleFunctionDescriptor getAbstractMethodOfSamType(@NotNull JetType type) { return (SimpleFunctionDescriptor) getAbstractMembers(type).get(0); diff --git a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java index 6f5a275b83e..4789fdafc64 100644 --- a/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java +++ b/compiler/frontend/src/org/jetbrains/jet/lang/resolve/BindingContext.java @@ -255,6 +255,7 @@ public interface BindingContext { WritableSlice IS_DECLARED_IN_JAVA = Slices.createSimpleSlice(); WritableSlice SAM_CONSTRUCTOR_TO_INTERFACE = Slices.createSimpleSlice(); + WritableSlice SAM_ADAPTER_FUNCTION_TO_ORIGINAL = Slices.createSimpleSlice(); WritableSlice SOURCE_DESCRIPTOR_FOR_SYNTHESIZED = Slices.createSimpleSlice(); @SuppressWarnings("UnusedDeclaration") diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.java b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.java new file mode 100644 index 00000000000..91bf27c96b2 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.java @@ -0,0 +1,9 @@ +package test; + +public class Basic { + public void foo(Runnable r) { + } + + public static void bar(Runnable r) { + } +} diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.txt b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.txt new file mode 100644 index 00000000000..8260f415824 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.txt @@ -0,0 +1,12 @@ +package test + +public open class Basic : java.lang.Object { + public constructor Basic() + public open /*synthesized*/ fun foo(/*0*/ p0 : (() -> jet.Unit)?) : jet.Unit + public open fun foo(/*0*/ p0 : java.lang.Runnable?) : jet.Unit +} + +package Basic { + public open /*synthesized*/ fun bar(/*0*/ p0 : (() -> jet.Unit)?) : jet.Unit + public open fun bar(/*0*/ p0 : java.lang.Runnable?) : jet.Unit +} diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.java b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.java new file mode 100644 index 00000000000..3f6e84a044e --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.java @@ -0,0 +1,12 @@ +package test; + +import java.io.FilenameFilter; +import java.util.Comparator; + +public class NonTrivialFunctionType { + public void foo(FilenameFilter filenameFilter) { + } + + public void foo(Comparator comparator) { + } +} \ No newline at end of file diff --git a/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.txt b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.txt new file mode 100644 index 00000000000..0e32ea5d021 --- /dev/null +++ b/compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.txt @@ -0,0 +1,9 @@ +package test + +public open class NonTrivialFunctionType : java.lang.Object { + public constructor NonTrivialFunctionType() + public open /*synthesized*/ fun foo(/*0*/ p0 : ((java.io.File?, jet.String?) -> jet.Boolean)?) : jet.Unit + public open /*synthesized*/ fun foo(/*0*/ p0 : ((jet.String?, jet.String?) -> jet.Int)?) : jet.Unit + public open fun foo(/*0*/ p0 : java.io.FilenameFilter?) : jet.Unit + public open fun foo(/*0*/ p0 : java.util.Comparator?) : jet.Unit +} diff --git a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java index 9e2d771691f..7f638b18621 100644 --- a/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/jvm/compiler/LoadJavaTestGenerated.java @@ -1172,6 +1172,7 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { } @TestMetadata("compiler/testData/loadJava/compiledJava/singleAbstractMethod") + @InnerTestClasses({SingleAbstractMethod.Adapter.class}) 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); @@ -1217,6 +1218,30 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/Runnable.java"); } + @TestMetadata("compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter") + public static class Adapter extends AbstractLoadJavaTest { + public void testAllFilesPresentInAdapter() throws Exception { + JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter"), Pattern.compile("^(.+)\\.java$"), true); + } + + @TestMetadata("Basic.java") + public void testBasic() throws Exception { + doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/Basic.java"); + } + + @TestMetadata("NonTrivialFunctionType.java") + public void testNonTrivialFunctionType() throws Exception { + doTestCompiledJava("compiler/testData/loadJava/compiledJava/singleAbstractMethod/adapter/NonTrivialFunctionType.java"); + } + + } + + public static Test innerSuite() { + TestSuite suite = new TestSuite("SingleAbstractMethod"); + suite.addTestSuite(SingleAbstractMethod.class); + suite.addTestSuite(Adapter.class); + return suite; + } } @TestMetadata("compiler/testData/loadJava/compiledJava/static") @@ -1269,7 +1294,7 @@ public class LoadJavaTestGenerated extends AbstractLoadJavaTest { suite.addTestSuite(ProtectedPackage.class); suite.addTestSuite(ProtectedStatic.class); suite.addTestSuite(SignaturePropagation.class); - suite.addTestSuite(SingleAbstractMethod.class); + suite.addTest(SingleAbstractMethod.innerSuite()); suite.addTestSuite(Static.class); return suite; }