Improve toString() for lambdas and function expressions
#KT-9952 Fixed
This commit is contained in:
+19
-12
@@ -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<kotlin.Unit>")
|
||||
check("() -> kotlin.Unit")
|
||||
{ -> }
|
||||
check("kotlin.jvm.functions.Function0<java.lang.Integer>")
|
||||
check("() -> kotlin.Int")
|
||||
{ -> 42 }
|
||||
check("kotlin.jvm.functions.Function1<java.lang.String, java.lang.Long>",
|
||||
check("(kotlin.String) -> kotlin.Long",
|
||||
fun (s: String) = 42.toLong())
|
||||
check("kotlin.jvm.functions.Function2<java.lang.Integer, java.lang.Integer, kotlin.Unit>")
|
||||
check("(kotlin.Int, kotlin.Int) -> kotlin.Unit")
|
||||
{ x: Int, y: Int -> }
|
||||
|
||||
check("kotlin.jvm.functions.Function1<java.lang.Integer, kotlin.Unit>",
|
||||
check("kotlin.Int.() -> kotlin.Unit",
|
||||
fun Int.() {})
|
||||
check("kotlin.jvm.functions.Function1<kotlin.Unit, java.lang.Integer>",
|
||||
fun Unit.(): Int = 42)
|
||||
check("kotlin.jvm.functions.Function2<java.lang.String, java.lang.String, java.lang.Long>",
|
||||
fun String.(s: String): Long = 42.toLong())
|
||||
check("kotlin.jvm.functions.Function3<java.lang.Integer, java.lang.Integer, java.lang.Integer, kotlin.Unit>",
|
||||
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.String>.(kotlin.collections.MutableSet<*>, kotlin.Nothing) -> kotlin.Unit",
|
||||
fun List<String>.(x: MutableSet<*>, y: Nothing) {})
|
||||
|
||||
check("(kotlin.IntArray, kotlin.ByteArray, kotlin.ShortArray, kotlin.CharArray, kotlin.LongArray, kotlin.BooleanArray, kotlin.FloatArray, kotlin.DoubleArray) -> kotlin.Array<kotlin.Int>",
|
||||
fun (ia: IntArray, ba: ByteArray, sa: ShortArray, ca: CharArray, la: LongArray, za: BooleanArray, fa: FloatArray, da: DoubleArray): Array<Int> = null!!)
|
||||
|
||||
check("(kotlin.Array<kotlin.Array<kotlin.Array<kotlin.collections.List<kotlin.String>>>>) -> kotlin.Comparable<kotlin.String>",
|
||||
fun (a: Array<Array<Array<List<String>>>>): Comparable<String> = null!!)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
|
||||
@@ -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<kotlin.Unit>")
|
||||
{ -> }
|
||||
check("Function0<java.lang.Integer>")
|
||||
{ -> 42 }
|
||||
check("Function1<java.lang.String, java.lang.Long>",
|
||||
fun (s: String) = 42.toLong())
|
||||
check("Function2<java.lang.Integer, java.lang.Integer, kotlin.Unit>")
|
||||
{ x: Int, y: Int -> }
|
||||
|
||||
check("Function1<java.lang.Integer, kotlin.Unit>",
|
||||
fun Int.() {})
|
||||
check("Function1<kotlin.Unit, java.lang.Integer>",
|
||||
fun Unit.(): Int? = 42)
|
||||
check("Function2<java.lang.String, java.lang.String, java.lang.Long>",
|
||||
fun String.(s: String?): Long = 42.toLong())
|
||||
check("Function3<java.util.List<? extends java.lang.String>, java.util.Set<?>, ?, kotlin.Unit>",
|
||||
fun List<String>.(x: MutableSet<*>, y: Nothing) {})
|
||||
|
||||
check("Function8<int[], byte[], short[], char[], long[], boolean[], float[], double[], java.lang.Integer[]>",
|
||||
fun (ia: IntArray, ba: ByteArray, sa: ShortArray, ca: CharArray, la: LongArray, za: BooleanArray, fa: FloatArray, da: DoubleArray): Array<Int> = null!!)
|
||||
|
||||
check("Function1<java.util.List<? extends java.lang.String>[][][], java.lang.Comparable<? super java.lang.String>>",
|
||||
fun (a: Array<Array<Array<List<String>>>>): Comparable<String> = null!!)
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user