[Interop][Linker] Full support for metadata-only interop

Add support for IdSignatures to interop declarations from metadata-only libraries.
This commit is contained in:
Sergey Bogolepov
2020-02-28 16:02:03 +07:00
committed by Sergey Bogolepov
parent ac5fd80cb4
commit dde5eced87
4 changed files with 91 additions and 1 deletions
@@ -0,0 +1,66 @@
/*
* Copyright 2010-2020 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
* that can be found in the LICENSE file.
*/
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.ir.util.IdSignature
// The code here is intentionally copy-pasted from DeclarationStubGenerator with
// minor changes.
// "Find descriptor by IdSignature" task appears to be common and should be unified later.
class DescriptorByIdSignatureFinder(
private val moduleDescriptor: ModuleDescriptor
) {
fun findDescriptorBySignature(signature: IdSignature): DeclarationDescriptor? = when (signature) {
is IdSignature.AccessorSignature -> findDescriptorForAccessorSignature(signature)
is IdSignature.PublicSignature -> findDescriptorForPublicSignature(signature)
else -> error("only PublicSignature or AccessorSignature should reach this point, got $signature")
}
private fun findDescriptorForAccessorSignature(signature: IdSignature.AccessorSignature): DeclarationDescriptor? {
val propertyDescriptor = findDescriptorBySignature(signature.propertySignature) as? PropertyDescriptor
?: return null
return propertyDescriptor.accessors.singleOrNull {
it.name == signature.accessorSignature.declarationFqn.shortName()
}
}
private fun findDescriptorForPublicSignature(signature: IdSignature.PublicSignature): DeclarationDescriptor? {
val packageDescriptor = moduleDescriptor.getPackage(signature.packageFqName())
val pathSegments = signature.declarationFqn.pathSegments()
val toplevelDescriptors = packageDescriptor.memberScope.getContributedDescriptors { name -> name == pathSegments.first() }
.filter { it.name == pathSegments.first() }
val candidates = pathSegments.drop(1).fold(toplevelDescriptors) { acc, current ->
acc.flatMap { container ->
val classDescriptor = container as? ClassDescriptor
?: return@flatMap emptyList<DeclarationDescriptor>()
val nextStepCandidates = classDescriptor.constructors +
classDescriptor.unsubstitutedMemberScope.getContributedDescriptors { name -> name == current } +
// Static scope is required only for Enum.values() and Enum.valueOf().
classDescriptor.staticScope.getContributedDescriptors { name -> name == current }
nextStepCandidates.filter { it.name == current }
}
}
return when (candidates.size) {
1 -> candidates.first()
else -> {
findDescriptorByHash(candidates, signature.id)
?: error("No descriptor found for $signature")
}
}
}
private fun findDescriptorByHash(candidates: List<DeclarationDescriptor>, id: Long?): DeclarationDescriptor? =
candidates.firstOrNull { candidate ->
if (id == null) {
// We don't compute id for typealiases and classes.
candidate is ClassDescriptor || candidate is TypeAliasDescriptor
} else {
val candidateHash = with(KonanManglerDesc) { candidate.signatureMangle }
candidateHash == id
}
}
}
@@ -1,12 +1,31 @@
package org.jetbrains.kotlin.backend.konan.serialization
import org.jetbrains.kotlin.backend.common.serialization.DeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.GlobalDeclarationTable
import org.jetbrains.kotlin.backend.common.serialization.signature.DescToIrIdSignatureComputer
import org.jetbrains.kotlin.backend.common.serialization.signature.IdSignatureSerializer
import org.jetbrains.kotlin.backend.konan.descriptors.isFromInteropLibrary
import org.jetbrains.kotlin.ir.declarations.IrDeclaration
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
import org.jetbrains.kotlin.ir.util.IdSignature
import org.jetbrains.kotlin.resolve.descriptorUtil.module
class KonanGlobalDeclarationTable(signatureSerializer: IdSignatureSerializer, builtIns: IrBuiltIns) : GlobalDeclarationTable(signatureSerializer, KonanManglerIr) {
init {
loadKnownBuiltins(builtIns)
}
}
class KonanDeclarationTable(
globalDeclarationTable: GlobalDeclarationTable
) : DeclarationTable(globalDeclarationTable) {
private val signatureIdComposer = DescToIrIdSignatureComputer(KonanIdSignaturer(KonanManglerDesc))
// TODO: We should get rid of this extension point in favor of proper support in IR-based mangler.
override fun tryComputeBackendSpecificSignature(declaration: IrDeclaration): IdSignature? =
if (declaration.descriptor.module.isFromInteropLibrary()) {
signatureIdComposer.computeSignature(declaration)
} else null
}
@@ -29,5 +29,5 @@ class KonanIrModuleSerializer(
file.fileEntry.name != IrProviderForCEnumAndCStructStubs.cTypeDefinitionsFileName
override fun createSerializerForFile(file: IrFile): KonanIrFileSerializer =
KonanIrFileSerializer(logger, DeclarationTable(globalDeclarationTable), expectDescriptorToSymbol, skipExpects = skipExpects)
KonanIrFileSerializer(logger, KonanDeclarationTable(globalDeclarationTable), expectDescriptorToSymbol, skipExpects = skipExpects)
}
@@ -48,6 +48,8 @@ class KonanIrLinker(
exportedDependencies: List<ModuleDescriptor>
) : KotlinIrLinker(logger, builtIns, symbolTable, exportedDependencies, forwardModuleDescriptor) {
private val descriptorByIdSignatureFinder = DescriptorByIdSignatureFinder(currentModule)
override fun reader(moduleDescriptor: ModuleDescriptor, fileIndex: Int, idSigIndex: Int) =
moduleDescriptor.konanLibrary!!.irDeclaration(idSigIndex, fileIndex)
@@ -93,6 +95,9 @@ class KonanIrLinker(
}
override fun resolvePlatformDescriptor(idSig: IdSignature): DeclarationDescriptor? {
if (idSig.isInteropSignature()) {
return descriptorByIdSignatureFinder.findDescriptorBySignature(idSig)
}
if (!idSig.isForwardDeclarationSignature()) return null
val fwdModule = forwardModuleDescriptor ?: error("Forward declaration module should not be null")