diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt index 9ed53eb303d..0ed6ee7b2b4 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/checkers/JvmReflectionAPICallChecker.kt @@ -21,6 +21,7 @@ import org.jetbrains.kotlin.builtins.ReflectionTypes import org.jetbrains.kotlin.descriptors.ModuleDescriptor import org.jetbrains.kotlin.descriptors.findClassAcrossModuleDependencies import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.calls.checkers.AbstractReflectionApiCallChecker import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext import org.jetbrains.kotlin.resolve.jvm.diagnostics.ErrorsJvm.NO_REFLECTION_IN_CLASS_PATH @@ -36,6 +37,9 @@ class JvmReflectionAPICallChecker( reflectionTypes: ReflectionTypes, storageManager: StorageManager ) : AbstractReflectionApiCallChecker(reflectionTypes, storageManager) { + override fun isAllowedKClassMember(name: Name): Boolean = + super.isAllowedKClassMember(name) || name.asString() == "qualifiedName" + override val isWholeReflectionApiAvailable by storageManager.createLazyValue { module.findClassAcrossModuleDependencies(JvmAbi.REFLECTION_FACTORY_IMPL) != null } diff --git a/compiler/testData/codegen/box/reflection/classes/javaVoid.kt b/compiler/testData/codegen/box/reflection/classes/javaVoid.kt index 69eb3654890..7754f906973 100644 --- a/compiler/testData/codegen/box/reflection/classes/javaVoid.kt +++ b/compiler/testData/codegen/box/reflection/classes/javaVoid.kt @@ -14,6 +14,7 @@ fun box(): String { assertEquals(Void::class.java, Void.TYPE.kotlin.javaObjectType) assertEquals("Void", Void::class.simpleName) + assertEquals("java.lang.Void", Void::class.qualifiedName) return "OK" } diff --git a/compiler/testData/codegen/box/reflection/classes/qualifiedName.kt b/compiler/testData/codegen/box/reflection/classes/qualifiedName.kt index b48029cb128..df202f619cc 100644 --- a/compiler/testData/codegen/box/reflection/classes/qualifiedName.kt +++ b/compiler/testData/codegen/box/reflection/classes/qualifiedName.kt @@ -1,6 +1,5 @@ // TARGET_BACKEND: JVM - -// WITH_REFLECT +// WITH_RUNTIME package test @@ -8,14 +7,19 @@ import kotlin.test.assertEquals class Klass { class Nested + class `Nested$With$Dollars` companion object } fun box(): String { assertEquals("test.Klass", Klass::class.qualifiedName) assertEquals("test.Klass.Nested", Klass.Nested::class.qualifiedName) + assertEquals("test.Klass.Nested\$With\$Dollars", Klass.`Nested$With$Dollars`::class.qualifiedName) assertEquals("test.Klass.Companion", Klass.Companion::class.qualifiedName) + assertEquals("java.util.Date", java.util.Date::class.qualifiedName) + assertEquals("kotlin.jvm.internal.Ref.ObjectRef", kotlin.jvm.internal.Ref.ObjectRef::class.qualifiedName) + class Local assertEquals(null, Local::class.qualifiedName) diff --git a/compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt b/compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt index b2ad8b9def0..550a3e47ff3 100644 --- a/compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt +++ b/compiler/testData/codegen/box/reflection/classes/qualifiedNameOfStandardClasses.kt @@ -1,25 +1,40 @@ // TARGET_BACKEND: JVM - -// WITH_REFLECT +// WITH_RUNTIME import kotlin.test.assertEquals fun box(): String { assertEquals("kotlin.Any", Any::class.qualifiedName) - assertEquals("kotlin.Int", Int::class.qualifiedName) - assertEquals("kotlin.Int.Companion", Int.Companion::class.qualifiedName) - assertEquals("kotlin.IntArray", IntArray::class.qualifiedName) - assertEquals("kotlin.collections.List", List::class.qualifiedName) assertEquals("kotlin.String", String::class.qualifiedName) - assertEquals("kotlin.String", java.lang.String::class.qualifiedName) + assertEquals("kotlin.CharSequence", CharSequence::class.qualifiedName) + assertEquals("kotlin.Number", Number::class.qualifiedName) + assertEquals("kotlin.Int", Int::class.qualifiedName) + assertEquals("kotlin.Long", Long::class.qualifiedName) assertEquals("kotlin.Array", Array::class.qualifiedName) - assertEquals("kotlin.Array", Array::class.qualifiedName) + assertEquals("kotlin.Array", Array::class.qualifiedName) assertEquals("kotlin.Array", Array>::class.qualifiedName) - assertEquals("java.util.Date", java.util.Date::class.qualifiedName) - assertEquals("kotlin.jvm.internal.Ref.ObjectRef", kotlin.jvm.internal.Ref.ObjectRef::class.qualifiedName) - assertEquals("java.lang.Void", java.lang.Void::class.qualifiedName) + assertEquals("kotlin.IntArray", IntArray::class.qualifiedName) + assertEquals("kotlin.DoubleArray", DoubleArray::class.qualifiedName) + + assertEquals("kotlin.Int.Companion", Int.Companion::class.qualifiedName) + assertEquals("kotlin.Double.Companion", Double.Companion::class.qualifiedName) + assertEquals("kotlin.Char.Companion", Char.Companion::class.qualifiedName) + + assertEquals("kotlin.ranges.IntRange", IntRange::class.qualifiedName) + + assertEquals("kotlin.collections.List", List::class.qualifiedName) + assertEquals("kotlin.collections.Map.Entry", Map.Entry::class.qualifiedName) + + // TODO: KT-11754 + assertEquals("kotlin.collections.List", MutableList::class.qualifiedName) + assertEquals("kotlin.collections.Map.Entry", MutableMap.MutableEntry::class.qualifiedName) + + assertEquals("kotlin.Function0", Function0::class.qualifiedName) + assertEquals("kotlin.Function1", Function1::class.qualifiedName) + assertEquals("kotlin.Function5", Function5::class.qualifiedName) + assertEquals("kotlin.jvm.functions.FunctionN", Function42::class.qualifiedName) return "OK" } diff --git a/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaClass.kt b/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaClass.kt index 041918dda19..07da9ee0eaf 100644 --- a/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaClass.kt +++ b/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaClass.kt @@ -18,7 +18,6 @@ fun box(): String { assertEquals("Klass", kClass.simpleName) assertEquals(kjClass, jjClass) - try { kClass.qualifiedName; return "Fail qualifiedName" } catch (e: Error) {} try { kClass.members; return "Fail members" } catch (e: Error) {} val jlError = Error::class.java diff --git a/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaVoid.kt b/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaVoid.kt index 8d18fe14955..74ca559916a 100644 --- a/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaVoid.kt +++ b/compiler/testData/codegen/box/reflection/noReflectAtRuntime/javaVoid.kt @@ -14,6 +14,7 @@ fun box(): String { assertEquals(Void::class.java, Void.TYPE.kotlin.javaObjectType) assertEquals("Void", Void::class.simpleName) + assertEquals("java.lang.Void", Void::class.qualifiedName) return "OK" } diff --git a/compiler/testData/diagnostics/tests/reflection/noReflectionInClassPath.kt b/compiler/testData/diagnostics/tests/reflection/noReflectionInClassPath.kt index c55fb0ae62d..cccb8c05af7 100644 --- a/compiler/testData/diagnostics/tests/reflection/noReflectionInClassPath.kt +++ b/compiler/testData/diagnostics/tests/reflection/noReflectionInClassPath.kt @@ -22,7 +22,7 @@ fun y04() = Foo::class.properties fun kclass(k: KClass<*>, kt: KClass) { k.simpleName - k.qualifiedName + k.qualifiedName k.members k.constructors k.nestedClasses 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 ba7b9767921..fe4c260caf7 100644 --- a/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt +++ b/core/reflection.jvm/src/kotlin/reflect/jvm/internal/KClassImpl.kt @@ -56,16 +56,6 @@ internal class KClassImpl(override val jClass: Class) : KDeclaration val annotations: List by ReflectProperties.lazySoft { descriptor.computeAnnotations() } - val qualifiedName: String? by ReflectProperties.lazySoft { - if (jClass.isAnonymousClass) return@lazySoft null - - val classId = classId - when { - classId.isLocal -> null - else -> classId.asSingleFqName().asString() - } - } - @Suppress("UNCHECKED_CAST") val constructors: Collection> by ReflectProperties.lazySoft { constructorDescriptors.map { descriptor -> @@ -163,6 +153,9 @@ internal class KClassImpl(override val jClass: Class) : KDeclaration private val classId: ClassId get() = RuntimeTypeMapper.mapJvmClassToKotlinClassId(jClass).also { result -> if (!jClass.isAnonymousClass && !jClass.isLocalClass) { + assert(result.asSingleFqName().asString() == qualifiedName) { + "Incorrect class name computed for class ${jClass.name}. Result: $result. Expected qualified name $qualifiedName" + } assert(result.shortClassName.asString() == simpleName) { "Incorrect class name computed for class ${jClass.name}. Result: $result. Expected simple name $simpleName" } @@ -217,7 +210,7 @@ internal class KClassImpl(override val jClass: Class) : KDeclaration override val simpleName: String? get() = ClassReference.getClassSimpleName(jClass) - override val qualifiedName: String? get() = data().qualifiedName + override val qualifiedName: String? get() = ClassReference.getClassQualifiedName(jClass) override val constructors: Collection> get() = data().constructors diff --git a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt index 6ac4bf7819a..bc67e773556 100644 --- a/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt +++ b/libraries/stdlib/jvm/runtime/kotlin/jvm/internal/ClassReference.kt @@ -12,7 +12,7 @@ public class ClassReference(override val jClass: Class<*>) : KClass, ClassB get() = getClassSimpleName(jClass) override val qualifiedName: String? - get() = error() + get() = getClassQualifiedName(jClass) override val members: Collection> get() = error() @@ -137,6 +137,9 @@ public class ClassReference(override val jClass: Class<*>) : KClass, ClassB primitiveFqNames.values.associateTo(this) { kotlinName -> "kotlin.jvm.internal.${kotlinName.substringAfterLast('.')}CompanionObject" to "$kotlinName.Companion" } + for (arity in 0 until 23) { + put("kotlin.jvm.functions.Function$arity", "kotlin.Function$arity") + } } private val simpleNames = classFqNames.mapValues { (_, fqName) -> fqName.substringAfterLast('.') } @@ -158,5 +161,18 @@ public class ClassReference(override val jClass: Class<*>) : KClass, ClassB } else -> simpleNames[jClass.name] ?: jClass.simpleName } + + public fun getClassQualifiedName(jClass: Class<*>): String? = when { + jClass.isAnonymousClass -> null + jClass.isLocalClass -> null + jClass.isArray -> { + val componentType = jClass.componentType + when { + componentType.isPrimitive -> classFqNames[componentType.name]?.plus("Array") + else -> null + } ?: "kotlin.Array" + } + else -> classFqNames[jClass.name] ?: jClass.canonicalName + } } } diff --git a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt index 54a31d8f977..eb4b29f7804 100644 --- a/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt +++ b/libraries/tools/binary-compatibility-validator/reference-public-api/kotlin-stdlib-runtime-merged.txt @@ -3297,6 +3297,7 @@ public final class kotlin/jvm/internal/ClassReference : kotlin/jvm/internal/Clas } public final class kotlin/jvm/internal/ClassReference$Companion { + public final fun getClassQualifiedName (Ljava/lang/Class;)Ljava/lang/String; public final fun getClassSimpleName (Ljava/lang/Class;)Ljava/lang/String; }