From dde5eced877bc5c1c317850a2f9f64737d750bd3 Mon Sep 17 00:00:00 2001 From: Sergey Bogolepov Date: Fri, 28 Feb 2020 16:02:03 +0700 Subject: [PATCH] [Interop][Linker] Full support for metadata-only interop Add support for IdSignatures to interop declarations from metadata-only libraries. --- .../DescriptorByIdSignatureFinder.kt | 66 +++++++++++++++++++ .../serialization/KonanDeclarationTable.kt | 19 ++++++ .../serialization/KonanIrModuleSerializer.kt | 2 +- .../konan/serialization/KonanIrlinker.kt | 5 ++ 4 files changed, 91 insertions(+), 1 deletion(-) create mode 100644 backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/DescriptorByIdSignatureFinder.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/DescriptorByIdSignatureFinder.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/DescriptorByIdSignatureFinder.kt new file mode 100644 index 00000000000..fa164b488ca --- /dev/null +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/DescriptorByIdSignatureFinder.kt @@ -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() + 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, 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 + } + } +} \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDeclarationTable.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDeclarationTable.kt index 5a06de3a5a4..a15fcbf129d 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDeclarationTable.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDeclarationTable.kt @@ -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 } \ No newline at end of file diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt index 7a25f8b7dbe..de9c7b4522c 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrModuleSerializer.kt @@ -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) } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt index 2a2a7753d91..091a43fa573 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanIrlinker.kt @@ -48,6 +48,8 @@ class KonanIrLinker( exportedDependencies: List ) : 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")