Reflection: add test on introspection of local classes

kotlin-reflect works correctly already for Kotlin-generated local
classes and anonymous objects, but not for Java ones. This is fixed in a
subsequent commit.

 #KT-41373 In Progress
This commit is contained in:
Alexander Udalov
2023-03-28 23:44:42 +02:00
committed by Space Team
parent 9fcdc10019
commit 1e031d9fb8
16 changed files with 345 additions and 0 deletions
@@ -0,0 +1,144 @@
// TARGET_BACKEND: JVM
// WITH_REFLECT
// FILE: anonymousObjects.kt
package test
import kotlin.reflect.*
import kotlin.reflect.jvm.isAccessible
import kotlin.test.*
interface I
fun check(x: KClass<*>, value: Any) {
assertEquals(null, x.qualifiedName)
assertEquals(
setOf(
"foo", "bar",
"equals", "hashCode", "toString"
),
x.members.mapTo(hashSetOf()) { it.name }
)
assertEquals(emptyList(), x.annotations)
assertEquals(emptyList(), x.nestedClasses)
assertEquals(null, x.objectInstance)
assertEquals(emptyList(), x.sealedSubclasses)
assertEquals(listOf(typeOf<I>(), typeOf<Any>()), x.supertypes)
// Local class visibility cannot be represented in Kotlin, so `KClass.visibility` is null.
assertEquals(null, x.visibility)
// It's not really important whether the class is considered final or open, but it shouldn't be both (or neither).
assertTrue(x.isFinal xor x.isOpen)
assertFalse(x.isAbstract)
assertFalse(x.isSealed)
assertFalse(x.isData)
assertFalse(x.isInner)
assertFalse(x.isCompanion)
assertFalse(x.isFun)
assertFalse(x.isValue)
assertFalse(x.isInstance(42))
assertTrue(x.isInstance(value))
val equals = x.members.single { it.name == "equals" } as KFunction<Boolean>
assertTrue(equals.call(value, value))
val foo = x.members.single { it.name == "foo" }.apply { isAccessible = true } as KFunction<String>
assertEquals("OK", foo.call(value))
val bar = x.members.single { it.name == "bar" }.apply { isAccessible = true } as KProperty1<Any, Int>
assertEquals(42, bar.get(value))
}
fun checkKotlinAnonymousObject() {
val anonymousObject = object : I {
val bar: Int = 42
fun foo(): String = "OK"
}
val klass = anonymousObject::class
// isAnonymousClass/simpleName behavior is different for Kotlin anonymous classes in JDK 1.8 and 9+, see KT-23072.
if (klass.java.isAnonymousClass) {
assertEquals(null, klass.simpleName)
} else {
assertEquals("anonymousObject\$1", klass.simpleName)
}
assertEquals(emptyList(), klass.constructors)
check(klass, anonymousObject)
}
fun checkKotlinLocalClass() {
class Local : I {
val bar: Int = 42
fun foo(): String = "OK"
}
val instance = Local()
val klass = instance::class
// simpleName behavior is different for Kotlin anonymous classes in JDK 1.8 and 9+, see KT-23072.
assertTrue(klass.simpleName!!.endsWith("Local"))
assertEquals(listOf("fun `<init>`(): test.`AnonymousObjectsKt\$checkKotlinLocalClass\$Local`"), klass.constructors.map { it.toString() })
check(klass, instance)
}
fun checkJavaAnonymousObject() {
val anonymousObject = JavaClass.anonymousObject()
val klass = anonymousObject::class
assertEquals(null, klass.simpleName)
assertEquals(listOf("fun `<init>`(): `JavaClass$1`"), klass.constructors.map { it.toString() })
check(klass, anonymousObject)
}
fun checkJavaLocalClass() {
val instance = JavaClass.localClassInstance()
val klass = instance::class
assertEquals("Local", klass.simpleName)
assertEquals(listOf("fun `<init>`(): Local"), klass.constructors.map { it.toString() })
check(klass, instance)
}
fun box(): String {
checkKotlinAnonymousObject()
checkKotlinLocalClass()
// TODO: fails with KotlinReflectionInternalError: Unresolved class: class JavaClass$1 (kind = null)
// checkJavaAnonymousObject()
// checkJavaLocalClass()
return "OK"
}
// FILE: JavaClass.java
public class JavaClass {
public static Object anonymousObject() {
return new test.I() {
int bar = 42;
String foo() { return "OK"; }
};
}
public static Object localClassInstance() {
class Local implements test.I {
int bar = 42;
String foo() { return "OK"; }
}
return new Local();
}
}