From d833b732c91e0ed28a6fa01de7f6f4f927a809e7 Mon Sep 17 00:00:00 2001 From: Alexander Udalov Date: Tue, 28 Mar 2023 01:11:49 +0200 Subject: [PATCH] Reflection: support Java anonymous/local classes Local classes and anonymous objects are normal classes and kotlin-reflect can load all declarations and modifiers from them, and support calling members, exactly in the same way as it does for normal non-local classes. #KT-41373 In Progress --- .../localClasses/localClassesAndAnonymousObjects.kt | 5 ++--- .../descriptors/runtime/structure/ReflectJavaClass.kt | 9 ++++++++- .../src/kotlin/reflect/jvm/internal/KClassImpl.kt | 9 +++++++-- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/compiler/testData/codegen/box/reflection/localClasses/localClassesAndAnonymousObjects.kt b/compiler/testData/codegen/box/reflection/localClasses/localClassesAndAnonymousObjects.kt index ecd38907402..c7859ef89b8 100644 --- a/compiler/testData/codegen/box/reflection/localClasses/localClassesAndAnonymousObjects.kt +++ b/compiler/testData/codegen/box/reflection/localClasses/localClassesAndAnonymousObjects.kt @@ -116,9 +116,8 @@ fun box(): String { checkKotlinAnonymousObject() checkKotlinLocalClass() - // TODO: fails with KotlinReflectionInternalError: Unresolved class: class JavaClass$1 (kind = null) - // checkJavaAnonymousObject() - // checkJavaLocalClass() + checkJavaAnonymousObject() + checkJavaLocalClass() return "OK" } diff --git a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt index 4cf9eaa6326..b590ba6d84c 100644 --- a/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt +++ b/core/descriptors.runtime/src/org/jetbrains/kotlin/descriptors/runtime/structure/ReflectJavaClass.kt @@ -105,7 +105,14 @@ class ReflectJavaClass( get() = null override val name: Name - get() = Name.identifier(klass.simpleName) + get() = if (klass.isAnonymousClass) { + // For anonymous classes, `Class.simpleName` returns an empty string in Java reflection. + // We don't want that because it breaks all sorts of invariants on names which cannot be empty in Kotlin. + // So we extract the simple name from the full JVM binary name, e.g. "org.test.Foo$1" -> "Foo$1". + Name.identifier(klass.name.substringAfterLast(".")) + } else { + Name.identifier(klass.simpleName) + } override val typeParameters: List get() = klass.typeParameters.map(::ReflectJavaTypeParameter) diff --git a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt index 7787a78fcd0..17449a072a6 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -51,8 +51,13 @@ internal class KClassImpl( val moduleData = data.value.moduleData val descriptor = - if (classId.isLocal) moduleData.deserialization.deserializeClass(classId) - else moduleData.module.findClassAcrossModuleDependencies(classId) + if (classId.isLocal && jClass.isAnnotationPresent(Metadata::class.java)) { + // If it's a Kotlin local class or anonymous object, deserialize its metadata directly because it cannot be found via + // `module.findClassAcrossModuleDependencies`. + moduleData.deserialization.deserializeClass(classId) + } else { + moduleData.module.findClassAcrossModuleDependencies(classId) + } descriptor ?: reportUnresolvedClass() }