diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/classToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/classToString.kt new file mode 100644 index 00000000000..6692de70e1f --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/classToString.kt @@ -0,0 +1,14 @@ +import kotlin.test.* +import kotlin.reflect.jvm.kotlin + +class A + +fun box(): String { + val p = javaClass().kotlin + if ("$p" != "class A") return "Fail: $p" + + val s = javaClass().kotlin + if ("$s" != "class java.lang.String") return "Fail: $s" + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/defaultPackageToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/defaultPackageToString.kt new file mode 100644 index 00000000000..cc711bc7d8d --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/defaultPackageToString.kt @@ -0,0 +1,8 @@ +import kotlin.test.* +import kotlin.reflect.jvm.kotlinPackage + +fun box(): String { + val p = Class.forName("_DefaultPackage").kotlinPackage + if ("$p" != "package ") return "Fail: $p" + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/extensionPropertyReceiverToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/extensionPropertyReceiverToString.kt new file mode 100644 index 00000000000..2fc67171bc3 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/extensionPropertyReceiverToString.kt @@ -0,0 +1,66 @@ +import kotlin.reflect.KExtensionProperty +import kotlin.test.assertEquals + +fun check(expected: String, p: KExtensionProperty<*, *>) { + var s = p.toString() + // Strip "val" or "var" + s = s.substring(4) + // Strip property name, leave only receiver class + s = s.substring(0, s.lastIndexOf('.')) + + assertEquals(expected, s) +} + +val Boolean.x: Any get() = this +val Char.x: Any get() = this +val Byte.x: Any get() = this +val Short.x: Any get() = this +val Int.x: Any get() = this +val Float.x: Any get() = this +val Long.x: Any get() = this +val Double.x: Any get() = this + +val BooleanArray.x: Any get() = this +val CharArray.x: Any get() = this +val ByteArray.x: Any get() = this +val ShortArray.x: Any get() = this +val IntArray.x: Any get() = this +val FloatArray.x: Any get() = this +val LongArray.x: Any get() = this +val DoubleArray.x: Any get() = this + +val Array.a1: Any get() = this +val Array.a2: Any get() = this +val Array>.a3: Any get() = this +val Array.a4: Any get() = this + +val Map.m: Any get() = this + +fun box(): String { + check("kotlin.Boolean", Boolean::x) + check("kotlin.Char", Char::x) + check("kotlin.Byte", Byte::x) + check("kotlin.Short", Short::x) + check("kotlin.Int", Int::x) + check("kotlin.Float", Float::x) + check("kotlin.Long", Long::x) + check("kotlin.Double", Double::x) + + check("kotlin.BooleanArray", BooleanArray::x) + check("kotlin.CharArray", CharArray::x) + check("kotlin.ByteArray", ByteArray::x) + check("kotlin.ShortArray", ShortArray::x) + check("kotlin.IntArray", IntArray::x) + check("kotlin.FloatArray", FloatArray::x) + check("kotlin.LongArray", LongArray::x) + check("kotlin.DoubleArray", DoubleArray::x) + + check("kotlin.Array", Array::a1) + check("kotlin.Array", Array::a2) + check("kotlin.Array>", Array>::a3) + check("kotlin.Array", Array::a4) + + check("java.util.Map", Map::m) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageForJavaStaticToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageForJavaStaticToString.kt new file mode 100644 index 00000000000..2d015d38f17 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageForJavaStaticToString.kt @@ -0,0 +1,8 @@ +import kotlin.test.* +import kotlin.reflect.jvm.kotlinPackage + +fun box(): String { + val p = javaClass().kotlinPackage + if ("$p" != "package java.lang.String") return "Fail: $p" + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageToString.kt new file mode 100644 index 00000000000..0f60b75b211 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageToString.kt @@ -0,0 +1,10 @@ +package test.foo.bar + +import kotlin.test.* +import kotlin.reflect.jvm.kotlinPackage + +fun box(): String { + val p = Class.forName("test.foo.bar.BarPackage").kotlinPackage + if ("$p" != "package test.foo.bar") return "Fail: $p" + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/propertyToString.kt b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/propertyToString.kt new file mode 100644 index 00000000000..2a1e96b7574 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/propertyToString.kt @@ -0,0 +1,26 @@ +package test + +import kotlin.test.assertEquals + +val top = 42 +var top2 = -23 + +val String.ext: Int get() = 0 +var IntRange?.ext2: Int get() = 0; set(value) {} + +class A(val mem: String) +class B(var mem: String) + +fun assertToString(s: String, x: Any) { + assertEquals(s, x.toString()) +} + +fun box(): String { + assertToString("val top", ::top) + assertToString("var top2", ::top2) + assertToString("val java.lang.String.ext", String::ext) + assertToString("var kotlin.IntRange.ext2", IntRange::ext2) + assertToString("val test.A.mem", A::mem) + assertToString("var test.B.mem", B::mem) + return "OK" +} diff --git a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index 252025ee4ed..411dd47a771 100644 --- a/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/jet/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -1536,11 +1536,41 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.TestsPackage", new File("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny"), Pattern.compile("^(.+)\\.kt$"), true); } + @TestMetadata("classToString.kt") + public void testClassToString() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/classToString.kt"); + } + + @TestMetadata("defaultPackageToString.kt") + public void testDefaultPackageToString() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/defaultPackageToString.kt"); + } + @TestMetadata("equalsHashCode.kt") public void testEqualsHashCode() throws Exception { doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/equalsHashCode.kt"); } + @TestMetadata("extensionPropertyReceiverToString.kt") + public void testExtensionPropertyReceiverToString() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/extensionPropertyReceiverToString.kt"); + } + + @TestMetadata("packageForJavaStaticToString.kt") + public void testPackageForJavaStaticToString() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageForJavaStaticToString.kt"); + } + + @TestMetadata("packageToString.kt") + public void testPackageToString() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/packageToString.kt"); + } + + @TestMetadata("propertyToString.kt") + public void testPropertyToString() throws Exception { + doTestWithStdlib("compiler/testData/codegen/boxWithStdlib/reflection/methodsFromAny/propertyToString.kt"); + } + } public static Test innerSuite() { diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 49f66afefc7..fa339c7c336 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -62,4 +62,7 @@ class KClassImpl(val jClass: Class) : KClass { override fun hashCode(): Int = jClass.hashCode() + + override fun toString(): String = + jClass.toString() } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KForeignMemberProperty.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KForeignMemberProperty.kt index c0aab6889b1..ef62750475c 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KForeignMemberProperty.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KForeignMemberProperty.kt @@ -36,6 +36,10 @@ open class KForeignMemberProperty( override fun hashCode(): Int = name.hashCode() * 31 + owner.hashCode() + + // TODO: include visibility, return type + override fun toString(): String = + "val ${owner.jClass.getName()}.$name" } class KMutableForeignMemberProperty( @@ -47,4 +51,7 @@ class KMutableForeignMemberProperty( override fun set(receiver: T, value: R) { field.set(receiver, value) } + + override fun toString(): String = + "var ${owner.jClass.getName()}.$name" } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt index 8135c7dfea2..b6196ed9a50 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KMemberPropertyImpl.kt @@ -45,6 +45,10 @@ open class KMemberPropertyImpl( override fun hashCode(): Int = name.hashCode() * 31 + owner.hashCode() + + // TODO: include visibility, return type + override fun toString(): String = + "val ${owner.jClass.getName()}.$name" } class KMutableMemberPropertyImpl( @@ -56,4 +60,7 @@ class KMutableMemberPropertyImpl( override fun set(receiver: T, value: R) { setter(receiver, value) } + + override fun toString(): String = + "var ${owner.jClass.getName()}.$name" } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt index a2bab0d6737..4a5e182b304 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KPackageImpl.kt @@ -17,6 +17,9 @@ package kotlin.reflect.jvm.internal import kotlin.reflect.KPackage +import kotlin.jvm.internal.KotlinPackage + +private val KOTLIN_PACKAGE_ANNOTATION_CLASS = javaClass() class KPackageImpl(val jClass: Class<*>) : KPackage { override fun equals(other: Any?): Boolean = @@ -24,4 +27,23 @@ class KPackageImpl(val jClass: Class<*>) : KPackage { override fun hashCode(): Int = jClass.hashCode() + + suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") + override fun toString(): String { + val name = jClass.getName() as java.lang.String + return if (jClass.isAnnotationPresent(KOTLIN_PACKAGE_ANNOTATION_CLASS)) { + // Cast to Any is needed to suppress the error: "Operator '==' cannot be applied to 'java.lang.String' and 'kotlin.String'" + if ((name : Any) == "_DefaultPackage") { + "package " + } + else { + val lastDot = name.lastIndexOf(".") + if (lastDot >= 0) { + "package ${name.substring(0, lastDot)}" + } + else "package $name" + } + } + else "package $name" + } } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelExtensionPropertyImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelExtensionPropertyImpl.kt index 057a8cb5eba..d200c100f2e 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelExtensionPropertyImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelExtensionPropertyImpl.kt @@ -38,6 +38,10 @@ open class KTopLevelExtensionPropertyImpl( override fun hashCode(): Int = (name.hashCode() * 31 + owner.hashCode()) * 31 + receiverClass.hashCode() + + // TODO: include visibility, return type, maybe package + override fun toString(): String = + "val ${mapJavaClassToKotlin(receiverClass.getName())}.$name" } class KMutableTopLevelExtensionPropertyImpl( @@ -50,4 +54,40 @@ class KMutableTopLevelExtensionPropertyImpl( override fun set(receiver: T, value: R) { setter.invoke(null, receiver, value) } + + override fun toString(): String = + "var ${mapJavaClassToKotlin(receiverClass.getName())}.$name" +} + +suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") +private fun mapJavaClassToKotlin(name: String): String { + if (Character.isLowerCase(name[0])) { + return when (name) { + "boolean" -> "kotlin.Boolean" + "char" -> "kotlin.Char" + "byte" -> "kotlin.Byte" + "short" -> "kotlin.Short" + "int" -> "kotlin.Int" + "float" -> "kotlin.Float" + "long" -> "kotlin.Long" + "double" -> "kotlin.Double" + else -> name + } + } + if (name[0] == '[') { + val element = (name as java.lang.String).substring(1) + return when (element[0]) { + 'Z' -> "kotlin.BooleanArray" + 'C' -> "kotlin.CharArray" + 'B' -> "kotlin.ByteArray" + 'S' -> "kotlin.ShortArray" + 'I' -> "kotlin.IntArray" + 'F' -> "kotlin.FloatArray" + 'J' -> "kotlin.LongArray" + 'D' -> "kotlin.DoubleArray" + 'L' -> "kotlin.Array<${mapJavaClassToKotlin((element as java.lang.String).substring(1, element.length() - 1))}>" + else -> "kotlin.Array<${mapJavaClassToKotlin(element)}>" + } + } + return name } diff --git a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelVariableImpl.kt b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelVariableImpl.kt index 72dd86fa406..d88bb73491e 100644 --- a/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelVariableImpl.kt +++ b/core/runtime.jvm/src/kotlin/reflect/jvm/internal/KTopLevelVariableImpl.kt @@ -38,6 +38,10 @@ open class KTopLevelVariableImpl( override fun hashCode(): Int = name.hashCode() * 31 + owner.hashCode() + + // TODO: include visibility, return type, maybe package + override fun toString(): String = + "val $name" } class KMutableTopLevelVariableImpl( @@ -49,4 +53,7 @@ class KMutableTopLevelVariableImpl( override fun set(value: R) { setter.invoke(null, value) } + + override fun toString(): String = + "var $name" }