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.
<clinit> is not executed).

 #KT-31318 Fixed
This commit is contained in:
Alexander Udalov
2019-07-03 13:35:25 +02:00
parent 72d74ff888
commit a6be6f4986
7 changed files with 36 additions and 7 deletions
@@ -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<String>::class) String = ""
fun box(): String {
val ann = ::s.returnType.annotations[0] as MyAnn
return if (ann.cls == Array<String>::class) "OK" else "Fail: ${ann.cls}"
}
@@ -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<String>::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()
)
@@ -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");
@@ -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");
@@ -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");
@@ -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
}
@@ -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)