Do not use .kotlin_module files in reflection

Previously, we used a pretty roundabout way to load a MemberScope from a
single file facade represented by KPackageImpl, which involved going
through ModuleDescriptor, PackageFragmentProvider, PackagePartProvider
etc. The only advantage of this approach was that it sort of works
similarly as in the compiler, however mutable state in
RuntimePackagePartProvider and the fact that .kotlin_module files were
required for this to work diminished this advantage.

In this change, we load MemberScope from a KPackageImpl pretty much
directly, by using the existing method
`DeserializedDescriptorResolver.createKotlinPackagePartScope` and
caching the result in the new component PackagePartScopeCache.

 #KT-30344 Fixed
This commit is contained in:
Alexander Udalov
2019-03-15 19:01:25 +01:00
parent 7aa75ab89b
commit cd6c88fa2c
6 changed files with 75 additions and 155 deletions
@@ -16,10 +16,10 @@
package kotlin.reflect.jvm.internal
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.ConstructorDescriptor
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
import org.jetbrains.kotlin.load.java.lazy.descriptors.LazyJavaPackageFragment
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinaryPackageSourceElement
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
@@ -30,7 +30,6 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.serialization.deserialization.MemberDeserializer
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedCallableMemberDescriptor
import kotlin.reflect.KCallable
import kotlin.reflect.jvm.internal.KDeclarationContainerImpl.MemberBelonginess.DECLARED
import kotlin.reflect.jvm.internal.components.ReflectKotlinClass
@@ -42,26 +41,24 @@ internal class KPackageImpl(
) : KDeclarationContainerImpl() {
private inner class Data : KDeclarationContainerImpl.Data() {
private val kotlinClass: ReflectKotlinClass? by ReflectProperties.lazySoft {
// TODO: do not read ReflectKotlinClass multiple times
ReflectKotlinClass.create(jClass)
}
val descriptor: PackageViewDescriptor by ReflectProperties.lazySoft {
with(moduleData) {
kotlinClass?.packageModuleName?.let(packagePartProvider::registerModule)
module.getPackage(jClass.classId.packageFqName)
}
val scope: MemberScope by ReflectProperties.lazySoft {
val klass = kotlinClass
if (klass != null)
moduleData.packagePartScopeCache.getPackagePartScope(klass)
else MemberScope.Empty
}
val methodOwner: Class<*> by ReflectProperties.lazy {
val multifileFacade: Class<*>? by ReflectProperties.lazy {
val facadeName = kotlinClass?.classHeader?.multifileClassName
// We need to check isNotEmpty because this is the value read from the annotation which cannot be null.
// The default value for 'xs' is empty string, as declared in kotlin.Metadata
if (facadeName != null && facadeName.isNotEmpty()) {
if (facadeName != null && facadeName.isNotEmpty())
jClass.classLoader.loadClass(facadeName.replace('/', '.'))
} else {
jClass
}
else null
}
val metadata: Triple<JvmNameResolver, ProtoBuf.Package, JvmMetadataVersion>? by ReflectProperties.lazy {
@@ -76,20 +73,15 @@ internal class KPackageImpl(
}
val members: Collection<KCallableImpl<*>> by ReflectProperties.lazySoft {
getMembers(scope, DECLARED).filter { member ->
val callableDescriptor = member.descriptor as DeserializedCallableMemberDescriptor
val packageFragment = callableDescriptor.containingDeclaration as PackageFragmentDescriptor
val source = (packageFragment as? LazyJavaPackageFragment)?.source as? KotlinJvmBinaryPackageSourceElement
(source?.getContainingBinaryClass(callableDescriptor) as? ReflectKotlinClass)?.klass == jClass
}
getMembers(scope, DECLARED)
}
}
private val data = ReflectProperties.lazy { Data() }
override val methodOwner: Class<*> get() = data().methodOwner
override val methodOwner: Class<*> get() = data().multifileFacade ?: jClass
private val scope: MemberScope get() = data().descriptor.memberScope
private val scope: MemberScope get() = data().scope
override val members: Collection<KCallable<*>> get() = data().members
@@ -119,8 +111,6 @@ internal class KPackageImpl(
override fun hashCode(): Int =
jClass.hashCode()
override fun toString(): String {
val fqName = jClass.classId.packageFqName
return "package " + (if (fqName.isRoot) "<default>" else fqName.asString())
}
override fun toString(): String =
"file class ${jClass.classId.asSingleFqName()}"
}
@@ -20,13 +20,12 @@ import org.jetbrains.kotlin.builtins.jvm.JavaToKotlinClassMap
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.annotations.Annotated
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import org.jetbrains.kotlin.load.kotlin.header.KotlinClassHeader
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.*
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion
import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.deserialization.VersionRequirementTable
import org.jetbrains.kotlin.name.ClassId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.protobuf.MessageLite
@@ -169,27 +168,6 @@ internal fun Any?.asKPropertyImpl(): KPropertyImpl<*>? =
internal fun Any?.asKCallableImpl(): KCallableImpl<*>? =
this as? KCallableImpl<*> ?: asKFunctionImpl() ?: asKPropertyImpl()
internal val ReflectKotlinClass.packageModuleName: String?
get() {
val header = classHeader
if (!header.metadataVersion.isCompatible()) return null
return when (header.kind) {
KotlinClassHeader.Kind.FILE_FACADE, KotlinClassHeader.Kind.MULTIFILE_CLASS_PART -> {
// TODO: avoid reading and parsing metadata twice (here and later in KPackageImpl#descriptor)
val (nameResolver, proto) = JvmProtoBufUtil.readPackageDataFrom(header.data!!, header.strings!!)
// If no packageModuleName extension is written, the name is assumed to be JvmAbi.DEFAULT_MODULE_NAME
// (see JvmSerializerExtension.serializePackage)
proto.getExtensionOrNull(JvmProtoBuf.packageModuleName)?.let(nameResolver::getString) ?: JvmAbi.DEFAULT_MODULE_NAME
}
KotlinClassHeader.Kind.MULTIFILE_CLASS -> {
val partName = header.multifilePartNames.firstOrNull() ?: return null
ReflectKotlinClass.create(klass.classLoader.loadClass(partName.replace('/', '.')))?.packageModuleName
}
else -> null
}
}
internal val CallableDescriptor.instanceReceiverParameter: ReceiverParameterDescriptor?
get() =
if (dispatchReceiverParameter != null) (containingDeclaration as ClassDescriptor).thisAsReceiverParameter