From ac0aaff61184b7db1e59b3700ae41769ec33814b Mon Sep 17 00:00:00 2001 From: Dmitry Petrov Date: Mon, 10 May 2021 14:52:46 +0300 Subject: [PATCH] JVM_IR KT-46562 don't use LambdaMetafactory for Serializable SAMs TODO support serializable lambdas creation with LambdaMetafactory --- .../FirBlackBoxCodegenTestGenerated.java | 12 ++++++ .../jetbrains/kotlin/ir/util/IrTypeUtils.kt | 18 ++++++++- .../lower/indy/LambdaMetafactoryArguments.kt | 12 ++++++ .../box/invokedynamic/sam/serializableSam1.kt | 31 ++++++++++++++++ .../box/invokedynamic/sam/serializableSam2.kt | 37 +++++++++++++++++++ .../codegen/BlackBoxCodegenTestGenerated.java | 12 ++++++ .../IrBlackBoxCodegenTestGenerated.java | 12 ++++++ .../LightAnalysisModeTestGenerated.java | 10 +++++ 8 files changed, 143 insertions(+), 1 deletion(-) create mode 100644 compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt create mode 100644 compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt diff --git a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java index dc0b4741471..81bad638c4e 100644 --- a/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java +++ b/compiler/fir/fir2ir/tests-gen/org/jetbrains/kotlin/test/runners/codegen/FirBlackBoxCodegenTestGenerated.java @@ -20931,6 +20931,18 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/invokedynamic/sam/samExtFunWithCapturingLambda.kt"); } + @Test + @TestMetadata("serializableSam1.kt") + public void testSerializableSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt"); + } + + @Test + @TestMetadata("serializableSam2.kt") + public void testSerializableSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt"); + } + @Test @TestMetadata("simpleFunInterfaceConstructor.kt") public void testSimpleFunInterfaceConstructor() throws Exception { diff --git a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index d335d2be042..0665f5dd14a 100644 --- a/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/backend.common/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -143,8 +143,24 @@ private fun collectAllSupertypes(irType: IrSimpleType, result: MutableSet>, A>, Any } // where Z is a type parameter of class C. -fun getAllSubstitutedSupertypes(irClass: IrClass): MutableSet { +fun getAllSubstitutedSupertypes(irClass: IrClass): Set { val result = HashSet() collectAllSupertypes(irClass.defaultType, result) return result } + +private fun collectAllSuperclasses(irClass: IrClass, set: MutableSet) { + for (superType in irClass.superTypes) { + val classifier = superType.classifierOrNull as? IrClassSymbol ?: continue + val superClass = classifier.owner + if (set.add(superClass)) { + collectAllSuperclasses(superClass, set) + } + } +} + +fun IrClass.getAllSuperclasses(): Set { + val result = HashSet() + collectAllSuperclasses(this, result) + return result +} diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt index ced95ba2f26..13f10745b5f 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.ir.types.* import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.load.java.JvmAnnotationNames +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.types.Variance import org.jetbrains.kotlin.utils.addIfNotNull @@ -59,6 +60,11 @@ internal class LambdaMetafactoryArgumentsBuilder( if (!reference.origin.isLambda && !samClass.isFromJava()) return null + // Don't use JDK LambdaMetafactory for serializable lambdas + // TODO implement support for serializable lambdas with LambdaMetafactory (requires additional code for deserialization) + if (samClass.isInheritedFromSerializable()) + return null + val samMethod = samClass.getSingleAbstractMethod() ?: throw AssertionError("SAM class has no single abstract method: ${samClass.render()}") @@ -122,6 +128,12 @@ internal class LambdaMetafactoryArgumentsBuilder( return getLambdaMetafactoryArgsOrNullInner(reference, samMethod, samType, implFun) } + private val javaIoSerializableFqn = + FqName("java.io").child(Name.identifier("Serializable")) + + private fun IrClass.isInheritedFromSerializable(): Boolean = + getAllSuperclasses().any { it.fqNameWhenAvailable == javaIoSerializableFqn } + private fun IrClass.requiresDelegationToDefaultImpls(): Boolean { for (irMemberFun in functions) { if (irMemberFun.modality == Modality.ABSTRACT) diff --git a/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt new file mode 100644 index 00000000000..ff710d2b9db --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 0 java/lang/invoke/LambdaMetafactory + +// FILE: serializableSam1.kt +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream + +fun roundtrip(r: Ok): String { + val output = ByteArrayOutputStream() + ObjectOutputStream(output).writeObject(r) + val input = ByteArrayInputStream(output.toByteArray()) + val rr = ObjectInputStream(input).readObject() as Ok + return rr.ok() +} + +fun box() = roundtrip { "OK" } + +// FILE: Ok.java +import java.io.Serializable; + +public interface Ok extends Serializable { + String ok(); +} diff --git a/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt new file mode 100644 index 00000000000..2b0fbf675bb --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 0 java/lang/invoke/LambdaMetafactory + +// FILE: serializableSam2.kt +import java.io.ByteArrayInputStream +import java.io.ByteArrayOutputStream +import java.io.ObjectInputStream +import java.io.ObjectOutputStream + +fun roundtrip(r: Ok): String { + val output = ByteArrayOutputStream() + ObjectOutputStream(output).writeObject(r) + val input = ByteArrayInputStream(output.toByteArray()) + val rr = ObjectInputStream(input).readObject() as Ok + return rr.ok() +} + +fun box() = roundtrip { "OK" } + +// FILE: Ok.java +public interface Ok extends Base, Marker {} + +// FILE: Base.java +public interface Base { + String ok(); +} + +// FILE: Marker.java +import java.io.Serializable; + +public interface Marker extends Serializable {} diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java index 66db617bbbf..32a1e0b8ae9 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/BlackBoxCodegenTestGenerated.java @@ -20907,6 +20907,18 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/invokedynamic/sam/samExtFunWithCapturingLambda.kt"); } + @Test + @TestMetadata("serializableSam1.kt") + public void testSerializableSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt"); + } + + @Test + @TestMetadata("serializableSam2.kt") + public void testSerializableSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt"); + } + @Test @TestMetadata("simpleFunInterfaceConstructor.kt") public void testSimpleFunInterfaceConstructor() throws Exception { diff --git a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java index 282c51e0ee9..682875ae2f5 100644 --- a/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests-common-new/tests-gen/org/jetbrains/kotlin/test/runners/codegen/IrBlackBoxCodegenTestGenerated.java @@ -20931,6 +20931,18 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/invokedynamic/sam/samExtFunWithCapturingLambda.kt"); } + @Test + @TestMetadata("serializableSam1.kt") + public void testSerializableSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt"); + } + + @Test + @TestMetadata("serializableSam2.kt") + public void testSerializableSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt"); + } + @Test @TestMetadata("simpleFunInterfaceConstructor.kt") public void testSimpleFunInterfaceConstructor() throws Exception { diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 42f4775d6ed..3ee9e94af0d 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -17478,6 +17478,16 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/invokedynamic/sam/samExtFunWithCapturingLambda.kt"); } + @TestMetadata("serializableSam1.kt") + public void testSerializableSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt"); + } + + @TestMetadata("serializableSam2.kt") + public void testSerializableSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt"); + } + @TestMetadata("simpleFunInterfaceConstructor.kt") public void testSimpleFunInterfaceConstructor() throws Exception { runTest("compiler/testData/codegen/box/invokedynamic/sam/simpleFunInterfaceConstructor.kt");