Support KClass.nestedClasses

This commit is contained in:
Alexander Udalov
2015-08-04 22:50:55 +03:00
parent e8dc9ede31
commit 94d45f35d4
9 changed files with 126 additions and 5 deletions
@@ -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"
}
@@ -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/");
@@ -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.
*/
@@ -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()