diff --git a/compiler/testData/codegen/box/functions/functionNtoString.kt b/compiler/testData/codegen/box/functions/functionNtoString.kt index 2f5720b03d7..d4a9fc38291 100644 --- a/compiler/testData/codegen/box/functions/functionNtoString.kt +++ b/compiler/testData/codegen/box/functions/functionNtoString.kt @@ -1,28 +1,35 @@ +// WITH_REFLECT + fun check(expected: String, obj: Any?) { val actual = obj.toString() if (actual != expected) throw AssertionError("Expected: $expected, actual: $actual") } - fun box(): String { - check("kotlin.jvm.functions.Function0") + check("() -> kotlin.Unit") { -> } - check("kotlin.jvm.functions.Function0") + check("() -> kotlin.Int") { -> 42 } - check("kotlin.jvm.functions.Function1", + check("(kotlin.String) -> kotlin.Long", fun (s: String) = 42.toLong()) - check("kotlin.jvm.functions.Function2") + check("(kotlin.Int, kotlin.Int) -> kotlin.Unit") { x: Int, y: Int -> } - check("kotlin.jvm.functions.Function1", + check("kotlin.Int.() -> kotlin.Unit", fun Int.() {}) - check("kotlin.jvm.functions.Function1", - fun Unit.(): Int = 42) - check("kotlin.jvm.functions.Function2", - fun String.(s: String): Long = 42.toLong()) - check("kotlin.jvm.functions.Function3", - fun Int.(x: Int, y: Int) {}) + check("kotlin.Unit.() -> kotlin.Int?", + fun Unit.(): Int? = 42) + check("kotlin.String.(kotlin.String?) -> kotlin.Long", + fun String.(s: String?): Long = 42.toLong()) + check("kotlin.collections.List.(kotlin.collections.MutableSet<*>, kotlin.Nothing) -> kotlin.Unit", + fun List.(x: MutableSet<*>, y: Nothing) {}) + + check("(kotlin.IntArray, kotlin.ByteArray, kotlin.ShortArray, kotlin.CharArray, kotlin.LongArray, kotlin.BooleanArray, kotlin.FloatArray, kotlin.DoubleArray) -> kotlin.Array", + fun (ia: IntArray, ba: ByteArray, sa: ShortArray, ca: CharArray, la: LongArray, za: BooleanArray, fa: FloatArray, da: DoubleArray): Array = null!!) + + check("(kotlin.Array>>>) -> kotlin.Comparable", + fun (a: Array>>>): Comparable = null!!) return "OK" } diff --git a/compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt b/compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt new file mode 100644 index 00000000000..19eff61393d --- /dev/null +++ b/compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt @@ -0,0 +1,33 @@ +fun check(expected: String, obj: Any?) { + val actual = obj.toString() + if (actual != expected) + throw AssertionError("Expected: $expected, actual: $actual") +} + +fun box(): String { + check("Function0") + { -> } + check("Function0") + { -> 42 } + check("Function1", + fun (s: String) = 42.toLong()) + check("Function2") + { x: Int, y: Int -> } + + check("Function1", + fun Int.() {}) + check("Function1", + fun Unit.(): Int? = 42) + check("Function2", + fun String.(s: String?): Long = 42.toLong()) + check("Function3, java.util.Set, ?, kotlin.Unit>", + fun List.(x: MutableSet<*>, y: Nothing) {}) + + check("Function8", + fun (ia: IntArray, ba: ByteArray, sa: ShortArray, ca: CharArray, la: LongArray, za: BooleanArray, fa: FloatArray, da: DoubleArray): Array = null!!) + + check("Function1[][][], java.lang.Comparable>", + fun (a: Array>>>): Comparable = null!!) + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index a742d90e6ea..d80a2ba4dec 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -6175,6 +6175,12 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { doTest(fileName); } + @TestMetadata("functionNtoStringNoReflect.kt") + public void testFunctionNtoStringNoReflect() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/functionNtoStringNoReflect.kt"); + doTest(fileName); + } + @TestMetadata("infixRecursiveCall.kt") public void testInfixRecursiveCall() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/box/functions/infixRecursiveCall.kt"); diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java index cf4e08b70c1..a19674b502b 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionFactoryImpl.java @@ -18,6 +18,8 @@ package kotlin.reflect.jvm.internal; import kotlin.jvm.internal.*; import kotlin.reflect.*; +import kotlin.reflect.jvm.ReflectLambdaKt; +import org.jetbrains.kotlin.descriptors.FunctionDescriptor; /** * @suppress @@ -49,6 +51,18 @@ public class ReflectionFactoryImpl extends ReflectionFactory { return KClassCacheKt.getOrCreateKotlinClass(javaClass); } + @Override + public String renderLambdaToString(Lambda lambda) { + KFunction kFunction = ReflectLambdaKt.reflect(lambda); + if (kFunction != null) { + KFunctionImpl impl = UtilKt.asKFunctionImpl(kFunction); + if (impl != null) { + return ReflectionObjectRenderer.INSTANCE.renderLambda(impl.getDescriptor()); + } + } + return super.renderLambdaToString(lambda); + } + // Functions @Override diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt index 2bf45cc3a9e..bd10472f08a 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/ReflectionObjectRenderer.kt @@ -34,7 +34,7 @@ internal object ReflectionObjectRenderer { } } - private fun StringBuilder.appendReceiversAndName(callable: CallableDescriptor) { + private fun StringBuilder.appendReceivers(callable: CallableDescriptor) { val dispatchReceiver = callable.dispatchReceiverParameter val extensionReceiver = callable.extensionReceiverParameter @@ -44,8 +44,6 @@ internal object ReflectionObjectRenderer { if (addParentheses) append("(") appendReceiverType(extensionReceiver) if (addParentheses) append(")") - - append(renderer.renderName(callable.name)) } fun renderCallable(descriptor: CallableDescriptor): String { @@ -60,7 +58,8 @@ internal object ReflectionObjectRenderer { fun renderProperty(descriptor: PropertyDescriptor): String { return buildString { append(if (descriptor.isVar) "var " else "val ") - appendReceiversAndName(descriptor) + appendReceivers(descriptor) + append(renderer.renderName(descriptor.name)) append(": ") append(renderType(descriptor.type)) @@ -70,7 +69,8 @@ internal object ReflectionObjectRenderer { fun renderFunction(descriptor: FunctionDescriptor): String { return buildString { append("fun ") - appendReceiversAndName(descriptor) + appendReceivers(descriptor) + append(renderer.renderName(descriptor.name)) descriptor.valueParameters.joinTo(this, separator = ", ", prefix = "(", postfix = ")") { renderType(it.type) // TODO: vararg @@ -81,6 +81,19 @@ internal object ReflectionObjectRenderer { } } + fun renderLambda(invoke: FunctionDescriptor): String { + return buildString { + appendReceivers(invoke) + + invoke.valueParameters.joinTo(this, separator = ", ", prefix = "(", postfix = ")") { + renderType(it.type) + } + + append(" -> ") + append(renderType(invoke.returnType!!)) + } + } + fun renderParameter(parameter: KParameterImpl): String { return buildString { when (parameter.kind) { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/Lambda.kt b/core/runtime.jvm/src/kotlin/jvm/internal/Lambda.kt index 8ed80f05d0c..33cb84f966f 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/Lambda.kt +++ b/core/runtime.jvm/src/kotlin/jvm/internal/Lambda.kt @@ -19,6 +19,5 @@ package kotlin.jvm.internal abstract class Lambda(private val arity: Int) : FunctionImpl() { override fun getArity() = arity - @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") - override fun toString() = "${(this as Object).`class`.genericInterfaces[0]}" + override fun toString() = Reflection.renderLambdaToString(this) } diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java b/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java index fc37e966593..e8bd257e357 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/Reflection.java @@ -74,6 +74,10 @@ public class Reflection { return kClasses; } + public static String renderLambdaToString(Lambda lambda) { + return factory.renderLambdaToString(lambda); + } + // Functions public static KFunction function(FunctionReference f) { diff --git a/core/runtime.jvm/src/kotlin/jvm/internal/ReflectionFactory.java b/core/runtime.jvm/src/kotlin/jvm/internal/ReflectionFactory.java index 17770f25ddb..a98a6b72c27 100644 --- a/core/runtime.jvm/src/kotlin/jvm/internal/ReflectionFactory.java +++ b/core/runtime.jvm/src/kotlin/jvm/internal/ReflectionFactory.java @@ -19,6 +19,8 @@ package kotlin.jvm.internal; import kotlin.reflect.*; public class ReflectionFactory { + private static final String KOTLIN_JVM_FUNCTIONS = "kotlin.jvm.functions."; + public KClass createKotlinClass(Class javaClass) { return new ClassReference(javaClass); } @@ -39,6 +41,11 @@ public class ReflectionFactory { return new ClassReference(javaClass); } + public String renderLambdaToString(Lambda lambda) { + String result = lambda.getClass().getGenericInterfaces()[0].toString(); + return result.startsWith(KOTLIN_JVM_FUNCTIONS) ? result.substring(KOTLIN_JVM_FUNCTIONS.length()) : result; + } + // Functions public KFunction function(FunctionReference f) {