Support KClass.nestedClasses
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
public class J {
|
||||
public class Inner {}
|
||||
|
||||
public static class Nested {}
|
||||
|
||||
private static class PrivateNested {}
|
||||
|
||||
// This anonymous class should not appear in 'nestedClasses'
|
||||
private final Object o = new Object() {};
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
fun box(): String {
|
||||
assertEquals(listOf("Inner", "Nested", "PrivateNested"), J::class.nestedClasses.map { it.simpleName!! }.toSortedList())
|
||||
|
||||
return "OK"
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
// FULL_JDK
|
||||
|
||||
import kotlin.reflect.KClass
|
||||
import kotlin.reflect.jvm.*
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
class A {
|
||||
companion object {}
|
||||
inner class Inner
|
||||
class Nested
|
||||
private class PrivateNested
|
||||
}
|
||||
|
||||
fun nestedNames(c: KClass<*>) = c.nestedClasses.map { it.simpleName ?: throw AssertionError("Unnamed class: ${it.java}") }.toSortedList()
|
||||
|
||||
fun box(): String {
|
||||
// Kotlin class without nested classes
|
||||
assertEquals(emptyList<String>(), nestedNames(A.Inner::class))
|
||||
// Kotlin class with nested classes
|
||||
assertEquals(listOf("Companion", "Inner", "Nested", "PrivateNested"), nestedNames(A::class))
|
||||
|
||||
// Java class without nested classes
|
||||
assertEquals(emptyList<String>(), nestedNames(Error::class))
|
||||
// Java class with nested classes
|
||||
assertEquals(listOf("State", "UncaughtExceptionHandler"), nestedNames(Thread::class))
|
||||
|
||||
// Built-ins
|
||||
assertEquals(emptyList<String>(), nestedNames(Array<Any>::class))
|
||||
assertEquals(emptyList<String>(), nestedNames(CharSequence::class))
|
||||
assertEquals(listOf("Companion"), nestedNames(String::class))
|
||||
|
||||
assertEquals(emptyList<String>(), nestedNames(Collection::class))
|
||||
assertEquals(emptyList<String>(), nestedNames(MutableCollection::class))
|
||||
assertEquals(emptyList<String>(), nestedNames(List::class))
|
||||
assertEquals(emptyList<String>(), nestedNames(MutableList::class))
|
||||
assertEquals(listOf("Entry"), nestedNames(Map::class))
|
||||
assertEquals(emptyList<String>(), nestedNames(Map.Entry::class))
|
||||
assertEquals(emptyList<String>(), nestedNames(MutableMap.MutableEntry::class))
|
||||
|
||||
// TODO: should be MutableEntry. Currently we do not distinguish between Map and MutableMap.
|
||||
assertEquals(listOf("Entry"), nestedNames(MutableMap::class))
|
||||
|
||||
// Primitives
|
||||
for (primitive in listOf(Byte::class, Double::class, Float::class, Int::class, Long::class, Short::class, Char::class)) {
|
||||
assertEquals(listOf("Companion"), nestedNames(primitive))
|
||||
}
|
||||
assertEquals(emptyList<String>(), nestedNames(Boolean::class))
|
||||
|
||||
// Primitive arrays
|
||||
for (primitiveArray in listOf(
|
||||
ByteArray::class, DoubleArray::class, FloatArray::class, IntArray::class,
|
||||
LongArray::class, ShortArray::class, CharArray::class, BooleanArray::class
|
||||
)) {
|
||||
assertEquals(emptyList<String>(), nestedNames(primitiveArray))
|
||||
}
|
||||
|
||||
return "OK"
|
||||
}
|
||||
+6
@@ -339,6 +339,12 @@ public class BlackBoxWithJavaCodegenTestGenerated extends AbstractBlackBoxCodege
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClasses")
|
||||
public void testNestedClasses() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/nestedClasses/");
|
||||
doTestWithJava(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("noConflictOnKotlinGetterAndJavaField")
|
||||
public void testNoConflictOnKotlinGetterAndJavaField() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithJava/reflection/noConflictOnKotlinGetterAndJavaField/");
|
||||
|
||||
+6
@@ -3030,6 +3030,12 @@ public class BlackBoxWithStdlibCodegenTestGenerated extends AbstractBlackBoxCode
|
||||
doTestWithStdlib(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("nestedClasses.kt")
|
||||
public void testNestedClasses() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classes/nestedClasses.kt");
|
||||
doTestWithStdlib(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("objectInstance.kt")
|
||||
public void testObjectInstance() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/codegen/boxWithStdlib/reflection/classes/objectInstance.kt");
|
||||
|
||||
@@ -49,6 +49,11 @@ public interface KClass<T : Any> : KDeclarationContainer, KAnnotatedElement {
|
||||
*/
|
||||
public val constructors: Collection<KFunction<T>>
|
||||
|
||||
/**
|
||||
* All classes declared inside this class. This includes both inner and static nested classes.
|
||||
*/
|
||||
public val nestedClasses: Collection<KClass<*>>
|
||||
|
||||
/**
|
||||
* The instance of the object declaration, or `null` if this class is not an object declaration.
|
||||
*/
|
||||
|
||||
+1
-1
@@ -40,7 +40,7 @@ private val TYPES_ELIGIBLE_FOR_SIMPLE_VISIT = setOf(
|
||||
)
|
||||
|
||||
public class ReflectKotlinClass private constructor(
|
||||
private val klass: Class<*>,
|
||||
internal val klass: Class<*>,
|
||||
private val classHeader: KotlinClassHeader
|
||||
) : KotlinJvmBinaryClass {
|
||||
|
||||
|
||||
@@ -16,15 +16,20 @@
|
||||
|
||||
package kotlin.reflect.jvm.internal
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.java.reflect.tryLoadClass
|
||||
import org.jetbrains.kotlin.load.java.sources.JavaSourceElement
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.ReflectJavaClass
|
||||
import org.jetbrains.kotlin.load.java.structure.reflect.safeClassLoader
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
import org.jetbrains.kotlin.load.kotlin.reflect.ReflectKotlinClass
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.scopes.JetScope
|
||||
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
|
||||
import kotlin.reflect.KCallable
|
||||
@@ -111,6 +116,27 @@ class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclarationContainer
|
||||
KFunctionImpl(this, it) as KFunction<T>
|
||||
}
|
||||
|
||||
override val nestedClasses: Collection<KClass<*>>
|
||||
get() = descriptor.unsubstitutedInnerClassesScope.getAllDescriptors().map { nestedClass ->
|
||||
val source = (nestedClass as DeclarationDescriptorWithSource).source
|
||||
when (source) {
|
||||
is KotlinJvmBinarySourceElement ->
|
||||
(source.binaryClass as ReflectKotlinClass).klass
|
||||
is JavaSourceElement ->
|
||||
(source.javaElement as ReflectJavaClass).element
|
||||
SourceElement.NO_SOURCE -> {
|
||||
// If neither a Kotlin class nor a Java class, it must be a built-in
|
||||
val classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(DescriptorUtils.getFqName(nestedClass))
|
||||
?: throw KotlinReflectionInternalError("Class with no source must be a built-in: $nestedClass")
|
||||
val packageName = classId.packageFqName.asString()
|
||||
val className = classId.relativeClassName.asString().replace('.', '$')
|
||||
// All pseudo-classes like String.Companion must be accessible from the current class loader
|
||||
javaClass.safeClassLoader.tryLoadClass("$packageName.$className")
|
||||
}
|
||||
else -> throw KotlinReflectionInternalError("Unsupported class: $nestedClass (source = $source)")
|
||||
}
|
||||
}.filterNotNull().map { KClassImpl(it) }
|
||||
|
||||
@suppress("UNCHECKED_CAST")
|
||||
override val objectInstance: T? by ReflectProperties.lazy {
|
||||
val descriptor = descriptor
|
||||
|
||||
@@ -33,6 +33,9 @@ public class ClassReference(override val jClass: Class<*>) : KClass<Any>, ClassB
|
||||
override val constructors: Collection<KFunction<Any>>
|
||||
get() = error()
|
||||
|
||||
override val nestedClasses: Collection<KClass<*>>
|
||||
get() = error()
|
||||
|
||||
override val annotations: List<Annotation>
|
||||
get() = error()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user