Reflection: improve support of built-in classes in 'toJavaClass'

Also support mock class descriptor created for java.io.Serializable. This will
be needed (and covered with tests) later for correct implementation of advanced
reflection API
This commit is contained in:
Alexander Udalov
2016-07-13 18:34:32 +03:00
parent 0c98231f53
commit 5e3efe0e58
2 changed files with 34 additions and 15 deletions
@@ -20,13 +20,10 @@ 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.structure.reflect.safeClassLoader
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
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.MemberScope
import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies
@@ -120,16 +117,9 @@ internal class KClassImpl<T : Any>(override val jClass: Class<T>) : KDeclaration
override val nestedClasses: Collection<KClass<*>>
get() = descriptor.unsubstitutedInnerClassesScope.getContributedDescriptors().filterNot(DescriptorUtils::isEnumEntry).mapNotNull {
nestedClass ->
(nestedClass as ClassDescriptor).toJavaClass() ?: run {
// 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
(this as Any).javaClass.safeClassLoader.tryLoadClass("$packageName.$className")
}
}.map { KClassImpl(it) }
val jClass = (nestedClass as ClassDescriptor).toJavaClass()
jClass?.let { KClassImpl(it) }
}
@Suppress("UNCHECKED_CAST")
private val objectInstance_ = ReflectProperties.lazy {
@@ -18,10 +18,15 @@ package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.load.java.components.RuntimeSourceElementFactory
import org.jetbrains.kotlin.load.java.reflect.tryLoadClass
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.FqName
import org.jetbrains.kotlin.platform.JavaToKotlinClassMap
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
import kotlin.jvm.internal.FunctionReference
import kotlin.jvm.internal.PropertyReference
import kotlin.reflect.IllegalCallableAccessException
@@ -38,12 +43,36 @@ internal fun ClassDescriptor.toJavaClass(): Class<*>? {
(source.javaElement as ReflectJavaClass).element
}
else -> {
// This is a built-in class
null
// If this is neither a Kotlin class nor a Java class, it is either a built-in or some fake class descriptor like the one
// that's created for java.io.Serializable in JvmBuiltInsSettings
val classId = JavaToKotlinClassMap.INSTANCE.mapKotlinToJava(DescriptorUtils.getFqName(this)) ?: classId
val packageName = classId.packageFqName.asString()
val className = classId.relativeClassName.asString()
// All pseudo-classes like kotlin.String.Companion must be accessible from the current class loader
loadClass(javaClass.safeClassLoader, packageName, className)
}
}
}
internal fun loadClass(classLoader: ClassLoader, packageName: String, className: String): Class<*>? {
if (packageName == "kotlin") {
// See mapBuiltInType() in typeSignatureMapping.kt
when (className) {
"Array" -> return Array<Any>::class.java
"BooleanArray" -> return BooleanArray::class.java
"ByteArray" -> return ByteArray::class.java
"CharArray" -> return CharArray::class.java
"DoubleArray" -> return DoubleArray::class.java
"FloatArray" -> return FloatArray::class.java
"IntArray" -> return IntArray::class.java
"LongArray" -> return LongArray::class.java
"ShortArray" -> return ShortArray::class.java
}
}
return classLoader.tryLoadClass("$packageName.${className.replace('.', '$')}")
}
// TODO: wrap other exceptions
internal inline fun <R> reflectionCall(block: () -> R): R =
try {