From 827d9d48c17e6ec12394e2efeb96a320dacaa672 Mon Sep 17 00:00:00 2001 From: Dmitry Jemerov Date: Wed, 25 Mar 2015 15:13:47 +0100 Subject: [PATCH] Correctly apply SAM conversions in superclass constructor calls #KT-5452 Fixed --- .../kotlin/codegen/ExpressionCodegen.java | 12 +++++----- .../codegen/ImplementationBodyCodegen.java | 2 +- .../kotlin/codegen/SamCodegenUtil.java | 5 ++++ .../binding/CodegenAnnotatingVisitor.java | 12 +++++++++- .../codegen/binding/CodegenBinding.java | 2 +- .../kotlin/codegen/state/JetTypeMapper.java | 2 +- .../sam/adapters/localClass.java | 11 +++++++++ .../boxAgainstJava/sam/adapters/localClass.kt | 8 +++++++ .../sam/adapters/localObjectConstructor.java | 11 +++++++++ .../sam/adapters/localObjectConstructor.kt | 6 +++++ .../localObjectConstructorWithFnValue.java | 11 +++++++++ .../localObjectConstructorWithFnValue.kt | 7 ++++++ .../adapters/superconstructorWithClosure.java | 5 ++++ .../adapters/superconstructorWithClosure.kt | 9 +++++++ .../AbstractBlackBoxCodegenTest.java | 1 - ...ackBoxAgainstJavaCodegenTestGenerated.java | 24 +++++++++++++++++++ 16 files changed, 117 insertions(+), 11 deletions(-) create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.java create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.kt create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.java create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.kt create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.java create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.kt create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.java create mode 100644 compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.kt diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java index fb51d62e79e..683926b06ab 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ExpressionCodegen.java @@ -1446,9 +1446,10 @@ public class ExpressionCodegen extends JetVisitor implem if (superCall != null) { // For an anonymous object, we should also generate all non-default arguments that it captures for its super call ConstructorDescriptor superConstructor = superCall.getResultingDescriptor(); + ConstructorDescriptor constructorToCall = SamCodegenUtil.resolveSamAdapter(superConstructor); List superValueParameters = superConstructor.getValueParameters(); int params = superValueParameters.size(); - List superMappedTypes = typeMapper.mapToCallableMethod(superConstructor).getValueParameterTypes(); + List superMappedTypes = typeMapper.mapToCallableMethod(constructorToCall).getValueParameterTypes(); assert superMappedTypes.size() >= params : String .format("Incorrect number of mapped parameters vs arguments: %d < %d for %s", superMappedTypes.size(), params, classDescriptor); @@ -1474,7 +1475,7 @@ public class ExpressionCodegen extends JetVisitor implem assert constructors.size() == 1 : "Unexpected number of constructors for class: " + classDescriptor + " " + constructors; ConstructorDescriptor constructorDescriptor = KotlinPackage.single(constructors); - JvmMethodSignature constructor = typeMapper.mapSignature(constructorDescriptor); + JvmMethodSignature constructor = typeMapper.mapSignature(SamCodegenUtil.resolveSamAdapter(constructorDescriptor)); v.invokespecial(type.getInternalName(), "", constructor.getAsmMethod().getDescriptor(), false); return Unit.INSTANCE$; } @@ -2313,8 +2314,7 @@ public class ExpressionCodegen extends JetVisitor implem @NotNull private CallableMethod resolveToCallableMethod(@NotNull FunctionDescriptor fd, boolean superCall, @NotNull CodegenContext context) { - SimpleFunctionDescriptor originalOfSamAdapter = (SimpleFunctionDescriptor) SamCodegenUtil.getOriginalIfSamAdapter(fd); - return typeMapper.mapToCallableMethod(originalOfSamAdapter != null ? originalOfSamAdapter : fd, superCall, context); + return typeMapper.mapToCallableMethod(SamCodegenUtil.resolveSamAdapter(fd), superCall, context); } public void invokeMethodWithArguments( @@ -3593,8 +3593,8 @@ public class ExpressionCodegen extends JetVisitor implem // See StackValue.receiver for more info pushClosureOnStack(constructor.getContainingDeclaration(), dispatchReceiver == null, defaultCallGenerator); - ConstructorDescriptor originalOfSamAdapter = (ConstructorDescriptor) SamCodegenUtil.getOriginalIfSamAdapter(constructor); - CallableMethod method = typeMapper.mapToCallableMethod(originalOfSamAdapter == null ? constructor : originalOfSamAdapter); + constructor = SamCodegenUtil.resolveSamAdapter(constructor); + CallableMethod method = typeMapper.mapToCallableMethod(constructor); invokeMethodWithArguments(method, resolvedCall, StackValue.none()); return Unit.INSTANCE$; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java index 6928ef728f5..8a6c71bad3c 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/ImplementationBodyCodegen.java @@ -1466,7 +1466,7 @@ public class ImplementationBodyCodegen extends ClassBodyCodegen { return; } iv.load(0, OBJECT_TYPE); - ConstructorDescriptor delegateConstructor = delegationConstructorCall.getResultingDescriptor(); + ConstructorDescriptor delegateConstructor = SamCodegenUtil.resolveSamAdapter(delegationConstructorCall.getResultingDescriptor()); CallableMethod delegateConstructorCallable = typeMapper.mapToCallableMethod(delegateConstructor); CallableMethod callable = typeMapper.mapToCallableMethod(constructorDescriptor); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamCodegenUtil.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamCodegenUtil.java index 3a507212d6a..cc2baa2ab86 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/SamCodegenUtil.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/SamCodegenUtil.java @@ -42,6 +42,11 @@ public class SamCodegenUtil { return null; } + public static T resolveSamAdapter(@NotNull T descriptor) { + FunctionDescriptor original = getOriginalIfSamAdapter(descriptor); + return original != null ? (T) original : descriptor; + } + private SamCodegenUtil() { } } diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java index 8c18c194f6d..a1fde2eb99b 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenAnnotatingVisitor.java @@ -386,6 +386,10 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { @Override public void visitCallExpression(@NotNull JetCallExpression expression) { super.visitCallExpression(expression); + checkSamCall(expression); + } + + private void checkSamCall(@NotNull JetCallElement expression) { ResolvedCall call = CallUtilPackage.getResolvedCall(expression, bindingContext); if (call == null) return; @@ -416,7 +420,13 @@ class CodegenAnnotatingVisitor extends JetVisitorVoid { } } - private void recordSamConstructorIfNeeded(@NotNull JetCallExpression expression, @NotNull ResolvedCall call) { + @Override + public void visitDelegationToSuperCallSpecifier(@NotNull JetDelegatorToSuperCall call) { + super.visitDelegationToSuperCallSpecifier(call); + checkSamCall(call); + } + + private void recordSamConstructorIfNeeded(@NotNull JetCallElement expression, @NotNull ResolvedCall call) { CallableDescriptor callableDescriptor = call.getResultingDescriptor(); if (!(callableDescriptor.getOriginal() instanceof SamConstructorDescriptor)) return; diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java index 16d60c063bd..9faa4f7012f 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/binding/CodegenBinding.java @@ -62,7 +62,7 @@ public class CodegenBinding { public static final WritableSlice SAM_VALUE = Slices.createSimpleSlice(); - public static final WritableSlice SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice(); + public static final WritableSlice SAM_CONSTRUCTOR_TO_ARGUMENT = Slices.createSimpleSlice(); public static final WritableSlice MAPPING_FOR_WHEN_BY_ENUM = Slices.sliceBuilder().build(); diff --git a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java index 4a6b2e28ca2..dd0ed8bf2ad 100644 --- a/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java +++ b/compiler/backend/src/org/jetbrains/kotlin/codegen/state/JetTypeMapper.java @@ -924,7 +924,7 @@ public class JetTypeMapper { @NotNull ResolvedCall superCall, boolean hasOuter ) { - ConstructorDescriptor superDescriptor = superCall.getResultingDescriptor(); + ConstructorDescriptor superDescriptor = SamCodegenUtil.resolveSamAdapter(superCall.getResultingDescriptor()); List valueArguments = superCall.getValueArgumentsByIndex(); assert valueArguments != null : "Failed to arrange value arguments by index: " + superDescriptor; diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.java b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.java new file mode 100644 index 00000000000..c47e8ab052e --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.java @@ -0,0 +1,11 @@ +class JavaClass { + private Runnable r; + + public JavaClass(Runnable r) { + this.r = r; + } + + public void run() { + r.run(); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.kt b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.kt new file mode 100644 index 00000000000..22920f97b76 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.kt @@ -0,0 +1,8 @@ +var status: String = "fail" // global property to avoid issues with accessing closure from local class (KT-4174) + +fun box(): String { + class C() : JavaClass({status = "OK"}) {} + C().run() + return status +} + diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.java b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.java new file mode 100644 index 00000000000..c47e8ab052e --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.java @@ -0,0 +1,11 @@ +class JavaClass { + private Runnable r; + + public JavaClass(Runnable r) { + this.r = r; + } + + public void run() { + r.run(); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.kt b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.kt new file mode 100644 index 00000000000..d704e59c95a --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.kt @@ -0,0 +1,6 @@ +fun box(): String { + var v = "FAIL" + val x = object : JavaClass({-> v = "OK"}) {} + x.run() + return v +} diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.java b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.java new file mode 100644 index 00000000000..c47e8ab052e --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.java @@ -0,0 +1,11 @@ +class JavaClass { + private Runnable r; + + public JavaClass(Runnable r) { + this.r = r; + } + + public void run() { + r.run(); + } +} \ No newline at end of file diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.kt b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.kt new file mode 100644 index 00000000000..0d434bf48dc --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.kt @@ -0,0 +1,7 @@ +fun box(): String { + var v = "FAIL" + val f = {-> v = "OK"} + val x = object : JavaClass(f) {} + x.run() + return v +} diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.java b/compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.java new file mode 100644 index 00000000000..2107dda85a7 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.java @@ -0,0 +1,5 @@ +class JavaClass { + JavaClass(Runnable r) { + if (r != null) r.run(); + } +} diff --git a/compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.kt b/compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.kt new file mode 100644 index 00000000000..1f54e9e8ba8 --- /dev/null +++ b/compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.kt @@ -0,0 +1,9 @@ +var status: String = "fail" // global property to avoid issues with accessing closure from local class (KT-4174) + +class KotlinClass(): JavaClass({status="OK"}) { +} + +fun box(): String { + KotlinClass() + return status +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/AbstractBlackBoxCodegenTest.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/AbstractBlackBoxCodegenTest.java index d0a7e2b418a..ff763255283 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/AbstractBlackBoxCodegenTest.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/AbstractBlackBoxCodegenTest.java @@ -27,7 +27,6 @@ import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles; import org.jetbrains.kotlin.cli.jvm.compiler.JetCoreEnvironment; import org.jetbrains.kotlin.codegen.CodegenTestCase; import org.jetbrains.kotlin.codegen.GenerationUtils; -import org.jetbrains.kotlin.codegen.InlineTestUtil; import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.psi.JetFile; import org.jetbrains.kotlin.test.ConfigurationKind; diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java index 55587ee04fa..894864c52ac 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxAgainstJavaCodegenTestGenerated.java @@ -499,6 +499,24 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod doTestAgainstJava(fileName); } + @TestMetadata("localClass.kt") + public void testLocalClass() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/sam/adapters/localClass.kt"); + doTestAgainstJava(fileName); + } + + @TestMetadata("localObjectConstructor.kt") + public void testLocalObjectConstructor() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructor.kt"); + doTestAgainstJava(fileName); + } + + @TestMetadata("localObjectConstructorWithFnValue.kt") + public void testLocalObjectConstructorWithFnValue() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/sam/adapters/localObjectConstructorWithFnValue.kt"); + doTestAgainstJava(fileName); + } + @TestMetadata("nonLiteralAndLiteralRunnable.kt") public void testNonLiteralAndLiteralRunnable() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/sam/adapters/nonLiteralAndLiteralRunnable.kt"); @@ -547,6 +565,12 @@ public class BlackBoxAgainstJavaCodegenTestGenerated extends AbstractBlackBoxCod doTestAgainstJava(fileName); } + @TestMetadata("superconstructorWithClosure.kt") + public void testSuperconstructorWithClosure() throws Exception { + String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/sam/adapters/superconstructorWithClosure.kt"); + doTestAgainstJava(fileName); + } + @TestMetadata("typeParameterOfClass.kt") public void testTypeParameterOfClass() throws Exception { String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxAgainstJava/sam/adapters/typeParameterOfClass.kt");