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 d11440a7e7e..959af81e15f 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 @@ -20035,6 +20035,58 @@ public class FirBlackBoxCodegenTestGenerated extends AbstractFirBlackBoxCodegenT runTest("compiler/testData/codegen/box/invokedynamic/sam/unboundFunctionReferenceEquality.kt"); } + @Nested + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + public class FunctionExpressionArgument { + @Test + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("capturedSamArgument.kt") + public void testCapturedSamArgument() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt"); + } + + @Test + @TestMetadata("capturingLambda.kt") + public void testCapturingLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt"); + } + + @Test + @TestMetadata("extensionLambda1.kt") + public void testExtensionLambda1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt"); + } + + @Test + @TestMetadata("extensionLambda2.kt") + public void testExtensionLambda2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt"); + } + + @Test + @TestMetadata("genericSam1.kt") + public void testGenericSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt"); + } + + @Test + @TestMetadata("genericSam2.kt") + public void testGenericSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt"); + } + } + @Nested @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt index 98a807e89a6..1cebd00bcd4 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/FunctionReferenceLowering.kt @@ -13,6 +13,10 @@ import org.jetbrains.kotlin.backend.common.phaser.makeIrFilePhase import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.JvmLoweredDeclarationOrigin import org.jetbrains.kotlin.backend.jvm.ir.* +import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArguments +import org.jetbrains.kotlin.backend.jvm.lower.indy.LambdaMetafactoryArgumentsBuilder +import org.jetbrains.kotlin.backend.jvm.lower.indy.SamDelegatingLambdaBlock +import org.jetbrains.kotlin.backend.jvm.lower.indy.SamDelegatingLambdaBuilder import org.jetbrains.kotlin.backend.jvm.lower.inlineclasses.InlineClassAbi import org.jetbrains.kotlin.config.JvmClosureGenerationScheme import org.jetbrains.kotlin.descriptors.DescriptorVisibilities @@ -142,6 +146,14 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) } else if (invokable is IrBlock && invokable.origin.isLambda && invokable.statements.last() is IrFunctionReference) { invokable.statements.dropLast(1).forEach { it.transform(this, null) } invokable.statements.last() as IrFunctionReference + } else if (shouldGenerateIndySamConversions && canGenerateIndySamConversionOnFunctionalExpression(samSuperType, invokable)) { + val lambdaBlock = SamDelegatingLambdaBuilder(context) + .build(invokable, samSuperType, currentScope!!.scope.scopeOwnerSymbol) + val lambdaMetafactoryArguments = LambdaMetafactoryArgumentsBuilder(context, crossinlineLambdas) + .getLambdaMetafactoryArgumentsOrNull(lambdaBlock.ref, samSuperType, false) + ?: return super.visitTypeOperator(expression) + invokable.transformChildrenVoid() + return wrapSamDelegatingLambdaWithIndySamConversion(samSuperType, lambdaBlock, lambdaMetafactoryArguments) } else { return super.visitTypeOperator(expression) } @@ -159,18 +171,36 @@ internal class FunctionReferenceLowering(private val context: JvmBackendContext) return FunctionReferenceBuilder(reference, samSuperType).build() } + private fun canGenerateIndySamConversionOnFunctionalExpression(samSuperType: IrType, expression: IrExpression): Boolean { + val samClass = samSuperType.classOrNull + ?: throw AssertionError("Class type expected: ${samSuperType.render()}") + if (!samClass.owner.isFromJava()) + return false + if (expression is IrBlock && expression.origin == IrStatementOrigin.ADAPTED_FUNCTION_REFERENCE) + return false + return true + } + + private fun wrapSamDelegatingLambdaWithIndySamConversion( + samSuperType: IrType, + lambdaBlock: SamDelegatingLambdaBlock, + lambdaMetafactoryArguments: LambdaMetafactoryArguments + ): IrExpression { + val indySamConversion = wrapWithIndySamConversion(samSuperType, lambdaMetafactoryArguments) + lambdaBlock.replaceRefWith(indySamConversion) + return lambdaBlock.block + } + private fun wrapSamConversionArgumentWithIndySamConversion( expression: IrTypeOperatorCall, lambdaMetafactoryArguments: LambdaMetafactoryArguments ): IrExpression { val samType = expression.typeOperand return when (val argument = expression.argument) { - is IrFunctionReference -> { + is IrFunctionReference -> wrapWithIndySamConversion(samType, lambdaMetafactoryArguments) - } - is IrBlock -> { + is IrBlock -> wrapFunctionReferenceInsideBlockWithIndySamConversion(samType, lambdaMetafactoryArguments, argument) - } else -> throw AssertionError("Block or function reference expected: ${expression.render()}") } } diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/LambdaMetafactoryArguments.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt similarity index 99% rename from compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/LambdaMetafactoryArguments.kt rename to compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt index 8411de1f7ae..bcf1aa2ffa5 100644 --- a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/LambdaMetafactoryArguments.kt +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/LambdaMetafactoryArguments.kt @@ -3,7 +3,7 @@ * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. */ -package org.jetbrains.kotlin.backend.jvm.lower +package org.jetbrains.kotlin.backend.jvm.lower.indy import org.jetbrains.kotlin.backend.common.ir.allOverridden import org.jetbrains.kotlin.backend.common.lower.VariableRemapper @@ -12,6 +12,7 @@ import org.jetbrains.kotlin.backend.jvm.JvmBackendContext import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod import org.jetbrains.kotlin.backend.jvm.ir.isCompiledToJvmDefault +import org.jetbrains.kotlin.backend.jvm.lower.findInterfaceImplementation import org.jetbrains.kotlin.builtins.functions.BuiltInFunctionArity import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.ir.builders.declarations.buildClass @@ -30,14 +31,14 @@ import org.jetbrains.kotlin.ir.util.render import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid import org.jetbrains.kotlin.name.Name -class LambdaMetafactoryArguments( +internal class LambdaMetafactoryArguments( val samMethod: IrSimpleFunction, val fakeInstanceMethod: IrSimpleFunction, val implMethodReference: IrFunctionReference, val extraOverriddenMethods: List ) -class LambdaMetafactoryArgumentsBuilder( +internal class LambdaMetafactoryArgumentsBuilder( private val context: JvmBackendContext, private val crossinlineLambdas: Set ) { diff --git a/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/SamDelegatingLambdaBlock.kt b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/SamDelegatingLambdaBlock.kt new file mode 100644 index 00000000000..0211b355e0a --- /dev/null +++ b/compiler/ir/backend.jvm/src/org/jetbrains/kotlin/backend/jvm/lower/indy/SamDelegatingLambdaBlock.kt @@ -0,0 +1,234 @@ +/* + * Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors. + * Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.backend.jvm.lower.indy + +import org.jetbrains.kotlin.backend.jvm.JvmBackendContext +import org.jetbrains.kotlin.backend.jvm.ir.JvmIrBuilder +import org.jetbrains.kotlin.backend.jvm.ir.createJvmIrBuilder +import org.jetbrains.kotlin.backend.jvm.ir.getSingleAbstractMethod +import org.jetbrains.kotlin.descriptors.DescriptorVisibilities +import org.jetbrains.kotlin.descriptors.Modality +import org.jetbrains.kotlin.ir.builders.* +import org.jetbrains.kotlin.ir.builders.declarations.buildFun +import org.jetbrains.kotlin.ir.builders.declarations.buildValueParameter +import org.jetbrains.kotlin.ir.declarations.* +import org.jetbrains.kotlin.ir.expressions.IrContainerExpression +import org.jetbrains.kotlin.ir.expressions.IrExpression +import org.jetbrains.kotlin.ir.expressions.IrFunctionReference +import org.jetbrains.kotlin.ir.expressions.IrStatementOrigin +import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl +import org.jetbrains.kotlin.ir.symbols.IrSymbol +import org.jetbrains.kotlin.ir.types.* +import org.jetbrains.kotlin.ir.util.functions +import org.jetbrains.kotlin.ir.util.render +import org.jetbrains.kotlin.name.Name +import org.jetbrains.kotlin.util.OperatorNameConventions + +internal sealed class SamDelegatingLambdaBlock { + abstract val block: IrContainerExpression + abstract val ref: IrFunctionReference + abstract fun replaceRefWith(expression: IrExpression) +} + + +internal class RegularDelegatingLambdaBlock( + override val block: IrContainerExpression, + override val ref: IrFunctionReference +) : SamDelegatingLambdaBlock() { + + override fun replaceRefWith(expression: IrExpression) { + block.statements[block.statements.size - 1] = expression + block.type = expression.type + } +} + + +internal class NullableDelegatingLambdaBlock( + override val block: IrContainerExpression, + override val ref: IrFunctionReference, + private val ifExpr: IrExpression, + private val ifNotNullBlock: IrContainerExpression +) : SamDelegatingLambdaBlock() { + + override fun replaceRefWith(expression: IrExpression) { + ifNotNullBlock.statements[ifNotNullBlock.statements.size - 1] = expression + ifNotNullBlock.type = expression.type + ifExpr.type = expression.type + block.type = expression.type + } +} + + +internal class SamDelegatingLambdaBuilder(private val jvmContext: JvmBackendContext) { + fun build(expression: IrExpression, superType: IrType, scopeSymbol: IrSymbol): SamDelegatingLambdaBlock { + return if (superType.isNullable() && expression.type.isNullable()) + buildNullableDelegatingLambda(expression, superType, scopeSymbol) + else + buildRegularDelegatingLambda(expression, superType, scopeSymbol) + } + + private fun buildRegularDelegatingLambda( + expression: IrExpression, + superType: IrType, + scopeSymbol: IrSymbol + ): SamDelegatingLambdaBlock { + lateinit var ref: IrFunctionReference + val block = jvmContext.createJvmIrBuilder(scopeSymbol, expression.startOffset, expression.endOffset).run { + // { + // val tmp = + // fun ``(p1: T1, ..., pN: TN): R = + // tmp.invoke(p1, ..., pN) + // ::`` + // } + + irBlock(origin = IrStatementOrigin.LAMBDA) { + val tmp = irTemporary(expression) + val lambda = createDelegatingLambda(scopeSymbol, expression, superType, tmp) + .also { +it } + ref = createDelegatingLambdaReference(expression, lambda) + .also { +it } + } + } + block.type = expression.type + return RegularDelegatingLambdaBlock(block, ref) + } + + private fun buildNullableDelegatingLambda( + expression: IrExpression, + superType: IrType, + scopeSymbol: IrSymbol + ): SamDelegatingLambdaBlock { + lateinit var ref: IrFunctionReference + lateinit var ifExpr: IrExpression + lateinit var ifNotNullBlock: IrContainerExpression + val block = jvmContext.createJvmIrBuilder(scopeSymbol, expression.startOffset, expression.endOffset).run { + // { + // val tmp = + // if (tmp == null) + // null + // else { + // fun ``(p1: T1, ..., pN: TN): R = + // tmp.invoke(p1, ..., pN) + // ::`` + // } + // } + + irBlock(origin = IrStatementOrigin.LAMBDA) { + val tmp = irTemporary(expression) + ifNotNullBlock = irBlock { + val lambda = createDelegatingLambda(scopeSymbol, expression, superType, tmp) + .also { +it } + ref = createDelegatingLambdaReference(expression, lambda) + .also { +it } + } + ifExpr = irIfNull(expression.type, irGet(tmp), irNull(), ifNotNullBlock) + .also { +it } + } + } + block.type = expression.type + return NullableDelegatingLambdaBlock(block, ref, ifExpr, ifNotNullBlock) + } + + private fun createDelegatingLambda( + scopeSymbol: IrSymbol, + expression: IrExpression, + superType: IrType, + tmp: IrVariable + ): IrSimpleFunction { + val superMethod = superType.getSingleAbstractMethod() + ?: throw AssertionError("SAM type expected: ${superType.render()}") + val effectiveValueParametersCount = superMethod.valueParameters.size + + if (superMethod.extensionReceiverParameter == null) 0 else 1 + val invocableFunctionClass = + if (superMethod.isSuspend) + jvmContext.ir.symbols.suspendFunctionN(effectiveValueParametersCount).owner + else + jvmContext.ir.symbols.functionN(effectiveValueParametersCount).owner + val invokeFunction = invocableFunctionClass.functions.single { it.name == OperatorNameConventions.INVOKE } + val typeSubstitutor = createTypeSubstitutor(superType) + + return jvmContext.irFactory.buildFun { + name = Name.special("") + returnType = typeSubstitutor.substitute(superMethod.returnType) + visibility = DescriptorVisibilities.LOCAL + modality = Modality.FINAL + origin = IrDeclarationOrigin.LOCAL_FUNCTION_FOR_LAMBDA + isSuspend = superMethod.isSuspend + }.also { lambda -> + lambda.dispatchReceiverParameter = null + lambda.extensionReceiverParameter = null + lambda.valueParameters = createLambdaValueParameters(superMethod, lambda, typeSubstitutor) + lambda.body = jvmContext.createJvmIrBuilder(lambda.symbol, expression.startOffset, expression.endOffset) + .irBlockBody { + +irReturn( + irCall(invokeFunction).also { invokeCall -> + invokeCall.dispatchReceiver = irGet(tmp) + var parameterIndex = 0 + invokeFunction.extensionReceiverParameter?.let { + invokeCall.extensionReceiver = irGet(lambda.valueParameters[parameterIndex++]) + } + for (argumentIndex in invokeFunction.valueParameters.indices) { + invokeCall.putValueArgument(argumentIndex, irGet(lambda.valueParameters[parameterIndex++])) + } + } + ) + } + lambda.parent = scopeSymbol.owner as IrDeclarationParent + } + } + + private fun createLambdaValueParameters( + superMethod: IrSimpleFunction, + lambda: IrSimpleFunction, + typeSubstitutor: IrTypeSubstitutor + ): List { + val lambdaParameters = ArrayList() + var index = 0 + superMethod.extensionReceiverParameter?.let { superExtensionReceiver -> + lambdaParameters.add(superExtensionReceiver.copySubstituted(lambda, typeSubstitutor, index++, Name.identifier("\$receiver"))) + } + superMethod.valueParameters.mapTo(lambdaParameters) { superValueParameter -> + superValueParameter.copySubstituted(lambda, typeSubstitutor, index++) + } + return lambdaParameters + } + + private fun IrValueParameter.copySubstituted( + function: IrSimpleFunction, + substitutor: IrTypeSubstitutor, + newIndex: Int, + newName: Name = name + ) = + buildValueParameter(function) { + name = newName + index = newIndex + type = substitutor.substitute(this@copySubstituted.type) + } + + private fun JvmIrBuilder.createDelegatingLambdaReference(expression: IrExpression, lambda: IrSimpleFunction): IrFunctionReference { + return IrFunctionReferenceImpl( + startOffset, endOffset, + expression.type, + lambda.symbol, + typeArgumentsCount = 0, + valueArgumentsCount = lambda.valueParameters.size, + reflectionTarget = null, + origin = IrStatementOrigin.LAMBDA + ) + } + + private fun createTypeSubstitutor(irType: IrType): IrTypeSubstitutor { + if (irType !is IrSimpleType) + throw AssertionError("Simple type expected: ${irType.render()}") + val irClassSymbol = irType.classOrNull + ?: throw AssertionError("Class type expected: ${irType.render()}") + return IrTypeSubstitutor( + irClassSymbol.owner.typeParameters.map { it.symbol }, + irType.arguments, + jvmContext.irBuiltIns + ) + } +} diff --git a/compiler/testData/codegen/box/annotations/annotatedLambda/samFunReference.kt b/compiler/testData/codegen/box/annotations/annotatedLambda/samFunReference.kt index 74ea0405d94..da6f739deda 100644 --- a/compiler/testData/codegen/box/annotations/annotatedLambda/samFunReference.kt +++ b/compiler/testData/codegen/box/annotations/annotatedLambda/samFunReference.kt @@ -1,4 +1,5 @@ // TARGET_BACKEND: JVM +// SAM_CONVERSIONS: CLASS // WITH_RUNTIME // FILE: Test.java diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt new file mode 100644 index 00000000000..5577bb6d634 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt @@ -0,0 +1,15 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: capturedSamArgument.kt +fun box(): String { + var lambda = { "OK" } + val sam = Sam(lambda) + lambda = { "Failed" } + return sam.get() +} + +// FILE: Sam.java +public interface Sam { + String get(); +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt new file mode 100644 index 00000000000..3498c335a9f --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt @@ -0,0 +1,14 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: capturingLambda.kt +fun box(): String { + val co = 'O' + val lambda = { co.toString() + "K" } + return Sam(lambda).get() +} + +// FILE: Sam.java +public interface Sam { + String get(); +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt new file mode 100644 index 00000000000..96efac5328e --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt @@ -0,0 +1,15 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: extensionLambda1.kt +fun samExtLambda(ext: String.() -> String) = Sam(ext) + +fun box(): String { + val oChar = 'O' + return samExtLambda { oChar.toString() + this }.get("K") +} + +// FILE: Sam.java +public interface Sam { + String get(String s); +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt new file mode 100644 index 00000000000..0d844f42f48 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt @@ -0,0 +1,15 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: extensionLambda2.kt +fun samExtLambda(ext: String.(String) -> String) = Sam(ext) + +fun box(): String { + val oChar = 'O' + return samExtLambda { oChar.toString() + this + it }.get("", "K") +} + +// FILE: Sam.java +public interface Sam { + String get(String s1, String s2); +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt new file mode 100644 index 00000000000..79a9fee256e --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt @@ -0,0 +1,11 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: genericSam1.kt + +fun box(): String = Sam { "O" + it }.get("K") + +// FILE: Sam.java +public interface Sam { + R get(T x); +} diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt new file mode 100644 index 00000000000..7817b5d6021 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt @@ -0,0 +1,13 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: genericSam2.kt + +fun test(lambda: (FT) -> FR) = Sam(lambda) + +fun box(): String = test { "O" + it }.get("K") + +// FILE: Sam.java +public interface Sam { + R get(T x); +} diff --git a/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt new file mode 100644 index 00000000000..7c44f655db8 --- /dev/null +++ b/compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt @@ -0,0 +1,12 @@ +// TARGET_BACKEND: JVM +// JVM_TARGET: 1.8 +// SAM_CONVERSIONS: INDY +// FILE: simple.kt +val lambda = { "OK" } + +fun box() = Sam(lambda).get() + +// FILE: Sam.java +public interface Sam { + String get(); +} \ No newline at end of file diff --git a/compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt b/compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt index 9e678745924..486484f9353 100644 --- a/compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt +++ b/compiler/testData/codegen/box/sam/constructors/sameWrapperClass.kt @@ -4,6 +4,7 @@ // IGNORE_BACKEND: JS_IR_ES6 // TODO: muted automatically, investigate should it be ran for JS or not // IGNORE_BACKEND: JS, NATIVE +// SAM_CONVERSIONS: CLASS fun box(): String { val f = { } diff --git a/compiler/testData/codegen/box/sam/inlinedSamWrapper.kt b/compiler/testData/codegen/box/sam/inlinedSamWrapper.kt index 6b88883410a..47d04dde445 100644 --- a/compiler/testData/codegen/box/sam/inlinedSamWrapper.kt +++ b/compiler/testData/codegen/box/sam/inlinedSamWrapper.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: MyRunnable.java public interface MyRunnable { public void run(); diff --git a/compiler/testData/codegen/box/sam/kt17091.kt b/compiler/testData/codegen/box/sam/kt17091.kt index b6da586db2a..834ddb369ba 100644 --- a/compiler/testData/codegen/box/sam/kt17091.kt +++ b/compiler/testData/codegen/box/sam/kt17091.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: Foo.kt package foo diff --git a/compiler/testData/codegen/box/sam/kt17091_2.kt b/compiler/testData/codegen/box/sam/kt17091_2.kt index a4fffc0cec0..beb111bf5bf 100644 --- a/compiler/testData/codegen/box/sam/kt17091_2.kt +++ b/compiler/testData/codegen/box/sam/kt17091_2.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: Foo.kt @file:JvmName("testXX") package test diff --git a/compiler/testData/codegen/box/sam/kt17091_3.kt b/compiler/testData/codegen/box/sam/kt17091_3.kt index 2282bfc5444..08c2146c7e6 100644 --- a/compiler/testData/codegen/box/sam/kt17091_3.kt +++ b/compiler/testData/codegen/box/sam/kt17091_3.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: Foo.kt @file:JvmMultifileClass @file:JvmName("testX") diff --git a/compiler/testData/codegen/box/sam/kt17091_4.kt b/compiler/testData/codegen/box/sam/kt17091_4.kt index e41aad8b0de..4ffd660b508 100644 --- a/compiler/testData/codegen/box/sam/kt17091_4.kt +++ b/compiler/testData/codegen/box/sam/kt17091_4.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: Foo.kt @file:JvmMultifileClass @file:JvmName("testX") diff --git a/compiler/testData/codegen/box/sam/kt22906.kt b/compiler/testData/codegen/box/sam/kt22906.kt index 16f799c8a9e..b63b761c3ef 100644 --- a/compiler/testData/codegen/box/sam/kt22906.kt +++ b/compiler/testData/codegen/box/sam/kt22906.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: kt22906_1.kt package test diff --git a/compiler/testData/codegen/box/sam/kt22906_2.kt b/compiler/testData/codegen/box/sam/kt22906_2.kt index 1699464b814..5e212ea834d 100644 --- a/compiler/testData/codegen/box/sam/kt22906_2.kt +++ b/compiler/testData/codegen/box/sam/kt22906_2.kt @@ -1,5 +1,6 @@ // TARGET_BACKEND: JVM // WITH_RUNTIME +// SAM_CONVERSIONS: CLASS // FILE: kt22906_1.kt package test diff --git a/compiler/testData/codegen/box/sam/predicateSamWrapper.kt b/compiler/testData/codegen/box/sam/predicateSamWrapper.kt index ef7dc3f32ff..1fe7b94a9e4 100644 --- a/compiler/testData/codegen/box/sam/predicateSamWrapper.kt +++ b/compiler/testData/codegen/box/sam/predicateSamWrapper.kt @@ -2,6 +2,7 @@ // WITH_RUNTIME // FULL_JDK // SKIP_JDK6 +// SAM_CONVERSIONS: CLASS // FILE: test.kt // Test that SAM wrappers with type parameters are cached properly. class A { diff --git a/compiler/testData/codegen/bytecodeText/sam/samWrapperForNullableInitialization.kt b/compiler/testData/codegen/bytecodeText/sam/samWrapperForNullableInitialization.kt index 54a2d031fd8..73980bdade3 100644 --- a/compiler/testData/codegen/bytecodeText/sam/samWrapperForNullableInitialization.kt +++ b/compiler/testData/codegen/bytecodeText/sam/samWrapperForNullableInitialization.kt @@ -26,7 +26,7 @@ fun test() { // JVM_IR_TEMPLATES // @TestKt.class -// 1 NEW +// 0 NEW // 1 IFNONNULL // 0 IFNULL // 2 ACONST_NULL 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 960be73cd2a..1261d8e2386 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 @@ -20035,6 +20035,58 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { runTest("compiler/testData/codegen/box/invokedynamic/sam/unboundFunctionReferenceEquality.kt"); } + @Nested + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + public class FunctionExpressionArgument { + @Test + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @Test + @TestMetadata("capturedSamArgument.kt") + public void testCapturedSamArgument() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt"); + } + + @Test + @TestMetadata("capturingLambda.kt") + public void testCapturingLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt"); + } + + @Test + @TestMetadata("extensionLambda1.kt") + public void testExtensionLambda1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt"); + } + + @Test + @TestMetadata("extensionLambda2.kt") + public void testExtensionLambda2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt"); + } + + @Test + @TestMetadata("genericSam1.kt") + public void testGenericSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt"); + } + + @Test + @TestMetadata("genericSam2.kt") + public void testGenericSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt"); + } + } + @Nested @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") 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 096f5e8e728..6968dda1cf0 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 @@ -20035,6 +20035,58 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes runTest("compiler/testData/codegen/box/invokedynamic/sam/unboundFunctionReferenceEquality.kt"); } + @Nested + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + public class FunctionExpressionArgument { + @Test + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true); + } + + @Test + @TestMetadata("capturedSamArgument.kt") + public void testCapturedSamArgument() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt"); + } + + @Test + @TestMetadata("capturingLambda.kt") + public void testCapturingLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt"); + } + + @Test + @TestMetadata("extensionLambda1.kt") + public void testExtensionLambda1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt"); + } + + @Test + @TestMetadata("extensionLambda2.kt") + public void testExtensionLambda2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt"); + } + + @Test + @TestMetadata("genericSam1.kt") + public void testGenericSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt"); + } + + @Test + @TestMetadata("genericSam2.kt") + public void testGenericSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt"); + } + + @Test + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt"); + } + } + @Nested @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") diff --git a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index 035e1085ce9..79bd3056596 100644 --- a/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests-gen/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -16793,6 +16793,54 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes runTest("compiler/testData/codegen/box/invokedynamic/sam/unboundFunctionReferenceEquality.kt"); } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FunctionExpressionArgument extends AbstractLightAnalysisModeTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.JVM, testDataFilePath); + } + + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true); + } + + @TestMetadata("capturedSamArgument.kt") + public void testCapturedSamArgument() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturedSamArgument.kt"); + } + + @TestMetadata("capturingLambda.kt") + public void testCapturingLambda() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/capturingLambda.kt"); + } + + @TestMetadata("extensionLambda1.kt") + public void testExtensionLambda1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda1.kt"); + } + + @TestMetadata("extensionLambda2.kt") + public void testExtensionLambda2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/extensionLambda2.kt"); + } + + @TestMetadata("genericSam1.kt") + public void testGenericSam1() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam1.kt"); + } + + @TestMetadata("genericSam2.kt") + public void testGenericSam2() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/genericSam2.kt"); + } + + @TestMetadata("simple.kt") + public void testSimple() throws Exception { + runTest("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument/simple.kt"); + } + } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 ce88f8a2ccd..683bca7103b 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 @@ -14578,6 +14578,19 @@ public class IrJsCodegenBoxES6TestGenerated extends AbstractIrJsCodegenBoxES6Tes KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FunctionExpressionArgument extends AbstractIrJsCodegenBoxES6Test { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR_ES6, testDataFilePath); + } + + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR_ES6, true); + } + } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 421555239ab..860b8daad47 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 @@ -14063,6 +14063,19 @@ public class IrJsCodegenBoxTestGenerated extends AbstractIrJsCodegenBoxTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FunctionExpressionArgument extends AbstractIrJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS_IR, testDataFilePath); + } + + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS_IR, true); + } + } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 4c83fd5c74c..ffa34fb50c2 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 @@ -14128,6 +14128,19 @@ public class JsCodegenBoxTestGenerated extends AbstractJsCodegenBoxTest { KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FunctionExpressionArgument extends AbstractJsCodegenBoxTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.JS, testDataFilePath); + } + + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JS, true); + } + } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) 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 ed5098f8259..bb1aae8e6a8 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 @@ -8164,6 +8164,19 @@ public class IrCodegenBoxWasmTestGenerated extends AbstractIrCodegenBoxWasmTest KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class FunctionExpressionArgument extends AbstractIrCodegenBoxWasmTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest0(this::doTest, TargetBackend.WASM, testDataFilePath); + } + + public void testAllFilesPresentInFunctionExpressionArgument() throws Exception { + KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("compiler/testData/codegen/box/invokedynamic/sam/functionExpressionArgument"), Pattern.compile("^([^_](.+))\\.kt$"), null, TargetBackend.WASM, true); + } + } + @TestMetadata("compiler/testData/codegen/box/invokedynamic/sam/inlineClassInSignature") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)