diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/java.kt b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/java.kt new file mode 100644 index 00000000000..be5fdc6d292 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/java.kt @@ -0,0 +1,58 @@ +import kotlin.reflect.KClass + +fun checkPrimitive(clazz: Class<*>, expected: String) { + assert (clazz!!.canonicalName == expected) { + "clazz name: ${clazz.canonicalName}" + } +} + +fun checkPrimitive(kClass: KClass<*>, expected: String) { + checkPrimitive(kClass.java, expected) +} + +fun checkObject(clazz: Class<*>, expected: String) { + assert (clazz.canonicalName == "$expected") { + "clazz should be object, but found: ${clazz!!.canonicalName}" + } +} + +fun checkObject(kClass: KClass<*>, expected: String) { + checkObject(kClass.java, expected) +} + +fun box(): String { + checkPrimitive(Boolean::class.java, "boolean") + checkPrimitive(Boolean::class, "boolean") + + checkPrimitive(Char::class.java, "char") + checkPrimitive(Char::class, "char") + + checkPrimitive(Byte::class.java, "byte") + checkPrimitive(Byte::class, "byte") + + checkPrimitive(Short::class.java, "short") + checkPrimitive(Short::class, "short") + + checkPrimitive(Int::class.java, "int") + checkPrimitive(Int::class, "int") + + checkPrimitive(Float::class.java, "float") + checkPrimitive(Float::class, "float") + + checkPrimitive(Long::class.java, "long") + checkPrimitive(Long::class, "long") + + checkPrimitive(Double::class.java, "double") + checkPrimitive(Double::class, "double") + + checkObject(String::class.java, "java.lang.String") + checkObject(String::class, "java.lang.String") + + checkObject(Nothing::class.java, "java.lang.Void") + checkObject(Nothing::class, "java.lang.Void") + + checkObject(java.lang.Void::class.java, "java.lang.Void") + checkObject(java.lang.Void::class, "java.lang.Void") + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectType.kt b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectType.kt new file mode 100644 index 00000000000..21ddc05e859 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectType.kt @@ -0,0 +1,48 @@ +import kotlin.reflect.KClass + +fun check(clazz: Class<*>?, expected: String) { + assert (clazz!!.canonicalName == expected) { + "clazz name: ${clazz.canonicalName}" + } +} + +fun check(kClass: KClass<*>, expected: String) { + check(kClass.javaObjectType, expected) +} + +fun box(): String { + check(Boolean::class.javaObjectType, "java.lang.Boolean") + check(Boolean::class, "java.lang.Boolean") + + check(Char::class.javaObjectType, "java.lang.Character") + check(Char::class, "java.lang.Character") + + check(Byte::class.javaObjectType, "java.lang.Byte") + check(Byte::class, "java.lang.Byte") + + check(Short::class.javaObjectType, "java.lang.Short") + check(Short::class, "java.lang.Short") + + check(Int::class.javaObjectType, "java.lang.Integer") + check(Int::class, "java.lang.Integer") + + check(Float::class.javaObjectType, "java.lang.Float") + check(Float::class, "java.lang.Float") + + check(Long::class.javaObjectType, "java.lang.Long") + check(Long::class, "java.lang.Long") + + check(Double::class.javaObjectType, "java.lang.Double") + check(Double::class, "java.lang.Double") + + check(String::class.javaObjectType, "java.lang.String") + check(String::class, "java.lang.String") + + check(Nothing::class.javaObjectType, "java.lang.Void") + check(Nothing::class, "java.lang.Void") + + check(java.lang.Void::class.javaObjectType, "java.lang.Void") + check(java.lang.Void::class, "java.lang.Void") + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectTypeReified.kt b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectTypeReified.kt new file mode 100644 index 00000000000..b1fca4906fd --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectTypeReified.kt @@ -0,0 +1,24 @@ +import kotlin.reflect.KClass + +inline fun check(expected: String) { + val clazz = T::class.javaObjectType!! + assert (clazz.canonicalName == "java.lang.${expected.capitalize()}") { + "clazz name: ${clazz.canonicalName}" + } +} + +fun box(): String { + check("boolean") + check("character") + check("byte") + check("short") + check("integer") + check("float") + check("long") + check("double") + + check("String") + check("Void") + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveType.kt b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveType.kt new file mode 100644 index 00000000000..b4a68e8ad4d --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveType.kt @@ -0,0 +1,58 @@ +import kotlin.reflect.KClass + +fun check(clazz: Class<*>?, expected: String) { + assert (clazz!!.canonicalName == expected) { + "clazz name: ${clazz.canonicalName}" + } +} + +fun check(kClass: KClass<*>, expected: String) { + check(kClass.javaPrimitiveType, expected) +} + +fun checkNull(clazz: Class<*>?) { + assert (clazz == null) { + "clazz should be null: ${clazz!!.canonicalName}" + } +} + +fun checkNull(kClass: KClass<*>) { + checkNull(kClass.javaPrimitiveType) +} + +fun box(): String { + check(Boolean::class.javaPrimitiveType, "boolean") + check(Boolean::class, "boolean") + + check(Char::class.javaPrimitiveType, "char") + check(Char::class, "char") + + check(Byte::class.javaPrimitiveType, "byte") + check(Byte::class, "byte") + + check(Short::class.javaPrimitiveType, "short") + check(Short::class, "short") + + check(Int::class.javaPrimitiveType, "int") + check(Int::class, "int") + + check(Float::class.javaPrimitiveType, "float") + check(Float::class, "float") + + check(Long::class.javaPrimitiveType, "long") + check(Long::class, "long") + + check(Double::class.javaPrimitiveType, "double") + check(Double::class, "double") + + checkNull(String::class.javaPrimitiveType) + checkNull(String::class) + + checkNull(Nothing::class.javaPrimitiveType) + checkNull(Nothing::class) + + checkNull(java.lang.Void::class.javaPrimitiveType) + checkNull(java.lang.Void::class) + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveTypeReified.kt b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveTypeReified.kt new file mode 100644 index 00000000000..0eb61184d65 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveTypeReified.kt @@ -0,0 +1,31 @@ +import kotlin.reflect.KClass + +inline fun check(expected: String) { + val clazz = T::class.javaPrimitiveType!! + assert (clazz.canonicalName == expected) { + "clazz name: ${clazz.canonicalName}" + } +} + +inline fun checkNull() { + val clazz = T::class.javaPrimitiveType + assert (clazz == null) { + "clazz should be null: ${clazz!!.canonicalName}" + } +} + +fun box(): String { + check("boolean") + check("char") + check("byte") + check("short") + check("int") + check("float") + check("long") + check("double") + + checkNull() + checkNull() + + return "OK" +} diff --git a/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaReified.kt b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaReified.kt new file mode 100644 index 00000000000..a681d32fdc9 --- /dev/null +++ b/compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaReified.kt @@ -0,0 +1,24 @@ +import kotlin.reflect.KClass + +inline fun check(expected: String) { + val clazz = T::class.java!! + assert (clazz.canonicalName == "java.lang.${expected.capitalize()}") { + "clazz name: ${clazz.canonicalName}" + } +} + +fun box(): String { + check("boolean") + check("character") + check("byte") + check("short") + check("integer") + check("float") + check("long") + check("double") + + check("String") + check("Void") + + return "OK" +} diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java index e7778e55637..57ddf3a806e 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/generated/BlackBoxWithStdlibCodegenTestGenerated.java @@ -3727,6 +3727,51 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode } } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class JavaProperties extends AbstractBlackBoxCodegenTest { + public void testAllFilesPresentInJavaProperties() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties"), Pattern.compile("^(.+)\\.kt$"), true); + } + + @TestMetadata("java.kt") + public void testJava() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/java.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("javaObjectType.kt") + public void testJavaObjectType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectType.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("javaObjectTypeReified.kt") + public void testJavaObjectTypeReified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaObjectTypeReified.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("javaPrimitiveType.kt") + public void testJavaPrimitiveType() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveType.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("javaPrimitiveTypeReified.kt") + public void testJavaPrimitiveTypeReified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaPrimitiveTypeReified.kt"); + doTestWithStdlib(fileName); + } + + @TestMetadata("javaReified.kt") + public void testJavaReified() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/javaProperties/javaReified.kt"); + doTestWithStdlib(fileName); + } + } + @TestMetadata("compiler/testData/codegen/boxWithStdlib/reflection/kClassInAnnotation") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class) diff --git a/core/runtime.jvm/src/kotlin/jvm/JvmClassMapping.kt b/core/runtime.jvm/src/kotlin/jvm/JvmClassMapping.kt index 89828ecabf4..a9b33d44728 100644 --- a/core/runtime.jvm/src/kotlin/jvm/JvmClassMapping.kt +++ b/core/runtime.jvm/src/kotlin/jvm/JvmClassMapping.kt @@ -32,6 +32,51 @@ public val KClass.java: Class @JvmName("getJavaClass") get() = (this as ClassBasedDeclarationContainer).jClass as Class +/** + * Returns a Java [Class] instance representing the primitive type corresponding to the given [KClass] if it exists. + */ +@Suppress("UNCHECKED_CAST") +public val KClass.javaPrimitiveType: Class? + get() { + val thisJClass = (this as ClassBasedDeclarationContainer).jClass + if (thisJClass.isPrimitive) return thisJClass as Class + + return when (thisJClass.canonicalName) { + "java.lang.Boolean" -> Boolean::class.java + "java.lang.Character" -> Char::class.java + "java.lang.Byte" -> Byte::class.java + "java.lang.Short" -> Short::class.java + "java.lang.Integer" -> Int::class.java + "java.lang.Float" -> Float::class.java + "java.lang.Long" -> Long::class.java + "java.lang.Double" -> Double::class.java + else -> null + } as Class? + } + +/** + * Returns a Java [Class] instance corresponding to the given [KClass] instance. + * In case of primitive types it returns corresponding wrapper classes. + */ +@Suppress("UNCHECKED_CAST", "PLATFORM_CLASS_MAPPED_TO_KOTLIN") +public val KClass.javaObjectType: Class + get() { + val thisJClass = (this as ClassBasedDeclarationContainer).jClass + if (!thisJClass.isPrimitive) return thisJClass as Class + + return when (thisJClass.canonicalName) { + "boolean" -> java.lang.Boolean::class.java + "char" -> java.lang.Character::class.java + "byte" -> java.lang.Byte::class.java + "short" -> java.lang.Short::class.java + "int" -> java.lang.Integer::class.java + "float" -> java.lang.Float::class.java + "long" -> java.lang.Long::class.java + "double" -> java.lang.Double::class.java + else -> thisJClass + } as Class + } + /** * Returns a [KClass] instance corresponding to the given Java [Class] instance. */