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 c93b9a86f3c..8d7008fe5b5 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 @@ -23001,6 +23001,154 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + public class Serializable { + @Test + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("multipleTopLevelFunRefs.kt") + public void testMultipleTopLevelFunRefs() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt"); + } + + @Test + @TestMetadata("sameImplMethodDifferentInterfaces.kt") + public void testSameImplMethodDifferentInterfaces() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt"); + } + + @Test + @TestMetadata("serializableBoundClassMemberFunRef.kt") + public void testSerializableBoundClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundInnerConstructorRef.kt") + public void testSerializableBoundInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableBoundInterfaceMemberFunRef.kt") + public void testSerializableBoundInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundTopLevelExtensionFunRef.kt") + public void testSerializableBoundTopLevelExtensionFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt") + public void testSerializableBoundTopLevelExtensionFunRefPrimitiveReceiver() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt"); + } + + @Test + @TestMetadata("serializableClassMemberFunRef.kt") + public void testSerializableClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableConstructorRef.kt") + public void testSerializableConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableFakeOverrideFunRef.kt") + public void testSerializableFakeOverrideFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt"); + } + + @Test + @TestMetadata("serializableInnerConstructorRef.kt") + public void testSerializableInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableInterfaceMemberFunRef.kt") + public void testSerializableInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableJavaStaticMethodRef.kt") + public void testSerializableJavaStaticMethodRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt"); + } + + @Test + @TestMetadata("serializableLambda.kt") + public void testSerializableLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingBoxedInlineClassAny.kt") + public void testSerializableLambdaCapturingBoxedInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingInlineClassAny.kt") + public void testSerializableLambdaCapturingInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingInlineClassInt.kt") + public void testSerializableLambdaCapturingInlineClassInt() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingNullableInlineClassAny.kt") + public void testSerializableLambdaCapturingNullableInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaToKotlinInterface.kt") + public void testSerializableLambdaToKotlinInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt"); + } + + @Test + @TestMetadata("serializableLambdaWithCapture.kt") + public void testSerializableLambdaWithCapture() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt"); + } + + @Test + @TestMetadata("serializableTopLevelFunRef.kt") + public void testSerializableTopLevelFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt"); + } + + @Test + @TestMetadata("serializableTopLevelFunRefAsGenericInterface.kt") + public void testSerializableTopLevelFunRefAsGenericInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt"); + } + + @Test + @TestMetadata("serializableWithBridge.kt") + public void testSerializableWithBridge() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt"); + } + } } @Nested diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 38d66717c51..d438a1e7a9b 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -504,6 +504,7 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) putValueArgument(1, lambdaMetafactoryArguments.implMethodReference) putValueArgument(2, irRawFunctionRef(lambdaMetafactoryArguments.fakeInstanceMethod)) putValueArgument(3, irVarargOfRawFunctionRefs(lambdaMetafactoryArguments.extraOverriddenMethods)) + putValueArgument(4, irBoolean(lambdaMetafactoryArguments.shouldBeSerializable)) } } } diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt index c62de351e97..a37155c49db 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/TypeOperatorLowering.kt @@ -10,12 +10,20 @@ import org.jetbrains.kotlin.backend.common.FileLoweringPass import org.jetbrains.kotlin.backend.common.lower.IrBuildingTransformer import org.jetbrains.kotlin.backend.common.lower.at import org.jetbrains.kotlin.backend.common.lower.irNot +import org.jetbrains.kotlin.backend.common.lower.irThrow import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase +import org.jetbrains.kotlin.backend.common.pop +import org.jetbrains.kotlin.backend.common.push import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.codegen.fileParent +import org.jetbrains.kotlin.backend.jvm.intrinsics.generateMethodHandle +import org.jetbrains.kotlin.backend.jvm.intrinsics.getBooleanConstArgument import org.jetbrains.kotlin.backend.jvm.ir.* +import org.jetbrains.kotlin.backend.jvm.unboxInlineClass +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities import org.jetbrains.kotlin.ir.IrElement +import org.jetbrains.kotlin.ir.IrStatement import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET import org.jetbrains.kotlin.ir.builders.* import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter @@ -26,13 +34,11 @@ import org.jetbrains.kotlin.ir.expressions.impl.IrCompositeImpl import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.util.defaultType -import org.jetbrains.kotlin.ir.util.dump -import org.jetbrains.kotlin.ir.util.parentAsClass -import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.ir.util.* import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.Handle import org.jetbrains.org.objectweb.asm.Opcodes @@ -101,35 +107,29 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil private val jvmIndyLambdaMetafactoryIntrinsic = context.ir.symbols.indyLambdaMetafactoryIntrinsic - private val indyIntrinsic = context.ir.symbols.jvmIndyIntrinsic + private fun JvmIrBuilder.jvmMethodHandle(handle: Handle) = + irCall(backendContext.ir.symbols.jvmMethodHandle).apply { + putValueArgument(0, irInt(handle.tag)) + putValueArgument(1, irString(handle.owner)) + putValueArgument(2, irString(handle.name)) + putValueArgument(3, irString(handle.desc)) + putValueArgument(4, irBoolean(handle.isInterface)) + } - private fun IrBuilderWithScope.jvmInvokeDynamic( + private fun JvmIrBuilder.jvmInvokeDynamic( dynamicCall: IrCall, - bootstrapMethod: Handle, + bootstrapMethodHandle: Handle, bootstrapMethodArguments: List ) = - irCall(indyIntrinsic, dynamicCall.type).apply { + irCall(backendContext.ir.symbols.jvmIndyIntrinsic, dynamicCall.type).apply { putTypeArgument(0, dynamicCall.type) putValueArgument(0, dynamicCall) - putValueArgument(1, irInt(bootstrapMethod.tag)) - putValueArgument(2, irString(bootstrapMethod.owner)) - putValueArgument(3, irString(bootstrapMethod.name)) - putValueArgument(4, irString(bootstrapMethod.desc)) - putValueArgument(5, irVararg(context.irBuiltIns.anyType, bootstrapMethodArguments)) + putValueArgument(1, jvmMethodHandle(bootstrapMethodHandle)) + putValueArgument(2, irVararg(context.irBuiltIns.anyType, bootstrapMethodArguments)) } - private val originalMethodTypeIntrinsic = context.ir.symbols.jvmOriginalMethodTypeIntrinsic - private val substitutedMethodTypeIntrinsic = context.ir.symbols.jvmSubstitutedMethodTypeIntrinsic - - private fun IrBuilderWithScope.jvmOriginalMethodType(methodSymbol: IrFunctionSymbol) = - irCall(originalMethodTypeIntrinsic, context.irBuiltIns.anyType).apply { - putValueArgument(0, irRawFunctionReferefence(context.irBuiltIns.anyType, methodSymbol)) - } - - @Suppress("unused") - private fun IrBuilderWithScope.jvmSubstitutedMethodType(ownerType: IrType, methodSymbol: IrFunctionSymbol) = - irCall(substitutedMethodTypeIntrinsic, context.irBuiltIns.anyType).apply { - putTypeArgument(0, ownerType) + private fun JvmIrBuilder.jvmOriginalMethodType(methodSymbol: IrFunctionSymbol) = + irCall(backendContext.ir.symbols.jvmOriginalMethodTypeIntrinsic, context.irBuiltIns.anyType).apply { putValueArgument(0, irRawFunctionReferefence(context.irBuiltIns.anyType, methodSymbol)) } @@ -179,6 +179,236 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil } } + private class SerializableMethodRefInfo( + val samType: IrType, + val samMethodSymbol: IrSimpleFunctionSymbol, + val implFunSymbol: IrFunctionSymbol, + val instanceFunSymbol: IrFunctionSymbol, + val requiredBridges: Collection, + val dynamicCallSymbol: IrSimpleFunctionSymbol + ) + + private class ClassContext { + val serializableMethodRefInfos = ArrayList() + } + + private val classContextStack = ArrayDeque() + + private fun enterClass(): ClassContext { + return ClassContext().also { + classContextStack.push(it) + } + } + + private fun leaveClass() { + classContextStack.pop() + } + + private fun getClassContext(): ClassContext { + if (classContextStack.isEmpty()) { + throw AssertionError("No class context") + } + return classContextStack.last() + } + + override fun visitClass(declaration: IrClass): IrStatement { + val context = enterClass() + val result = super.visitClass(declaration) + if (context.serializableMethodRefInfos.isNotEmpty()) { + generateDeserializeLambdaMethod(declaration, context.serializableMethodRefInfos) + } + leaveClass() + return result + } + + private data class DeserializedLambdaInfo( + val functionalInterfaceClass: String, + val implMethodHandle: Handle, + val functionalInterfaceMethod: Method + ) + + private fun generateDeserializeLambdaMethod( + irClass: IrClass, + serializableMethodRefInfos: List + ) { + // fun `$deserializeLambda$`(lambda: java.lang.invoke.SerializedLambda): Object { + // val tmp = lambda.getImplMethodName() + // when { + // ... + // tmp == NAME_i -> { + // when { + // ... + // lambda.getImplMethodKind() == [LAMBDA_i_k].implMethodKind && + // lambda.getFunctionalInterfaceClass() == [LAMBDA_i_k].functionalInterfaceClass && + // lambda.getFunctionalInterfaceMethodName() == [LAMBDA_i_k].functionalInterfaceMethodName && + // lambda.getFunctionalInterfaceMethodSignature() == [LAMBDA_i_k].functionalInterfaceMethodSignature && + // lambda.getImplClass() == [LAMBDA_i_k].implClass && + // lambda.getImplMethodSignature() = [LAMBDA_i_k].implMethodSignature -> + // ``([LAMBDA_i_k]) + // ... + // } + // } + // ... + // } + // throw IllegalArgumentException("Invalid lambda deserialization") + // } + + val groupedByImplMethodName = HashMap>() + for (serializableMethodRefInfo in serializableMethodRefInfos) { + val deserializedLambdaInfo = mapDeserializedLambda(serializableMethodRefInfo) + val implMethodName = deserializedLambdaInfo.implMethodHandle.name + val byDeserializedLambdaInfo = groupedByImplMethodName.getOrPut(implMethodName) { HashMap() } + byDeserializedLambdaInfo[deserializedLambdaInfo] = serializableMethodRefInfo + } + + val deserializeLambdaFun = context.irFactory.buildFun { + name = Name.identifier("\$deserializeLambda\$") + visibility = DescriptorVisibilities.PRIVATE + } + deserializeLambdaFun.parent = irClass + val lambdaParameter = deserializeLambdaFun.addValueParameter("lambda", context.ir.symbols.serializedLambda.irType) + deserializeLambdaFun.returnType = context.irBuiltIns.anyType + deserializeLambdaFun.body = context.createJvmIrBuilder(deserializeLambdaFun.symbol, UNDEFINED_OFFSET, UNDEFINED_OFFSET).run { + irBlockBody { + val tmp = irTemporary( + irCall(backendContext.ir.symbols.serializedLambda.getImplMethodName).apply { + dispatchReceiver = irGet(lambdaParameter) + } + ) + +irWhen( + backendContext.irBuiltIns.unitType, + groupedByImplMethodName.entries.map { (implMethodName, infos) -> + irBranch( + irEquals(irGet(tmp), irString(implMethodName)), + irWhen( + backendContext.irBuiltIns.unitType, + infos.entries.map { (deserializedLambdaInfo, serializedMethodRefInfo) -> + irBranch( + generateSerializedLambdaEquals(lambdaParameter, deserializedLambdaInfo), + irReturn(generateCreateDeserializedMethodRef(lambdaParameter, serializedMethodRefInfo)) + ) + } + ) + ) + } + ) + + +irThrow( + irCall(backendContext.ir.symbols.illegalArgumentExceptionCtorString).also { ctorCall -> + ctorCall.putValueArgument( + 0, + // Replace argument with: + // irCall(backendContext.irBuiltIns.anyClass.getSimpleFunction("toString")!!).apply { + // dispatchReceiver = irGet(lambdaParameter) + // } + // for debugging "Invalid lambda deserialization" exceptions. + irString("Invalid lambda deserialization") + ) + } + ) + } + } + + irClass.declarations.add(deserializeLambdaFun) + } + + private fun mapDeserializedLambda(info: SerializableMethodRefInfo) = + DeserializedLambdaInfo( + functionalInterfaceClass = context.typeMapper.mapType(info.samType).internalName, + implMethodHandle = generateMethodHandle(context, info.implFunSymbol.owner), + functionalInterfaceMethod = context.methodSignatureMapper.mapAsmMethod(info.samMethodSymbol.owner) + ) + + private fun JvmIrBuilder.generateSerializedLambdaEquals( + lambdaParameter: IrValueParameter, + deserializedLambdaInfo: DeserializedLambdaInfo + ): IrExpression { + val functionalInterfaceClass = deserializedLambdaInfo.functionalInterfaceClass + val implMethodHandle = deserializedLambdaInfo.implMethodHandle + val samMethod = deserializedLambdaInfo.functionalInterfaceMethod + + fun irGetLambdaProperty(getter: IrSimpleFunction) = + irCall(getter).apply { dispatchReceiver = irGet(lambdaParameter) } + + return irAndAnd( + irEquals( + irGetLambdaProperty(backendContext.ir.symbols.serializedLambda.getImplMethodKind), + irInt(implMethodHandle.tag) + ), + irObjectEquals( + irGetLambdaProperty(backendContext.ir.symbols.serializedLambda.getFunctionalInterfaceClass), + irString(functionalInterfaceClass) + ), + irObjectEquals( + irGetLambdaProperty(backendContext.ir.symbols.serializedLambda.getFunctionalInterfaceMethodName), + irString(samMethod.name) + ), + irObjectEquals( + irGetLambdaProperty(backendContext.ir.symbols.serializedLambda.getFunctionalInterfaceMethodSignature), + irString(samMethod.descriptor) + ), + irObjectEquals( + irGetLambdaProperty(backendContext.ir.symbols.serializedLambda.getImplClass), + irString(implMethodHandle.owner) + ), + irObjectEquals( + irGetLambdaProperty(backendContext.ir.symbols.serializedLambda.getImplMethodSignature), + irString(implMethodHandle.desc) + ) + ) + } + + private val equalsAny = context.irBuiltIns.anyClass.getSimpleFunction("equals")!! + + private fun JvmIrBuilder.irObjectEquals(receiver: IrExpression, arg: IrExpression) = + irCall(equalsAny).apply { + dispatchReceiver = receiver + putValueArgument(0, arg) + } + + private fun JvmIrBuilder.irAndAnd(vararg args: IrExpression): IrExpression { + if (args.isEmpty()) throw AssertionError("At least one argument expected") + var result = args[0] + for (i in 1 until args.size) { + result = irCall(backendContext.irBuiltIns.andandSymbol).apply { + putValueArgument(0, result) + putValueArgument(1, args[i]) + } + } + return result + } + + private fun JvmIrBuilder.generateCreateDeserializedMethodRef( + lambdaParameter: IrValueParameter, + info: SerializableMethodRefInfo + ): IrExpression { + val dynamicCall = irCall(info.dynamicCallSymbol) + for ((index, dynamicValueParameter) in info.dynamicCallSymbol.owner.valueParameters.withIndex()) { + val capturedArg = irCall(backendContext.ir.symbols.serializedLambda.getCapturedArg).also { call -> + call.dispatchReceiver = irGet(lambdaParameter) + call.putValueArgument(0, irInt(index)) + } + val expectedType = dynamicValueParameter.type + val downcastArg = + if (expectedType.isInlineClassType()) { + // Inline class type arguments are stored as their underlying representation. + val unboxedType = expectedType.unboxInlineClass() + irCall(backendContext.ir.symbols.unsafeCoerceIntrinsic).also { coercion -> + coercion.putTypeArgument(0, unboxedType) + coercion.putTypeArgument(1, expectedType) + coercion.putValueArgument(0, capturedArg) + } + } else { + irAs(capturedArg, expectedType) + } + dynamicCall.putValueArgument(index, downcastArg) + } + + return createLambdaMetafactoryCall( + info.samMethodSymbol, info.implFunSymbol, info.instanceFunSymbol, true, info.requiredBridges, dynamicCall + ) + } + /** * @see FunctionReferenceLowering.wrapWithIndySamConversion */ @@ -211,6 +441,8 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil } } + val shouldBeSerializable = call.getBooleanConstArgument(4) + val samMethod = samMethodRef.symbol.owner as? IrSimpleFunction ?: fail("SAM method is expected to be 'IrSimpleFunction': ${samMethodRef.symbol.owner.render()}") val instanceMethod = instanceMethodRef.symbol.owner as? IrSimpleFunction @@ -220,30 +452,80 @@ private class TypeOperatorLowering(private val context: JvmBackendContext) : Fil val requiredBridges = getOverriddenMethodsRequiringBridges(instanceMethod, samMethod, extraOverriddenMethods) - return context.createJvmIrBuilder(implFunSymbol, startOffset, endOffset).run { - val samMethodType = jvmOriginalMethodType(samMethod.symbol) - val irRawFunRef = irRawFunctionReferefence(implFunRef.type, implFunSymbol) - val instanceMethodType = jvmOriginalMethodType(instanceMethodRef.symbol) + if (shouldBeSerializable) { + getClassContext().serializableMethodRefInfos.add( + SerializableMethodRefInfo( + samType, samMethod.symbol, implFunSymbol, instanceMethodRef.symbol, requiredBridges, dynamicCall.symbol + ) + ) + } - if (requiredBridges.isNotEmpty()) { - val bridgeMethodTypes = requiredBridges.map { jvmOriginalMethodType(it.symbol) } - jvmInvokeDynamic( - dynamicCall, - jdkAltMetafactoryHandle, - listOf( - samMethodType, irRawFunRef, instanceMethodType, - irInt(LambdaMetafactory.FLAG_BRIDGES), - irInt(requiredBridges.size) - ) + bridgeMethodTypes - ) - } else { - jvmInvokeDynamic( - dynamicCall, - jdkMetafactoryHandle, - listOf(samMethodType, irRawFunRef, instanceMethodType) - ) + return context.createJvmIrBuilder(implFunSymbol, startOffset, endOffset) + .createLambdaMetafactoryCall( + samMethod.symbol, implFunSymbol, instanceMethodRef.symbol, shouldBeSerializable, requiredBridges, dynamicCall + ) + } + + private fun JvmIrBuilder.createLambdaMetafactoryCall( + samMethodSymbol: IrSimpleFunctionSymbol, + implFunSymbol: IrFunctionSymbol, + instanceMethodSymbol: IrFunctionSymbol, + shouldBeSerializable: Boolean, + requiredBridges: Collection, + dynamicCall: IrCall + ): IrCall { + val samMethodType = jvmOriginalMethodType(samMethodSymbol) + val implFunRawRef = irRawFunctionReferefence(context.irBuiltIns.anyType, implFunSymbol) + val instanceMethodType = jvmOriginalMethodType(instanceMethodSymbol) + + var bootstrapMethod = jdkMetafactoryHandle + val bootstrapMethodArguments = arrayListOf( + samMethodType, + implFunRawRef, + instanceMethodType + ) + var bridgeMethodTypes = emptyList() + + var flags = 0 + + if (shouldBeSerializable) { + flags += LambdaMetafactory.FLAG_SERIALIZABLE + } + + if (requiredBridges.isNotEmpty()) { + flags += LambdaMetafactory.FLAG_BRIDGES + bridgeMethodTypes = requiredBridges.map { jvmOriginalMethodType(it.symbol) } + } + + if (flags != 0) { + bootstrapMethod = jdkAltMetafactoryHandle + // Bootstrap arguments for LambdaMetafactory#altMetafactory should be: + // MethodType samMethodType, + // MethodHandle implMethod, + // MethodType instantiatedMethodType, + // // ---------------------------- same as LambdaMetafactory#metafactory until here + // int flags, // combination of LambdaMetafactory.{ FLAG_SERIALIZABLE, FLAG_MARKERS, FLAG_BRIDGES } + // int markerInterfaceCount, // IF flags has MARKERS set + // Class... markerInterfaces, // IF flags has MARKERS set + // int bridgeCount, // IF flags has BRIDGES set + // MethodType... bridges // IF flags has BRIDGES set + + // `flags` + bootstrapMethodArguments.add(irInt(flags)) + + // `markerInterfaceCount`, `markerInterfaces` + // Currently, it looks like there's no way a Kotlin function expression should be SAM-converted to a type implementing + // additional marker interfaces (such as `Runnable r = (Runnable & Marker) () -> { ... }` in Java). + // If such additional marker interfaces would appear, they should go here, between `flags` and `bridgeCount`. + + if (bridgeMethodTypes.isNotEmpty()) { + // `bridgeCount`, `bridges` + bootstrapMethodArguments.add(irInt(bridgeMethodTypes.size)) + bootstrapMethodArguments.addAll(bridgeMethodTypes) } } + + return jvmInvokeDynamic(dynamicCall, bootstrapMethod, bootstrapMethodArguments) } private fun getOverriddenMethodsRequiringBridges( diff --git a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt index 03950c94a70..46afbcee460 100644 --- a/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt +++ b/compiler/ir/backend.jvm/lower/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt @@ -52,7 +52,7 @@ internal sealed class MetafactoryArgumentsResult { // TODO might use Kotlin-specific metafactory if there would be one. object LambdaMetafactorySemanticsHazard : Failure() - // There's an ABI incompatibility between classes generated by j.l.invoke.LambdaMetafactory and classes generatede by Kotlin. + // There's an ABI incompatibility between classes generated by j.l.invoke.LambdaMetafactory and classes generated by Kotlin. // TODO might use Kotlin-specific metafactory if there would be one. object LambdaMetafactoryAbiHazard : Failure() @@ -75,7 +75,8 @@ internal class LambdaMetafactoryArguments( val samMethod: IrSimpleFunction, val fakeInstanceMethod: IrSimpleFunction, val implMethodReference: IrFunctionReference, - val extraOverriddenMethods: List + val extraOverriddenMethods: List, + val shouldBeSerializable: Boolean ) : MetafactoryArgumentsResult.Success() @@ -203,14 +204,13 @@ internal class LambdaMetafactoryArgumentsBuilder( semanticsHazard -> return MetafactoryArgumentsResult.Failure.LambdaMetafactorySemanticsHazard abiHazard -> return MetafactoryArgumentsResult.Failure.LambdaMetafactoryAbiHazard inliningHazard -> return MetafactoryArgumentsResult.Failure.InliningHazard - shouldBeSerializable -> return MetafactoryArgumentsResult.Failure.SerializationHazard functionHazard -> return MetafactoryArgumentsResult.Failure.FunctionHazard } // Do the hard work of matching Kotlin functional interface hierarchy against LambdaMetafactory constraints. // Briefly: sometimes we have to force boxing on the primitive and inline class values, sometimes we have to keep them unboxed. // If this results in conflicting requirements, we can't use INVOKEDYNAMIC with LambdaMetafactory for creating a closure. - return getLambdaMetafactoryArgsOrNullInner(reference, samMethod, samType, implFun) + return getLambdaMetafactoryArgsOrNullInner(reference, samMethod, samType, implFun, shouldBeSerializable) ?: MetafactoryArgumentsResult.Failure.FunctionHazard } @@ -242,7 +242,8 @@ internal class LambdaMetafactoryArgumentsBuilder( reference: IrFunctionReference, samMethod: IrSimpleFunction, samType: IrType, - implFun: IrFunction + implFun: IrFunction, + shouldBeSerializable: Boolean ): LambdaMetafactoryArguments? { val nonFakeOverriddenFuns = samMethod.allOverridden().filterNot { it.isFakeOverride } val relevantOverriddenFuns = if (samMethod.isFakeOverride) nonFakeOverriddenFuns else nonFakeOverriddenFuns + samMethod @@ -334,9 +335,15 @@ internal class LambdaMetafactoryArgumentsBuilder( reference if (samMethod.isFakeOverride && nonFakeOverriddenFuns.size == 1) { - return LambdaMetafactoryArguments(nonFakeOverriddenFuns.single(), fakeInstanceMethod, newReference, listOf()) + return LambdaMetafactoryArguments( + nonFakeOverriddenFuns.single(), + fakeInstanceMethod, + newReference, + listOf(), + shouldBeSerializable + ) } - return LambdaMetafactoryArguments(samMethod, fakeInstanceMethod, newReference, nonFakeOverriddenFuns) + return LambdaMetafactoryArguments(samMethod, fakeInstanceMethod, newReference, nonFakeOverriddenFuns, shouldBeSerializable) } private fun checkMethodSignatureCompliance( @@ -351,14 +358,7 @@ internal class LambdaMetafactoryArgumentsBuilder( withExtensionReceiver = reference.extensionReceiver == null ) val methodParameters = collectValueParameters(fakeInstanceMethod) - if (implParameters.size != methodParameters.size) - throw AssertionError( - "Mismatching lambda and instance method parameters:\n" + - "implFun: ${implFun.render()}\n" + - " (${implParameters.size} parameters)\n" + - "instance method: ${fakeInstanceMethod.render()}\n" + - " (${methodParameters.size} parameters)" - ) + validateMethodParameters(implParameters, methodParameters, implFun, fakeInstanceMethod) for ((implParameter, methodParameter) in implParameters.zip(methodParameters)) { val constraint = constraints.valueParameters[methodParameter] if (!checkTypeCompliesWithConstraint(implParameter.type, constraint)) @@ -397,14 +397,7 @@ internal class LambdaMetafactoryArgumentsBuilder( val implParameters = collectValueParameters(implFun) val methodParameters = collectValueParameters(fakeInstanceMethod) - if (implParameters.size != methodParameters.size) - throw AssertionError( - "Mismatching lambda and instance method parameters:\n" + - "implFun: ${implFun.render()}\n" + - " (${implParameters.size} parameters)\n" + - "instance method: ${fakeInstanceMethod.render()}\n" + - " (${methodParameters.size} parameters)" - ) + validateMethodParameters(implParameters, methodParameters, implFun, fakeInstanceMethod) for ((implParameter, methodParameter) in implParameters.zip(methodParameters)) { val parameterConstraint = constraints.valueParameters[methodParameter] if (parameterConstraint.requiresImplLambdaBoxing()) { @@ -418,6 +411,22 @@ internal class LambdaMetafactoryArgumentsBuilder( } } + private fun validateMethodParameters( + implParameters: List, + methodParameters: List, + implFun: IrFunction, + fakeInstanceMethod: IrSimpleFunction + ) { + if (implParameters.size != methodParameters.size) + throw AssertionError( + "Mismatching lambda and instance method parameters:\n" + + "implFun: ${implFun.render()}\n" + + " (${implParameters.size} parameters)\n" + + "instance method: ${fakeInstanceMethod.render()}\n" + + " (${methodParameters.size} parameters)" + ) + } + private fun remapExtensionLambda(lambda: IrSimpleFunction, reference: IrFunctionReference): IrFunctionReference { val oldExtensionReceiver = lambda.extensionReceiverParameter ?: return reference diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt index b3f8c2a58d2..6865ccfc901 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/JvmSymbols.kt @@ -59,8 +59,10 @@ class JvmSymbols( private val kotlinJvmFunctionsPackage: IrPackageFragment = createPackage(FqName("kotlin.jvm.functions")) private val kotlinReflectPackage: IrPackageFragment = createPackage(FqName("kotlin.reflect")) private val javaLangPackage: IrPackageFragment = createPackage(FqName("java.lang")) + private val javaLangInvokePackage: IrPackageFragment = createPackage(FqName("java.lang.invoke")) private val javaUtilPackage: IrPackageFragment = createPackage(FqName("java.util")) + private val kotlinInternalPackage: IrPackageFragment = createPackage(FqName("kotlin.internal")) // Special package for functions representing dynamic symbols referenced by 'INVOKEDYNAMIC' instruction - e.g., @@ -101,6 +103,7 @@ class JvmSymbols( "kotlin.jvm" -> kotlinJvmPackage "kotlin.reflect" -> kotlinReflectPackage "java.lang" -> javaLangPackage + "java.lang.invoke" -> javaLangInvokePackage "java.util" -> javaUtilPackage "kotlin.internal" -> kotlinInternalPackage else -> error("Other packages are not supported yet: $fqName") @@ -232,7 +235,7 @@ class JvmSymbols( klass.addConstructor { isPrimary = true } } - // This annotations also implies ACC_DEPRECATED flag in generated bytecode + // This annotation also implies ACC_DEPRECATED flag in generated bytecode val javaLangDeprecatedConstructorWithDeprecatedFlag = javaLangDeprecatedWithDeprecatedFlag.constructors.single() private val javaLangAssertionError: IrClassSymbol = @@ -596,14 +599,16 @@ class JvmSymbols( // samMethodType, // implMethodReference, // instantiatedMethodType, - // vararg extraOverriddenMethodTypes + // vararg extraOverriddenMethodTypes, + // shouldBeSerializable // ): SAM_TYPE // where: // `SAM_TYPE` is a single abstract method interface, which is implemented by a resulting closure; // `samMethodType` is a method type (signature and return type) of a method to be implemented by a closure; // `implMethodReference` is an actual implementation method (e.g., method for a lambda function); // `instantiatedMethodType` is a specialized implementation method type; - // `extraOverriddenMethodTypes` is a possibly empty vararg of additional methods to be implemented by a closure. + // `extraOverriddenMethodTypes` is a possibly empty vararg of additional methods to be implemented by a closure; + // `shouldBeSerializable` is true if the class of the resulting object should implement `java.io.Serializable`. // // At this stage, "method types" are represented as IrRawFunctionReference nodes for the functions with corresponding signature. // `` call rewriting selects a particular bootstrap method (`metafactory` or `altMetafactory`) @@ -632,34 +637,74 @@ class JvmSymbols( type = arrayOfAnyType varargElementType = irBuiltIns.anyType } + addValueParameter("shouldBeSerializable", irBuiltIns.booleanType) returnType = samType.defaultType }.symbol + + inner class SerializedLambdaClass + @Deprecated("Should not be used outside of JvmSymbols") internal constructor() { + val symbol = createClass(FqName("java.lang.invoke.SerializedLambda")) + val irClass = symbol.owner + val irType = irClass.defaultType + + val getImplMethodName = irClass.addFunction("getImplMethodName", irBuiltIns.stringType) + val getImplMethodKind = irClass.addFunction("getImplMethodKind", irBuiltIns.intType) + val getImplClass = irClass.addFunction("getImplClass", irBuiltIns.stringType) + val getImplMethodSignature = irClass.addFunction("getImplMethodSignature", irBuiltIns.stringType) + + val getFunctionalInterfaceClass = irClass.addFunction("getFunctionalInterfaceClass", irBuiltIns.stringType) + val getFunctionalInterfaceMethodName = irClass.addFunction("getFunctionalInterfaceMethodName", irBuiltIns.stringType) + val getFunctionalInterfaceMethodSignature = irClass.addFunction("getFunctionalInterfaceMethodSignature", irBuiltIns.stringType) + + val getCapturedArg = irClass.addFunction("getCapturedArg", irBuiltIns.anyNType).apply { + addValueParameter(Name.identifier("i"), irBuiltIns.intType) + } + } + + @Suppress("DEPRECATION") + val serializedLambda = SerializedLambdaClass() + + val illegalArgumentException = createClass(FqName("java.lang.IllegalArgumentException")) { irClass -> + irClass.addConstructor { + name = Name.special("") + }.apply { + addValueParameter("message", irBuiltIns.stringType) + } + } + val illegalArgumentExceptionCtorString = illegalArgumentException.constructors.single() + + val jvmMethodType: IrSimpleFunctionSymbol = + irFactory.buildFun { + name = Name.special("") + origin = IrDeclarationOrigin.IR_BUILTINS_STUB + }.apply { + returnType = irBuiltIns.anyType + parent = kotlinJvmInternalPackage + addValueParameter("descriptor", irBuiltIns.stringType) + }.symbol + + val jvmMethodHandle: IrSimpleFunctionSymbol = + irFactory.buildFun { + name = Name.special("") + origin = IrDeclarationOrigin.IR_BUILTINS_STUB + }.apply { + returnType = irBuiltIns.anyType + parent = kotlinJvmInternalPackage + addValueParameter("tag", irBuiltIns.intType) + addValueParameter("owner", irBuiltIns.stringType) + addValueParameter("name", irBuiltIns.stringType) + addValueParameter("descriptor", irBuiltIns.stringType) + addValueParameter("isInterface", irBuiltIns.booleanType) + }.symbol + // Intrinsic to represent INVOKEDYNAMIC calls in IR. // fun ``( // dynamicCall: T, - // bootstrapMethodTag: Int, - // bootstrapMethodOwner: String, - // bootstrapMethodName: String, - // bootstrapMethodDesc: String, + // bootstrapMethodHandle: Any, // vararg bootstrapMethodArgs: Any // ): T - // Bootstrap method handle is encoded as a bunch of constants. - // For example, - // REF_invokeStatic java/lang/invoke/LambdaMetafactory.metafactory:( - // Ljava/lang/invoke/MethodHandles$Lookup; - // Ljava/lang/String; - // Ljava/lang/invoke/MethodType; - // Ljava/lang/invoke/MethodType; - // Ljava/lang/invoke/MethodHandle; - // Ljava/lang/invoke/MethodType; - // )Ljava/lang/invoke/CallSite; - // is represented as - // bootstrapMethodTag: IrConst(value = H_INVOKESTATIC) - // bootstrapMethodOwner: IrConst(value = "java/lang/invoke/LambdaMetafactory") - // bootstrapMethodName: IrConst(value = "metafactory") - // bootstrapMethodDesc: IrConst(value = "(...)Ljava/lang/invoke/CallSite;") - // Bootstrap method owner is assumed to be a class (which is true for both LambdaMetafactory and StringConcatFactory). + // Bootstrap method handle is represented as a `` call. val jvmIndyIntrinsic: IrSimpleFunctionSymbol = irFactory.buildFun { name = Name.special("") @@ -668,10 +713,7 @@ class JvmSymbols( parent = kotlinJvmInternalPackage val t = addTypeParameter("T", irBuiltIns.anyNType) addValueParameter("dynamicCall", t.defaultType) - addValueParameter("bootstrapMethodTag", irBuiltIns.intType) - addValueParameter("bootstrapMethodOwner", irBuiltIns.stringType) - addValueParameter("bootstrapMethodName", irBuiltIns.stringType) - addValueParameter("bootstrapMethodDesc", irBuiltIns.stringType) + addValueParameter("bootstrapMethodHandle", irBuiltIns.anyType) addValueParameter { name = Name.identifier("bootstrapMethodArguments") type = arrayOfAnyType @@ -693,21 +735,6 @@ class JvmSymbols( returnType = irBuiltIns.anyType }.symbol - // Intrinsic used to represent MethodType objects in bootstrap method arguments (see jvmInvokeDynamicIntrinsic above). - // Type argument is a substituted method owner type (e.g., 'java.lang.function.Supplier'). - // Value argument is a raw function reference to a corresponding method (e.g., 'java.lang.function.Supplier#get'). - // Resulting method type is substituted. - val jvmSubstitutedMethodTypeIntrinsic: IrSimpleFunctionSymbol = - irFactory.buildFun { - name = Name.special("") - origin = IrDeclarationOrigin.IR_BUILTINS_STUB - }.apply { - parent = kotlinJvmInternalPackage - addTypeParameter("OwnerT", irBuiltIns.anyType) - addValueParameter("method", irBuiltIns.anyType) - returnType = irBuiltIns.anyType - }.symbol - private val collectionToArrayClass: IrClassSymbol = createClass(FqName("kotlin.jvm.internal.CollectionToArray")) { klass -> klass.origin = JvmLoweredDeclarationOrigin.TO_ARRAY diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmInvokeDynamic.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmInvokeDynamic.kt index d8f26a9d3e9..8b941bdb13b 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmInvokeDynamic.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/intrinsics/JvmInvokeDynamic.kt @@ -5,19 +5,18 @@ package org.jetbrains.kotlin.backend.jvm.intrinsics +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.codegen.* import org.jetbrains.kotlin.backend.jvm.ir.findSuperDeclaration -import org.jetbrains.kotlin.ir.builders.declarations.buildClass import org.jetbrains.kotlin.ir.declarations.IrClass import org.jetbrains.kotlin.ir.declarations.IrConstructor +import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction import org.jetbrains.kotlin.ir.expressions.* -import org.jetbrains.kotlin.ir.overrides.buildFakeOverrideMember -import org.jetbrains.kotlin.ir.types.* -import org.jetbrains.kotlin.ir.types.impl.makeTypeProjection -import org.jetbrains.kotlin.ir.util.* -import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.ir.util.dump +import org.jetbrains.kotlin.ir.util.parentAsClass +import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.utils.addToStdlib.safeAs import org.jetbrains.org.objectweb.asm.Handle import org.jetbrains.org.objectweb.asm.Opcodes @@ -36,24 +35,17 @@ object JvmInvokeDynamic : IntrinsicMethod() { ) fail("Unexpected dynamicCallee: '${dynamicCallee.render()}'") - val bootstrapMethodTag = expression.getValueArgument(1)?.getIntConst() - ?: fail("'bootstrapMethodTag' is expected to be an int const") - val bootstrapMethodOwner = expression.getValueArgument(2)?.getStringConst() - ?: fail("'bootstrapMethodOwner' is expected to be a string const") - val bootstrapMethodName = expression.getValueArgument(3)?.getStringConst() - ?: fail("'bootstrapMethodName' is expected to be a string const") - val bootstrapMethodDesc = expression.getValueArgument(4)?.getStringConst() - ?: fail("'bootstrapMethodDesc' is expected to be a string const") - val bootstrapMethodArgs = (expression.getValueArgument(5)?.safeAs() - ?: fail("'bootstrapMethodArgs' is expected to be a vararg")) + val bootstrapMethodHandleArg = expression.getValueArgument(1) as? IrCall + ?: fail("'bootstrapMethodHandle' should be a call") + val bootstrapMethodHandle = evalMethodHandle(bootstrapMethodHandleArg) + val bootstrapMethodArgs = (expression.getValueArgument(2)?.safeAs() + ?: fail("'bootstrapMethodArgs' is expected to be a vararg")) val asmBootstrapMethodArgs = bootstrapMethodArgs.elements .map { generateBootstrapMethodArg(it, codegen) } .toTypedArray() val dynamicCalleeMethod = codegen.methodSignatureMapper.mapAsmMethod(dynamicCallee) - val bootstrapMethodHandle = Handle(bootstrapMethodTag, bootstrapMethodOwner, bootstrapMethodName, bootstrapMethodDesc, false) - val dynamicCallGenerator = IrCallGenerator.DefaultCallGenerator val dynamicCalleeArgumentTypes = dynamicCalleeMethod.argumentTypes for (i in dynamicCallee.valueParameters.indices) { @@ -76,14 +68,8 @@ object JvmInvokeDynamic : IntrinsicMethod() { is IrRawFunctionReference -> generateMethodHandle(element, codegen) is IrCall -> - when (element.symbol) { - codegen.context.ir.symbols.jvmOriginalMethodTypeIntrinsic -> - generateOriginalMethodType(element, codegen) - codegen.context.ir.symbols.jvmSubstitutedMethodTypeIntrinsic -> - generateSubstitutedMethodType(element, codegen) - else -> - throw AssertionError("Unexpected callee in bootstrap method argument:\n${element.dump()}") - } + evalBootstrapArgumentIntrinsicCall(element, codegen) + ?: throw AssertionError("Unexpected callee in bootstrap method argument:\n${element.dump()}") is IrConst<*> -> when (element.kind) { IrConstKind.Byte -> (element.value as Byte).toInt() @@ -100,40 +86,38 @@ object JvmInvokeDynamic : IntrinsicMethod() { throw AssertionError("Unexpected bootstrap method argument:\n${element.dump()}") } - private fun generateMethodHandle(irRawFunctionReference: IrRawFunctionReference, codegen: ExpressionCodegen): Handle { - val irFun = when (val irFun0 = irRawFunctionReference.symbol.owner) { - is IrConstructor -> - irFun0 - is IrSimpleFunction -> { - // Note that if the given function is a fake override, we emit a method handle with explicit super class. - // This has the same binary compatibility guarantees as in Java. - findSuperDeclaration(irFun0, false, codegen.state.jvmDefaultMode) - } + private fun evalBootstrapArgumentIntrinsicCall(irCall: IrCall, codegen: ExpressionCodegen): Any? { + return when (irCall.symbol) { + codegen.context.ir.symbols.jvmOriginalMethodTypeIntrinsic -> + evalOriginalMethodType(irCall, codegen) + codegen.context.ir.symbols.jvmMethodType -> + evalMethodType(irCall) + codegen.context.ir.symbols.jvmMethodHandle -> + evalMethodHandle(irCall) else -> - throw java.lang.AssertionError("Simple function or constructor expected: ${irFun0.render()}") + null } - - val irParentClass = irFun.parent as? IrClass - ?: throw AssertionError("Unexpected parent: ${irFun.parent.render()}") - val owner = codegen.typeMapper.mapOwner(irParentClass) - - val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFun) - - val handleTag = when { - irFun is IrConstructor -> - Opcodes.H_NEWINVOKESPECIAL - irFun.dispatchReceiverParameter == null -> - Opcodes.H_INVOKESTATIC - irParentClass.isJvmInterface -> - Opcodes.H_INVOKEINTERFACE - else -> - Opcodes.H_INVOKEVIRTUAL - } - - return Handle(handleTag, owner.internalName, asmMethod.name, asmMethod.descriptor, irParentClass.isJvmInterface) } - private fun generateOriginalMethodType(irCall: IrCall, codegen: ExpressionCodegen): Type { + private fun evalMethodType(irCall: IrCall): Type { + val descriptor = irCall.getStringConstArgument(0) + return Type.getMethodType(descriptor) + } + + private fun evalMethodHandle(irCall: IrCall): Handle { + val tag = irCall.getIntConstArgument(0) + val owner = irCall.getStringConstArgument(1) + val name = irCall.getStringConstArgument(2) + val descriptor = irCall.getStringConstArgument(3) + val isInterface = irCall.getBooleanConstArgument(4) + return Handle(tag, owner, name, descriptor, isInterface) + } + + fun generateMethodHandle(irRawFunctionReference: IrRawFunctionReference, codegen: ExpressionCodegen): Handle { + return generateMethodHandle(codegen.context, irRawFunctionReference) + } + + private fun evalOriginalMethodType(irCall: IrCall, codegen: ExpressionCodegen): Type { val irRawFunRef = irCall.getValueArgument(0) as? IrRawFunctionReference ?: throw AssertionError( "Argument in ${irCall.symbol.owner.name} call is expected to be a raw function reference:\n" + @@ -144,64 +128,75 @@ object JvmInvokeDynamic : IntrinsicMethod() { return Type.getMethodType(asmMethod.descriptor) } - private fun generateSubstitutedMethodType(irCall: IrCall, codegen: ExpressionCodegen): Type { - fun fail(message: String): Nothing = - throw AssertionError("$message; irCall:\n${irCall.dump()}") +} - val irRawFunRef = irCall.getValueArgument(0) as? IrRawFunctionReference - ?: fail("Argument in ${irCall.symbol.owner.name} call is expected to be a raw function reference") - val irOriginalFun = irRawFunRef.symbol.owner as? IrSimpleFunction - ?: fail("IrSimpleFunction expected: ${irRawFunRef.symbol.owner.render()}") +fun IrCall.getIntConstArgument(i: Int) = + getValueArgument(i)?.getIntConst() + ?: throw AssertionError("Value argument #$i should be an Int const: ${dump()}") - val superType = irCall.getTypeArgument(0) as? IrSimpleType - ?: fail("Type argument expected") - val patchedSuperType = replaceTypeArgumentsWithNullable(superType) +fun IrCall.getStringConstArgument(i: Int) = + getValueArgument(i)?.getStringConst() + ?: throw AssertionError("Value argument #$i should be a String const: ${dump()}") - val fakeClass = codegen.context.irFactory.buildClass { name = Name.special("") } - fakeClass.parent = codegen.context.ir.symbols.kotlinJvmInternalInvokeDynamicPackage - val irFakeOverride = buildFakeOverrideMember(patchedSuperType, irOriginalFun, fakeClass) as IrSimpleFunction - irFakeOverride.overriddenSymbols = listOf(irOriginalFun.symbol) +fun IrCall.getBooleanConstArgument(i: Int) = + getValueArgument(i)?.getBooleanConst() + ?: throw AssertionError("Value argument #$i should be a Boolean const: ${dump()}") - val asmMethod = codegen.methodSignatureMapper.mapAsmMethod(irFakeOverride) - return Type.getMethodType(asmMethod.descriptor) + +internal fun IrExpression.getIntConst() = + if (this is IrConst<*> && kind == IrConstKind.Int) + this.value as Int + else + null + +internal fun IrExpression.getStringConst() = + if (this is IrConst<*> && kind == IrConstKind.String) + this.value as String + else + null + +internal fun IrExpression.getBooleanConst() = + if (this is IrConst<*> && kind == IrConstKind.Boolean) + this.value as Boolean + else + null + +internal fun generateMethodHandle(jvmBackendContext: JvmBackendContext, irRawFunctionReference: IrRawFunctionReference): Handle { + return generateMethodHandle(jvmBackendContext, irRawFunctionReference.symbol.owner) +} + +fun generateMethodHandle(jvmBackendContext: JvmBackendContext, irFun: IrFunction): Handle { + val irNonFakeFun = when (irFun) { + is IrConstructor -> + irFun + is IrSimpleFunction -> { + findSuperDeclaration(irFun, false, jvmBackendContext.state.jvmDefaultMode) + } + else -> + throw java.lang.AssertionError("Simple function or constructor expected: ${irFun.render()}") } - // Given the following functional interface - // fun interface IFoo { - // fun foo(x: T): T - // } - // To comply with java.lang.invoke.LambdaMetafactory requirements, we need an instance method that accepts references - // (not primitives, and not unboxed inline classes). - // In order to do so, we replace type arguments with nullable types. - private fun replaceTypeArgumentsWithNullable(substitutedType: IrSimpleType) = - substitutedType.classifier.typeWithArguments( - substitutedType.arguments.map { typeArgument -> - when (typeArgument) { - is IrStarProjection -> typeArgument - is IrTypeProjection -> { - val type = typeArgument.type - if (type !is IrSimpleType || type.hasQuestionMark) - typeArgument - else { - makeTypeProjection(type.withHasQuestionMark(true), typeArgument.variance) - } - } - else -> - throw AssertionError("Unexpected type argument '$typeArgument' :: ${typeArgument::class.simpleName}") - } - } - ) + val irParentClass = irNonFakeFun.parent as? IrClass + ?: throw AssertionError("Unexpected parent: ${irNonFakeFun.parent.render()}") + val owner = jvmBackendContext.typeMapper.mapOwner(irParentClass) - private fun IrExpression.getIntConst() = - if (this is IrConst<*> && kind == IrConstKind.Int) - this.value as Int - else - null + val asmMethod = jvmBackendContext.methodSignatureMapper.mapAsmMethod(irNonFakeFun) + + val handleTag = getMethodKindTag(irNonFakeFun) + + return Handle(handleTag, owner.internalName, asmMethod.name, asmMethod.descriptor, irParentClass.isJvmInterface) +} + +internal fun getMethodKindTag(irFun: IrFunction) = + when { + irFun is IrConstructor -> + Opcodes.H_NEWINVOKESPECIAL + irFun.dispatchReceiverParameter == null -> + Opcodes.H_INVOKESTATIC + irFun.parentAsClass.isJvmInterface -> + Opcodes.H_INVOKEINTERFACE + else -> + Opcodes.H_INVOKEVIRTUAL + } - private fun IrExpression.getStringConst() = - if (this is IrConst<*> && kind == IrConstKind.String) - this.value as String - else - null -} \ No newline at end of file diff --git a/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt index ff710d2b9db..121c58c6fe9 100644 --- a/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt +++ b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam1.kt @@ -5,7 +5,7 @@ // CHECK_BYTECODE_TEXT // JVM_IR_TEMPLATES -// 0 java/lang/invoke/LambdaMetafactory +// 2 java/lang/invoke/LambdaMetafactory // FILE: serializableSam1.kt import java.io.ByteArrayInputStream diff --git a/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt index 2b0fbf675bb..02b13058575 100644 --- a/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt +++ b/compiler/testData/codegen/box/invokedynamic/sam/serializableSam2.kt @@ -5,7 +5,7 @@ // CHECK_BYTECODE_TEXT // JVM_IR_TEMPLATES -// 0 java/lang/invoke/LambdaMetafactory +// 2 java/lang/invoke/LambdaMetafactory // FILE: serializableSam2.kt import java.io.ByteArrayInputStream diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt b/compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt new file mode 100644 index 00000000000..e9fece2ae43 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt @@ -0,0 +1,62 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// We generate 1 clause in '$deserializeLambda' for each unique possible combination of deserialized lambda parameters +// (that's all information stored during indy lambda serialization, anyway). + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 12 java/lang/invoke/LambdaMetafactory +// 1 (LOOKUP|TABLE)SWITCH +// 24 java/lang/String\.equals + +// FILE: multipleTopLevelFunRefs.kt +import java.io.* + +fun plusK1(s: String) = s + "K" +fun plusK2(s: String) = s + "K" +fun plusK3(s: String) = s + "K" +fun plusK4(s: String) = s + "K" + +fun box(): String { + val t1 = roundtrip(Sam(::plusK1)).get("O") + if (t1 != "OK") return "Failed: t1='$t1'" + + val t1a = roundtrip(Sam(::plusK1)).get("O") + if (t1a != "OK") return "Failed: t1a='$t1a'" + + val t1b = roundtrip(Sam(::plusK1)).get("O") + if (t1b != "OK") return "Failed: t1b='$t1b'" + + val t2 = roundtrip(Sam(::plusK2)).get("O") + if (t2 != "OK") return "Failed: t2='$t2'" + + val t2a = roundtrip(Sam(::plusK2)).get("O") + if (t2a != "OK") return "Failed: t2a='$t2a'" + + val t3 = roundtrip(Sam(::plusK3)).get("O") + if (t3 != "OK") return "Failed: t3='$t3'" + + val t4 = roundtrip(Sam(::plusK4)).get("O") + if (t4 != "OK") return "Failed: t4='$t4'" + + val t4a = roundtrip(Sam(::plusK4)).get("O") + if (t4a != "OK") return "Failed: t4a='$t4a'" + + return "OK" +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt b/compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt new file mode 100644 index 00000000000..ad022942f35 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt @@ -0,0 +1,47 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 4 java/lang/invoke/LambdaMetafactory + +// FILE: sameImplMethodDifferentInterfaces.kt +import java.io.* + +fun plusK(s: String) = s + "K" + +fun box(): String { + val sam1 = roundtrip(Sam1(::plusK)) + if (sam1 !is Sam1) return "Failed: $sam1 !is Sam1" + val t1 = sam1.get("O") + if (t1 != "OK") return "Failed: $t1='$t1'" + + val sam2 = roundtrip(Sam2(::plusK)) + if (sam2 !is Sam2) return "Failed: $sam2 !is Sam2" + val t2 = sam2.get("O") + if (t2 != "OK") return "Failed: $t2='$t2'" + + return "OK" +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam1.java +import java.io.*; + +public interface Sam1 extends Serializable { + String get(String s); +} + +// FILE: Sam2.java +import java.io.*; + +public interface Sam2 extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt new file mode 100644 index 00000000000..5858205014b --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt @@ -0,0 +1,33 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableBoundClassMemberFunRef.kt +import java.io.* + +class C(val s: String) : Serializable { + fun plus(ss: String) = ss + s +} + +fun box(): String { + return roundtrip(Sam(C("K")::plus)) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt new file mode 100644 index 00000000000..6899f9745a9 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt @@ -0,0 +1,35 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableBoundInnerConstructorRef.kt +import java.io.* + +class Outer(val s1: String) : Serializable { + inner class Inner (val s2: String) { + fun test() = s1 + s2 + } +} + +fun box(): String { + return roundtrip(Sam(Outer("O")::Inner)) + .get("K").test() +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + Outer.Inner get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt new file mode 100644 index 00000000000..3b5401cdf38 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt @@ -0,0 +1,38 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableBoundInterfaceMemberFunRef.kt +import java.io.* + +interface Plus { + fun plus(ss: String): String +} + +class C(val s: String) : Plus, Serializable { + override fun plus(ss: String) = ss + s +} + +fun box(): String { + val p: Plus = C("K") + return roundtrip(Sam(p::plus)) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt new file mode 100644 index 00000000000..ea1446d1650 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableBoundTopLevelExtensionFunRef.kt +import java.io.* + +fun String.plusK(k: String) = this + k + +fun box(): String { + return roundtrip(Sam("O"::plusK)) + .get("K") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt new file mode 100644 index 00000000000..5e0008e3b74 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt +import java.io.* + +fun Int.plusK(k: String) = this.toChar().toString() + k + +fun box(): String { + return roundtrip(Sam(('O'.toInt())::plusK)) + .get("K") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt new file mode 100644 index 00000000000..fece3adde03 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt @@ -0,0 +1,33 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableClassMemberFunRef.kt +import java.io.* + +class C(val s: String) { + fun plus(ss: String) = ss + s +} + +fun box(): String { + return roundtrip(Sam(C::plus)) + .get(C("K"), "O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(C x, String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt new file mode 100644 index 00000000000..2dd4c0d38b6 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableConstructorRef.kt +import java.io.* + +class C(val t: String) + +fun box(): String { + return roundtrip(Sam(::C)) + .get("OK").t +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + C get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt new file mode 100644 index 00000000000..4bc2670ae92 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt @@ -0,0 +1,39 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory +// 2 A\.plus\(Ljava/lang/String;\)Ljava/lang/String;\, +// ^ check that we generate a reference to A::plus (same as javac) + +// FILE: serializableFakeOverrideFunRef.kt +import java.io.* + +abstract class A(val s: String) { + fun plus(ss: String) = ss + s +} + +abstract class B(s: String) : A(s) + +class C(s: String) : B(s) + +fun box(): String { + return roundtrip(Sam(C::plus)) + .get(C("K"), "O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(C x, String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt new file mode 100644 index 00000000000..f83475f8da4 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt @@ -0,0 +1,35 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableInnerConstructorRef.kt +import java.io.* + +class Outer(val s1: String) { + inner class Inner (val s2: String) { + fun test() = s1 + s2 + } +} + +fun box(): String { + return roundtrip(Sam(Outer::Inner)) + .get(Outer("O"), "K").test() +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + Outer.Inner get(Outer outer, String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt new file mode 100644 index 00000000000..21ace9cac94 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt @@ -0,0 +1,37 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableInterfaceMemberFunRef.kt +import java.io.* + +interface Plus { + fun plus(ss: String): String +} + +class C(val s: String) : Plus { + override fun plus(ss: String) = ss + s +} + +fun box(): String { + return roundtrip(Sam(Plus::plus)) + .get(C("K"), "O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(Plus x, String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt new file mode 100644 index 00000000000..752a8681bb7 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt @@ -0,0 +1,36 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableJavaStaticMethodRef.kt +import java.io.* + +fun box(): String { + return roundtrip(Sam(J::plusK)) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} + +// FILE: J.java +public class J { + public static String plusK(String s) { + return s + "K"; + } +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt new file mode 100644 index 00000000000..1f2e3b9ccb4 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt @@ -0,0 +1,29 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambda.kt +import java.io.* + +fun box(): String { + return roundtrip(Sam { s -> s + "K" }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt new file mode 100644 index 00000000000..54e1d24ee87 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt @@ -0,0 +1,32 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambdaCapturingBoxedInlineClassAny.kt +import java.io.* + +inline class IC(val x: Any) : Serializable + +fun box(): String { + val k: Any = IC("K") + return roundtrip(Sam { s -> s + (k as IC).x }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt new file mode 100644 index 00000000000..cecb3c39a60 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt @@ -0,0 +1,32 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambdaCapturingInlineClassAny.kt +import java.io.* + +inline class IC(val x: Any) + +fun box(): String { + val k = IC("K") + return roundtrip(Sam { s -> s + k.x }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt new file mode 100644 index 00000000000..babb7534b8a --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt @@ -0,0 +1,32 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambdaCapturingInlineClassInt.kt +import java.io.* + +inline class IC(val x: Int) + +fun box(): String { + val k = IC('K'.toInt()) + return roundtrip(Sam { s -> s + k.x.toChar().toString() }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt new file mode 100644 index 00000000000..22a82d5d181 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt @@ -0,0 +1,32 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambdaCapturingNullableInlineClassAny.kt +import java.io.* + +inline class IC(val x: Any) : Serializable + +fun box(): String { + val k: IC? = IC("K") + return roundtrip(Sam { s -> s + k!!.x }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt new file mode 100644 index 00000000000..b1f912fe102 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt @@ -0,0 +1,26 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambdaToKotlinInterface.kt +import java.io.* + +fun interface Sam : Serializable { + fun get(s: String): String +} + +fun box(): String { + return roundtrip(Sam { s -> s + "K" }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt new file mode 100644 index 00000000000..9651dfa0be7 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt @@ -0,0 +1,30 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableLambdaWithCapture.kt +import java.io.* + +fun box(): String { + val k = "K" + return roundtrip(Sam { s -> s + k }) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt new file mode 100644 index 00000000000..1b59b1b95de --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableTopLevelFunRef.kt +import java.io.* + +fun plusK(s: String) = s + "K" + +fun box(): String { + return roundtrip(Sam(::plusK)) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(String s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt new file mode 100644 index 00000000000..bd0ce5daec2 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt @@ -0,0 +1,31 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableTopLevelFunRefAsGenericInterface.kt +import java.io.* + +fun plusK(s: String) = s + "K" + +fun box(): String { + return roundtrip(Sam(::plusK)) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Sam.java +import java.io.*; + +public interface Sam extends Serializable { + String get(T s); +} diff --git a/compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt b/compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt new file mode 100644 index 00000000000..eb1f1f0b8f0 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt @@ -0,0 +1,38 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FULL_JDK + +// CHECK_BYTECODE_TEXT +// JVM_IR_TEMPLATES +// 2 java/lang/invoke/LambdaMetafactory + +// FILE: serializableWithBridge.kt +import java.io.* + +fun plusK(s: String) = s + "K" + +fun box(): String { + return roundtrip(Sam(::plusK)) + .get("O") +} + +fun roundtrip(x: T): T { + val out1 = ByteArrayOutputStream() + ObjectOutputStream(out1).writeObject(x) + return ObjectInputStream(ByteArrayInputStream(out1.toByteArray())).readObject() as T +} + +// FILE: Base1.java +public interface Base1 { + String get(T1 s); +} + +// FILE: Base2.java +public interface Base2 { + String get(T2 s); +} + +// FILE: Sam.java +public interface Sam extends Base1, Base2, java.io.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 2d643e27707..8a7c74cb4ae 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 @@ -22863,6 +22863,154 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + public class Serializable { + @Test + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @Test + @TestMetadata("multipleTopLevelFunRefs.kt") + public void testMultipleTopLevelFunRefs() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt"); + } + + @Test + @TestMetadata("sameImplMethodDifferentInterfaces.kt") + public void testSameImplMethodDifferentInterfaces() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt"); + } + + @Test + @TestMetadata("serializableBoundClassMemberFunRef.kt") + public void testSerializableBoundClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundInnerConstructorRef.kt") + public void testSerializableBoundInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableBoundInterfaceMemberFunRef.kt") + public void testSerializableBoundInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundTopLevelExtensionFunRef.kt") + public void testSerializableBoundTopLevelExtensionFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt") + public void testSerializableBoundTopLevelExtensionFunRefPrimitiveReceiver() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt"); + } + + @Test + @TestMetadata("serializableClassMemberFunRef.kt") + public void testSerializableClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableConstructorRef.kt") + public void testSerializableConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableFakeOverrideFunRef.kt") + public void testSerializableFakeOverrideFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt"); + } + + @Test + @TestMetadata("serializableInnerConstructorRef.kt") + public void testSerializableInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableInterfaceMemberFunRef.kt") + public void testSerializableInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableJavaStaticMethodRef.kt") + public void testSerializableJavaStaticMethodRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt"); + } + + @Test + @TestMetadata("serializableLambda.kt") + public void testSerializableLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingBoxedInlineClassAny.kt") + public void testSerializableLambdaCapturingBoxedInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingInlineClassAny.kt") + public void testSerializableLambdaCapturingInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingInlineClassInt.kt") + public void testSerializableLambdaCapturingInlineClassInt() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingNullableInlineClassAny.kt") + public void testSerializableLambdaCapturingNullableInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaToKotlinInterface.kt") + public void testSerializableLambdaToKotlinInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt"); + } + + @Test + @TestMetadata("serializableLambdaWithCapture.kt") + public void testSerializableLambdaWithCapture() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt"); + } + + @Test + @TestMetadata("serializableTopLevelFunRef.kt") + public void testSerializableTopLevelFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt"); + } + + @Test + @TestMetadata("serializableTopLevelFunRefAsGenericInterface.kt") + public void testSerializableTopLevelFunRefAsGenericInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt"); + } + + @Test + @TestMetadata("serializableWithBridge.kt") + public void testSerializableWithBridge() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt"); + } + } } @Nested 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 90b0bc6b3a2..8d8a79f287b 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 @@ -23001,6 +23001,154 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes } } } + + @Nested + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + public class Serializable { + @Test + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("multipleTopLevelFunRefs.kt") + public void testMultipleTopLevelFunRefs() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt"); + } + + @Test + @TestMetadata("sameImplMethodDifferentInterfaces.kt") + public void testSameImplMethodDifferentInterfaces() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt"); + } + + @Test + @TestMetadata("serializableBoundClassMemberFunRef.kt") + public void testSerializableBoundClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundInnerConstructorRef.kt") + public void testSerializableBoundInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableBoundInterfaceMemberFunRef.kt") + public void testSerializableBoundInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundTopLevelExtensionFunRef.kt") + public void testSerializableBoundTopLevelExtensionFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt"); + } + + @Test + @TestMetadata("serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt") + public void testSerializableBoundTopLevelExtensionFunRefPrimitiveReceiver() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt"); + } + + @Test + @TestMetadata("serializableClassMemberFunRef.kt") + public void testSerializableClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableConstructorRef.kt") + public void testSerializableConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableFakeOverrideFunRef.kt") + public void testSerializableFakeOverrideFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt"); + } + + @Test + @TestMetadata("serializableInnerConstructorRef.kt") + public void testSerializableInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt"); + } + + @Test + @TestMetadata("serializableInterfaceMemberFunRef.kt") + public void testSerializableInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt"); + } + + @Test + @TestMetadata("serializableJavaStaticMethodRef.kt") + public void testSerializableJavaStaticMethodRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt"); + } + + @Test + @TestMetadata("serializableLambda.kt") + public void testSerializableLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingBoxedInlineClassAny.kt") + public void testSerializableLambdaCapturingBoxedInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingInlineClassAny.kt") + public void testSerializableLambdaCapturingInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingInlineClassInt.kt") + public void testSerializableLambdaCapturingInlineClassInt() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt"); + } + + @Test + @TestMetadata("serializableLambdaCapturingNullableInlineClassAny.kt") + public void testSerializableLambdaCapturingNullableInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt"); + } + + @Test + @TestMetadata("serializableLambdaToKotlinInterface.kt") + public void testSerializableLambdaToKotlinInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt"); + } + + @Test + @TestMetadata("serializableLambdaWithCapture.kt") + public void testSerializableLambdaWithCapture() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt"); + } + + @Test + @TestMetadata("serializableTopLevelFunRef.kt") + public void testSerializableTopLevelFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt"); + } + + @Test + @TestMetadata("serializableTopLevelFunRefAsGenericInterface.kt") + public void testSerializableTopLevelFunRefAsGenericInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt"); + } + + @Test + @TestMetadata("serializableWithBridge.kt") + public void testSerializableWithBridge() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt"); + } + } } @Nested diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 475076c5fba..b4360aa2ccb 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -19158,6 +19158,134 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes } } } + + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Serializable extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("multipleTopLevelFunRefs.kt") + public void testMultipleTopLevelFunRefs() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/multipleTopLevelFunRefs.kt"); + } + + @TestMetadata("sameImplMethodDifferentInterfaces.kt") + public void testSameImplMethodDifferentInterfaces() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/sameImplMethodDifferentInterfaces.kt"); + } + + @TestMetadata("serializableBoundClassMemberFunRef.kt") + public void testSerializableBoundClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundClassMemberFunRef.kt"); + } + + @TestMetadata("serializableBoundInnerConstructorRef.kt") + public void testSerializableBoundInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInnerConstructorRef.kt"); + } + + @TestMetadata("serializableBoundInterfaceMemberFunRef.kt") + public void testSerializableBoundInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundInterfaceMemberFunRef.kt"); + } + + @TestMetadata("serializableBoundTopLevelExtensionFunRef.kt") + public void testSerializableBoundTopLevelExtensionFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRef.kt"); + } + + @TestMetadata("serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt") + public void testSerializableBoundTopLevelExtensionFunRefPrimitiveReceiver() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableBoundTopLevelExtensionFunRefPrimitiveReceiver.kt"); + } + + @TestMetadata("serializableClassMemberFunRef.kt") + public void testSerializableClassMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableClassMemberFunRef.kt"); + } + + @TestMetadata("serializableConstructorRef.kt") + public void testSerializableConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableConstructorRef.kt"); + } + + @TestMetadata("serializableFakeOverrideFunRef.kt") + public void testSerializableFakeOverrideFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableFakeOverrideFunRef.kt"); + } + + @TestMetadata("serializableInnerConstructorRef.kt") + public void testSerializableInnerConstructorRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInnerConstructorRef.kt"); + } + + @TestMetadata("serializableInterfaceMemberFunRef.kt") + public void testSerializableInterfaceMemberFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableInterfaceMemberFunRef.kt"); + } + + @TestMetadata("serializableJavaStaticMethodRef.kt") + public void testSerializableJavaStaticMethodRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableJavaStaticMethodRef.kt"); + } + + @TestMetadata("serializableLambda.kt") + public void testSerializableLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambda.kt"); + } + + @TestMetadata("serializableLambdaCapturingBoxedInlineClassAny.kt") + public void testSerializableLambdaCapturingBoxedInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingBoxedInlineClassAny.kt"); + } + + @TestMetadata("serializableLambdaCapturingInlineClassAny.kt") + public void testSerializableLambdaCapturingInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassAny.kt"); + } + + @TestMetadata("serializableLambdaCapturingInlineClassInt.kt") + public void testSerializableLambdaCapturingInlineClassInt() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingInlineClassInt.kt"); + } + + @TestMetadata("serializableLambdaCapturingNullableInlineClassAny.kt") + public void testSerializableLambdaCapturingNullableInlineClassAny() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaCapturingNullableInlineClassAny.kt"); + } + + @TestMetadata("serializableLambdaToKotlinInterface.kt") + public void testSerializableLambdaToKotlinInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaToKotlinInterface.kt"); + } + + @TestMetadata("serializableLambdaWithCapture.kt") + public void testSerializableLambdaWithCapture() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableLambdaWithCapture.kt"); + } + + @TestMetadata("serializableTopLevelFunRef.kt") + public void testSerializableTopLevelFunRef() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRef.kt"); + } + + @TestMetadata("serializableTopLevelFunRefAsGenericInterface.kt") + public void testSerializableTopLevelFunRefAsGenericInterface() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableTopLevelFunRefAsGenericInterface.kt"); + } + + @TestMetadata("serializableWithBridge.kt") + public void testSerializableWithBridge() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/serializable/serializableWithBridge.kt"); + } + } } @TestMetadata("compiler/testData/codegen/box/ir") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java index 74e0380c7eb..b31916d78dd 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/es6/semantics/IrJsCodegenBoxES6TestGenerated.java @@ -16152,6 +16152,19 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes } } } + + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Serializable extends AbstractIrJsCodegenBoxES6Test { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath); + } + + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); + } + } } @TestMetadata("compiler/testData/codegen/box/ir") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java index 8aaa191c19f..79d7f4d0f45 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/semantics/IrJsCodegenBoxTestGenerated.java @@ -15558,6 +15558,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { } } } + + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Serializable extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } } @TestMetadata("compiler/testData/codegen/box/ir") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java index 4d4561fc2b4..0ae4f81f3dd 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/semantics/JsCodegenBoxTestGenerated.java @@ -15588,6 +15588,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { } } } + + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Serializable extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } } @TestMetadata("compiler/testData/codegen/box/ir") diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java index 42b4ed08fee..b25cb51c2e7 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/wasm/semantics/IrCodegenBoxWasmTestGenerated.java @@ -14884,6 +14884,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest } } } + + @TestMetadata("compiler/testData/codegen/box/invokedynamic/serializable") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class Serializable extends AbstractIrCodegenBoxWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInSerializable() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/serializable"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); + } + } } @TestMetadata("compiler/testData/codegen/box/ir")