Reflection: create synthetic classes instead of throwing UOE
... for Kotlin-generated classes which do not correspond to a "class" from the Kotlin language's point of view. For example, Kotlin lambdas, file facade classes, multifile class facade/part classes, WhenMappings, DefaultImpls. They can be distinguished from normal classes by the value of `KotlinClassHeader.Kind` (which is the same as `Metadata.kind`). Another theoretical option would be to throw exception at the point where the `::class` expression is used, if the expression's type on the left-hand side is a synthetic class. But we can't really do that since it'll affect performance of most `<expression>::class` expressions. So, construct a fake synthetic class instead, without any members except equals/hashCode/toString, and without any non-trivial modifiers. It kind of contradicts the general idea that kotlin-reflect presents anything exactly the same as the compiler sees it, but arguably it's worth it to avoid unexpected exceptions like in KT-41373. In the newly added test, Java lambda check is muted but it should work exactly the same as for Kotlin lambdas and other synthetic classes. It's fixed in a subsequent commit. #KT-41373 In Progress
This commit is contained in:
committed by
Space Team
parent
d833b732c9
commit
5dc882abf5
@@ -20,7 +20,10 @@ import org.jetbrains.kotlin.builtins.CompanionObjectMapping
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.builtins.isMappedIntrinsicCompanionObject
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.runtime.components.ReflectKotlinClass
|
||||
import org.jetbrains.kotlin.descriptors.runtime.components.RuntimeModuleData
|
||||
import org.jetbrains.kotlin.descriptors.runtime.structure.functionClassArity
|
||||
import org.jetbrains.kotlin.descriptors.runtime.structure.wrapperByPrimitive
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
@@ -32,6 +35,7 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.scopes.GivenFunctionsMemberScope
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
@@ -49,6 +53,7 @@ internal class KClassImpl<T : Any>(
|
||||
val descriptor: ClassDescriptor by ReflectProperties.lazySoft {
|
||||
val classId = classId
|
||||
val moduleData = data.value.moduleData
|
||||
val module = moduleData.module
|
||||
|
||||
val descriptor =
|
||||
if (classId.isLocal && jClass.isAnnotationPresent(Metadata::class.java)) {
|
||||
@@ -56,10 +61,10 @@ internal class KClassImpl<T : Any>(
|
||||
// `module.findClassAcrossModuleDependencies`.
|
||||
moduleData.deserialization.deserializeClass(classId)
|
||||
} else {
|
||||
moduleData.module.findClassAcrossModuleDependencies(classId)
|
||||
module.findClassAcrossModuleDependencies(classId)
|
||||
}
|
||||
|
||||
descriptor ?: reportUnresolvedClass()
|
||||
descriptor ?: createSyntheticClassOrFail(classId, moduleData)
|
||||
}
|
||||
|
||||
val annotations: List<Annotation> by ReflectProperties.lazySoft { descriptor.computeAnnotations() }
|
||||
@@ -310,29 +315,38 @@ internal class KClassImpl<T : Any>(
|
||||
}
|
||||
}
|
||||
|
||||
private fun reportUnresolvedClass(): Nothing {
|
||||
private fun createSyntheticClassOrFail(classId: ClassId, moduleData: RuntimeModuleData): ClassDescriptor {
|
||||
when (val kind = ReflectKotlinClass.create(jClass)?.classHeader?.kind) {
|
||||
KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
|
||||
throw UnsupportedOperationException(
|
||||
"Packages and file facades are not yet supported in Kotlin reflection. " +
|
||||
"Meanwhile please use Java reflection to inspect this class: $jClass"
|
||||
)
|
||||
}
|
||||
KotlinClassHeader.Kind.SYNTHETIC_CLASS -> {
|
||||
throw UnsupportedOperationException(
|
||||
"This class is an internal synthetic class generated by the Kotlin compiler, such as an anonymous class " +
|
||||
"for a lambda, a SAM wrapper, a callable reference, etc. It's not a Kotlin class or interface, so the reflection " +
|
||||
"library has no idea what declarations it has. Please use Java reflection to inspect this class: $jClass"
|
||||
)
|
||||
}
|
||||
KotlinClassHeader.Kind.FILE_FACADE,
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS,
|
||||
KotlinClassHeader.Kind.MULTIFILE_CLASS_PART,
|
||||
KotlinClassHeader.Kind.SYNTHETIC_CLASS ->
|
||||
return createSyntheticClass(classId, moduleData)
|
||||
KotlinClassHeader.Kind.UNKNOWN -> {
|
||||
// Should not happen since ABI-related exception must have happened earlier
|
||||
throw KotlinReflectionInternalError("Unknown class: $jClass (kind = $kind)")
|
||||
}
|
||||
KotlinClassHeader.Kind.CLASS, null -> {
|
||||
// Should not happen since a proper Kotlin- or Java-class must have been resolved
|
||||
throw KotlinReflectionInternalError("Unresolved class: $jClass")
|
||||
throw KotlinReflectionInternalError("Unresolved class: $jClass (kind = $kind)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun createSyntheticClass(classId: ClassId, moduleData: RuntimeModuleData): ClassDescriptor =
|
||||
ClassDescriptorImpl(
|
||||
EmptyPackageFragmentDescriptor(moduleData.module, classId.packageFqName),
|
||||
classId.shortClassName,
|
||||
Modality.FINAL,
|
||||
ClassKind.CLASS,
|
||||
listOf(moduleData.module.builtIns.any.defaultType),
|
||||
SourceElement.NO_SOURCE,
|
||||
false,
|
||||
moduleData.deserialization.storageManager,
|
||||
).also { descriptor ->
|
||||
descriptor.initialize(object : GivenFunctionsMemberScope(moduleData.deserialization.storageManager, descriptor) {
|
||||
// Don't declare any functions in this class descriptor, only inherit equals/hashCode/toString from Any.
|
||||
override fun computeDeclaredFunctions(): List<FunctionDescriptor> = emptyList()
|
||||
}, emptySet(), null)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user