[Commonizer] Optimized implementation of CirProvidedClassifiers

Read classifiers directly from metadata, don't use descriptors.
This commit is contained in:
Dmitriy Dolovov
2021-02-16 14:58:44 +03:00
parent 79e3ce022f
commit 43ad0ed907
6 changed files with 122 additions and 54 deletions
@@ -43,9 +43,10 @@ private fun mergeAndCommonize(storageManager: StorageManager, parameters: Common
forwardDeclarations = CirForwardDeclarations.default(),
dependencies = mapOf(
// for now, supply only common dependency libraries (ex: Kotlin stdlib)
parameters.sharedTarget to CirProvidedClassifiers.fromModules(storageManager) {
parameters.dependencyModulesProvider?.loadModules(emptyList())?.values.orEmpty()
}
parameters.sharedTarget to CirCompositeClassifiers(
CirFictitiousFunctionClassifiers,
CirLoadedClassifiers.from(parameters.dependencyModulesProvider)
)
)
)
val mergeResult = CirTreeMerger(storageManager, classifiers, parameters).merge()
@@ -0,0 +1,14 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.descriptors.commonizer.mergedtree
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId
class CirCompositeClassifiers(private val delegates: List<CirProvidedClassifiers>) : CirProvidedClassifiers {
constructor(vararg delegates: CirProvidedClassifiers) : this(delegates.toList())
override fun hasClassifier(classifierId: CirEntityId): Boolean = delegates.any { it.hasClassifier(classifierId) }
}
@@ -0,0 +1,32 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.descriptors.commonizer.mergedtree
import gnu.trove.THashSet
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName
object CirFictitiousFunctionClassifiers : CirProvidedClassifiers {
private const val MIN_ARITY = 0
private const val MAX_ARITY = 255
private val FUNCTION_PREFIXES = arrayOf("Function", "SuspendFunction")
private val PACKAGE_NAME = CirPackageName.create("kotlin")
private val classifiers: Set<CirEntityId> = THashSet<CirEntityId>().apply {
(MIN_ARITY..MAX_ARITY).forEach { arity ->
FUNCTION_PREFIXES.forEach { prefix ->
this += buildFictitiousFunctionClass(prefix, arity)
}
}
}
override fun hasClassifier(classifierId: CirEntityId): Boolean = classifierId in classifiers
private fun buildFictitiousFunctionClass(prefix: String, arity: Int): CirEntityId =
CirEntityId.create(PACKAGE_NAME, CirName.create("$prefix$arity"))
}
@@ -0,0 +1,67 @@
/*
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.descriptors.commonizer.mergedtree
import gnu.trove.THashSet
import org.jetbrains.kotlin.descriptors.commonizer.ModulesProvider
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirName
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName
import org.jetbrains.kotlin.library.metadata.parsePackageFragment
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl
class CirLoadedClassifiers(modulesProvider: ModulesProvider) : CirProvidedClassifiers {
private val classifiers: Set<CirEntityId> = loadClassifiers(modulesProvider)
override fun hasClassifier(classifierId: CirEntityId): Boolean = classifierId in classifiers
companion object {
fun from(modulesProvider: ModulesProvider?): CirProvidedClassifiers =
if (modulesProvider != null) CirLoadedClassifiers(modulesProvider) else CirProvidedClassifiers.EMPTY
}
}
private fun loadClassifiers(modulesProvider: ModulesProvider): Set<CirEntityId> {
val result = THashSet<CirEntityId>()
modulesProvider.loadModuleInfos().forEach { moduleInfo ->
val metadata = modulesProvider.loadModuleMetadata(moduleInfo.name)
for (i in metadata.fragmentNames.indices) {
val packageFqName = metadata.fragmentNames[i]
val packageFragments = metadata.fragments[i]
for (j in packageFragments.indices) {
val packageFragment: ProtoBuf.PackageFragment = parsePackageFragment(packageFragments[j])
val classes: List<ProtoBuf.Class> = packageFragment.class_List
val typeAliases: List<ProtoBuf.TypeAlias> = packageFragment.`package`?.typeAliasList.orEmpty()
if (classes.isEmpty() && typeAliases.isEmpty())
break // this and next package fragments do not contain classifiers and can be skipped
val packageName = CirPackageName.create(packageFqName)
val nameResolver = NameResolverImpl(packageFragment.strings, packageFragment.qualifiedNames)
for (clazz in classes) {
if (!nameResolver.isLocalClassName(clazz.fqName)) {
val classId = CirEntityId.create(nameResolver.getQualifiedClassName(clazz.fqName))
check(classId.packageName == packageName)
result += classId
}
}
for (typeAlias in typeAliases) {
val typeAliasId = CirEntityId.create(packageName, CirName.create(nameResolver.getString(typeAlias.name)))
result += typeAliasId
}
}
}
}
return result
}
@@ -7,16 +7,10 @@ package org.jetbrains.kotlin.descriptors.commonizer.mergedtree
import gnu.trove.THashMap
import gnu.trove.THashSet
import org.jetbrains.kotlin.descriptors.ModuleDescriptor
import org.jetbrains.kotlin.descriptors.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.descriptors.commonizer.CommonizerTarget
import org.jetbrains.kotlin.descriptors.commonizer.SharedCommonizerTarget
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirEntityId
import org.jetbrains.kotlin.descriptors.commonizer.cir.CirPackageName
import org.jetbrains.kotlin.descriptors.commonizer.utils.isUnderKotlinNativeSyntheticPackages
import org.jetbrains.kotlin.descriptors.commonizer.utils.resolveClassOrTypeAlias
import org.jetbrains.kotlin.resolve.scopes.MemberScope
import org.jetbrains.kotlin.storage.StorageManager
import org.jetbrains.kotlin.storage.getValue
class CirKnownClassifiers(
val commonized: CirCommonizedClassifiers,
@@ -28,6 +22,7 @@ class CirKnownClassifiers(
dependencies.filterKeys { it is SharedCommonizerTarget }.values.singleOrNull() ?: CirProvidedClassifiers.EMPTY
}
/** A set of all CIR nodes built for commonized classes and type aliases. */
interface CirCommonizedClassifiers {
/* Accessors */
fun classNode(classId: CirEntityId): CirClassNode?
@@ -58,6 +53,7 @@ interface CirCommonizedClassifiers {
}
}
/** A set of all exported forward declaration classes/objects/structs. */
interface CirForwardDeclarations {
/* Accessors */
fun isExportedForwardDeclaration(classId: CirEntityId): Boolean
@@ -79,6 +75,7 @@ interface CirForwardDeclarations {
}
}
/** A set of classes and type aliases provided by libraries (either the libraries to commonize, or their dependency libraries)/ */
interface CirProvidedClassifiers {
fun hasClassifier(classifierId: CirEntityId): Boolean
@@ -89,42 +86,5 @@ interface CirProvidedClassifiers {
internal val EMPTY = object : CirProvidedClassifiers {
override fun hasClassifier(classifierId: CirEntityId) = false
}
// N.B. This is suboptimal implementation. It will be replaced by another implementation that will
// retrieve classifier information directly from the metadata.
fun fromModules(storageManager: StorageManager, modules: () -> Collection<ModuleDescriptor>) = object : CirProvidedClassifiers {
private val nonEmptyMemberScopes: Map<CirPackageName, MemberScope> by storageManager.createLazyValue {
THashMap<CirPackageName, MemberScope>().apply {
for (module in modules()) {
module.collectNonEmptyPackageMemberScopes(probeRootPackageForEmptiness = true) { packageName, memberScope ->
this[packageName] = memberScope
}
}
}
}
private val presentClassifiers = THashSet<CirEntityId>()
private val missingClassifiers = THashSet<CirEntityId>()
override fun hasClassifier(classifierId: CirEntityId): Boolean {
if (classifierId.relativeNameSegments.isEmpty())
return false
val memberScope = nonEmptyMemberScopes[classifierId.packageName] ?: return false
return when (classifierId) {
in presentClassifiers -> true
in missingClassifiers -> false
else -> {
val found = memberScope.resolveClassOrTypeAlias(classifierId.relativeNameSegments) != null
when (found) {
true -> presentClassifiers += classifierId
false -> missingClassifiers += classifierId
}
found
}
}
}
}
}
}
@@ -64,23 +64,17 @@ internal inline fun FunctionCollector(
}
// collects member scopes for every non-empty package provided by this module
internal fun ModuleDescriptor.collectNonEmptyPackageMemberScopes(
probeRootPackageForEmptiness: Boolean = false, // false is the default as probing might be expensive and is not always necessary
collector: (CirPackageName, MemberScope) -> Unit
) {
internal fun ModuleDescriptor.collectNonEmptyPackageMemberScopes(collector: (CirPackageName, MemberScope) -> Unit) {
// we don's need to process fragments from other modules which are the dependencies of this module, so
// let's use the appropriate package fragment provider
val packageFragmentProvider = this.packageFragmentProvider
fun recurse(packageFqName: FqName) {
val probeForEmptiness = probeRootPackageForEmptiness && packageFqName.isRoot
val ownPackageMemberScopes = packageFragmentProvider.packageFragments(packageFqName)
.asSequence()
.filter { it !is ExportedForwardDeclarationsPackageFragmentDescriptor && it !is ClassifierAliasingPackageFragmentDescriptor }
.map { it.getMemberScope() }
.filter { it != MemberScope.Empty }
.filter { !probeForEmptiness || it.getContributedDescriptors().isNotEmpty() }
.toList()
if (ownPackageMemberScopes.isNotEmpty()) {