From a6be6f4986c32b486043e4ad90602bd070e67cd1 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Wed, 3 Jul 2019 13:35:25 +0200 Subject: [PATCH] Use Class.forName instead of ClassLoader.loadClass in reflection This fixes an issue in constructing annotation instances with array class elements. For some reason, behavior of `ClassLoader.loadClass` differs from `Class.forName` in handling arrays, namely: * `loadClass("[Ltest.Foo;")` returns null * `Class.forName("[Ltest.Foo;")` returns class for array of test.Foo Overall, there doesn't seem to be any way to load an array class with `CLassLoader.loadClass`. We pass initialize=false to forName because this is the behavior of ClassLoader.loadClass: it doesn't perform class initialization (e.g. is not executed). #KT-31318 Fixed --- .../reflection/annotations/onTypes/arrayKClass.kt | 14 ++++++++++++++ .../annotations/onTypes/differentArgumentTypes.kt | 8 ++++---- .../codegen/BlackBoxCodegenTestGenerated.java | 5 +++++ .../codegen/LightAnalysisModeTestGenerated.java | 5 +++++ .../codegen/ir/IrBlackBoxCodegenTestGenerated.java | 5 +++++ .../internal/components/ReflectJavaClassFinder.kt | 2 +- .../src/kotlin/reflect/jvm/internal/util.kt | 4 ++-- 7 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt diff --git a/compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt b/compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt new file mode 100644 index 00000000000..1c5db7f1fe2 --- /dev/null +++ b/compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt @@ -0,0 +1,14 @@ +// TARGET_BACKEND: JVM +// WITH_REFLECT + +import kotlin.reflect.KClass + +@Target(AnnotationTarget.TYPE) +annotation class MyAnn(val cls: KClass<*>) + +val s: @MyAnn(Array::class) String = "" + +fun box(): String { + val ann = ::s.returnType.annotations[0] as MyAnn + return if (ann.cls == Array::class) "OK" else "Fail: ${ann.cls}" +} diff --git a/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt b/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt index f7943f5b223..971cafaa23f 100644 --- a/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt +++ b/compiler/testData/codegen/box/reflection/annotations/onTypes/differentArgumentTypes.kt @@ -58,7 +58,7 @@ fun f(): @Anno( AnnotationTarget.EXPRESSION, Nested("1"), ["lmao"], - [Double::class, Unit::class, LongArray::class], + [Double::class, Unit::class, LongArray::class, Array::class], [AnnotationTarget.TYPEALIAS, AnnotationTarget.FIELD], [Nested("2"), Nested("3")] ) Unit {} @@ -67,9 +67,9 @@ fun box(): String { assertEquals( "[@Anno(b=1, c=x, d=3.14, f=-2.72, i=42424242, j=239239239239239, s=42, z=true, " + "ba=[-1], ca=[y], da=[-3.14159], fa=[2.7218], ia=[424242], ja=[239239239239], sa=[-43], za=[false, true], " + - "str=lol, k=class java.lang.Number, k2=class [I, e=EXPRESSION, a=@Nested(value=1), " + - "stra=[lmao], ka=[class java.lang.Double, class kotlin.Unit, class [J], ea=[TYPEALIAS, FIELD], " + - "aa=[@Nested(value=2), @Nested(value=3)])]", + "str=lol, k=class java.lang.Number, k2=class [I, e=EXPRESSION, a=@Nested(value=1), stra=[lmao], " + + "ka=[class java.lang.Double, class kotlin.Unit, class [J, class [Ljava.lang.String;], " + + "ea=[TYPEALIAS, FIELD], aa=[@Nested(value=2), @Nested(value=3)])]", ::f.returnType.annotations.toString() ) diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java index 95d37cd0628..d54ee63bf3f 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/BlackBoxCodegenTestGenerated.java @@ -20083,6 +20083,11 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/annotations/onTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("arrayKClass.kt") + public void testArrayKClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt"); + } + @TestMetadata("classLiteralWithExpectedType.kt") public void testClassLiteralWithExpectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/classLiteralWithExpectedType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java index c93fc1e189f..92c84967fa5 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/LightAnalysisModeTestGenerated.java @@ -20083,6 +20083,11 @@ public class LightAnalysisModeTestGenerated extends AbstractLightAnalysisModeTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/annotations/onTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM, true); } + @TestMetadata("arrayKClass.kt") + public void testArrayKClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt"); + } + @TestMetadata("classLiteralWithExpectedType.kt") public void testClassLiteralWithExpectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/classLiteralWithExpectedType.kt"); diff --git a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java index d82f8e4d8a6..98ea084d2f2 100644 --- a/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/codegen/ir/IrBlackBoxCodegenTestGenerated.java @@ -18973,6 +18973,11 @@ public class IrBlackBoxCodegenTestGenerated extends AbstractIrBlackBoxCodegenTes KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("compiler/testData/codegen/box/reflection/annotations/onTypes"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.JVM_IR, true); } + @TestMetadata("arrayKClass.kt") + public void testArrayKClass() throws Exception { + runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/arrayKClass.kt"); + } + @TestMetadata("classLiteralWithExpectedType.kt") public void testClassLiteralWithExpectedType() throws Exception { runTest("compiler/testData/codegen/box/reflection/annotations/onTypes/classLiteralWithExpectedType.kt"); diff --git a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt index 28a5ab6ac33..92f98c34478 100644 --- a/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt +++ b/core/descriptors.runtime/src/kotlin/reflect/jvm/internal/components/ReflectJavaClassFinder.kt @@ -46,7 +46,7 @@ class ReflectJavaClassFinder(private val classLoader: ClassLoader) : JavaClassFi fun ClassLoader.tryLoadClass(fqName: String) = try { - loadClass(fqName) + Class.forName(fqName, false, this) } catch (e: ClassNotFoundException) { null } diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt index d6e13e9cbdd..12c599c52c8 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/util.kt @@ -90,8 +90,8 @@ private fun loadClass(classLoader: ClassLoader, packageName: String, className: } var fqName = "$packageName.${className.replace('.', '$')}" - repeat(arrayDimensions) { - fqName = "[$fqName" + if (arrayDimensions > 0) { + fqName = "[".repeat(arrayDimensions) + "L$fqName;" } return classLoader.tryLoadClass(fqName)