Improve toString() for lambdas and function expressions

#KT-9952 Fixed
This commit is contained in:
Alexander Udalov
2016-02-15 18:04:40 +03:00
parent bab127ad33
commit eef3631b8a
8 changed files with 102 additions and 19 deletions
+19 -12
View File
@@ -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) {