From c40396bf1907ddafa18fd363deebaee4a5701573 Mon Sep 17 00:00:00 2001 From: Dmitriy Dolovov <41585329+ddolovov@users.noreply.github.com> Date: Thu, 13 Sep 2018 18:01:56 +0300 Subject: [PATCH] Split serialized package fragments metadata (#2051) --- .../kotlin/backend/konan/ObjCInterop.kt | 12 +- .../descriptors/LegacyDescriptorUtils.kt | 5 + .../konan/library/KonanLibraryWriter.kt | 2 +- .../konan/library/impl/MetadataWriterImpl.kt | 12 +- .../KonanDescriptorSerializer.kt | 2 +- .../serialization/KonanSerializationUtil.kt | 210 ++++++++++++------ .../konan/KonanClassDataFinder.kt | 5 +- .../konan/KonanPackageFragment.kt | 5 +- ...DeserializedPackageFragmentsFactoryImpl.kt | 8 +- .../jetbrains/kotlin/cli/klib/KlibPrinter.kt | 9 +- .../kotlin/cli/klib/test/ContentsTest.kt | 77 ++++++- .../TopLevelPropertiesCustomPackage.kt | 28 +++ .../testData/TopLevelPropertiesRootPackage.kt | 26 +++ ...LevelPropertiesWithClassesCustomPackage.kt | 31 +++ ...opLevelPropertiesWithClassesRootPackage.kt | 29 +++ .../kotlin/konan/library/KonanLibrary.kt | 4 +- .../konan/library/KonanLibraryLayout.kt | 7 +- .../kotlin/konan/library/KonanLibraryUtils.kt | 7 +- .../konan/library/KonanMetadataReader.kt | 2 +- .../library/impl/DefaultMetadataReaderImpl.kt | 4 +- .../konan/library/impl/KonanLibraryImpl.kt | 19 +- 21 files changed, 409 insertions(+), 95 deletions(-) create mode 100644 klib/src/test/testData/TopLevelPropertiesCustomPackage.kt create mode 100644 klib/src/test/testData/TopLevelPropertiesRootPackage.kt create mode 100644 klib/src/test/testData/TopLevelPropertiesWithClassesCustomPackage.kt create mode 100644 klib/src/test/testData/TopLevelPropertiesWithClassesRootPackage.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt index 6d754ffd3d3..1bc88fb488a 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/ObjCInterop.kt @@ -6,6 +6,7 @@ package org.jetbrains.kotlin.backend.konan import org.jetbrains.kotlin.backend.konan.descriptors.findPackage +import org.jetbrains.kotlin.backend.konan.descriptors.findPackageView import org.jetbrains.kotlin.backend.konan.descriptors.getStringValue import org.jetbrains.kotlin.backend.konan.irasdescriptors.constructedClass import org.jetbrains.kotlin.backend.konan.irasdescriptors.getExternalObjCMethodInfo @@ -21,6 +22,7 @@ import org.jetbrains.kotlin.resolve.ExternalOverridabilityCondition import org.jetbrains.kotlin.resolve.constants.BooleanValue import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperClassifiers +import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.descriptorUtil.parentsWithSelf import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -75,15 +77,15 @@ private fun FunctionDescriptor.decodeObjCMethodAnnotation(): ObjCMethodInfo? { assert (this.kind.isReal) val methodAnnotation = this.annotations.findAnnotation(objCMethodFqName) ?: return null - val packageFragment = this.findPackage() + val packageView = this.findPackageView() val bridgeName = methodAnnotation.getStringValue("bridge") - return objCMethodInfoByBridge(packageFragment, bridgeName) + return objCMethodInfoByBridge(packageView, bridgeName) } -private fun objCMethodInfoByBridge(packageFragment: PackageFragmentDescriptor, bridgeName: String): ObjCMethodInfo { - val bridge = packageFragment.getMemberScope() +private fun objCMethodInfoByBridge(packageView: PackageViewDescriptor, bridgeName: String): ObjCMethodInfo { + val bridge = packageView.memberScope .getContributedFunctions(Name.identifier(bridgeName), NoLookupLocation.FROM_BACKEND) .single() @@ -213,7 +215,7 @@ fun FunctionDescriptor.getObjCFactoryInitMethodInfo(): ObjCMethodInfo? { val factoryAnnotation = this.annotations.findAnnotation(objCFactoryFqName) ?: return null val bridgeName = factoryAnnotation.getStringValue("bridge") - return objCMethodInfoByBridge(this.findPackage(), bridgeName) + return objCMethodInfoByBridge(this.findPackageView(), bridgeName) } fun inferObjCSelector(descriptor: FunctionDescriptor): String = if (descriptor.valueParameters.isEmpty()) { diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt index b7e1970a3cb..5e1dda024c4 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/descriptors/LegacyDescriptorUtils.kt @@ -114,6 +114,11 @@ tailrec internal fun DeclarationDescriptor.findPackage(): PackageFragmentDescrip else this.containingDeclaration!!.findPackage() } +internal fun DeclarationDescriptor.findPackageView(): PackageViewDescriptor { + val packageFragment = this.findPackage() + return packageFragment.module.getPackage(packageFragment.fqName) +} + internal fun DeclarationDescriptor.allContainingDeclarations(): List { var list = mutableListOf() var current = this.containingDeclaration diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt index bf0662e5be1..6142e2f3735 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/KonanLibraryWriter.kt @@ -25,7 +25,7 @@ interface KonanLibraryWriter { class LinkData( val module: ByteArray, - val fragments: List, + val fragments: List>, val fragmentNames: List ) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/MetadataWriterImpl.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/MetadataWriterImpl.kt index 51a66467970..be8d8bb7388 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/MetadataWriterImpl.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/library/impl/MetadataWriterImpl.kt @@ -13,8 +13,16 @@ internal class MetadataWriterImpl(libraryLayout: KonanLibraryLayout): KonanLibra fun addLinkData(linkData: LinkData) { moduleHeaderFile.writeBytes(linkData.module) linkData.fragments.forEachIndexed { index, it -> - val name = linkData.fragmentNames[index] - packageFile(name).writeBytes(it) + val packageFqName = linkData.fragmentNames[index] + val shortName = packageFqName.substringAfterLast(".") + val dir = packageFragmentsDir(packageFqName) + dir.deleteRecursively() + dir.mkdirs() + val numCount = it.size.toString().length + fun withLeadingZeros(i: Int) = String.format("%0${numCount}d", i) + for ((i, fragment) in it.withIndex()) { + packageFragmentFile(packageFqName, "${withLeadingZeros(i)}_$shortName").writeBytes(fragment) + } } } } diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDescriptorSerializer.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDescriptorSerializer.kt index a5d006aa4d1..562fa7caf5e 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDescriptorSerializer.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanDescriptorSerializer.kt @@ -615,7 +615,7 @@ class KonanDescriptorSerializer private constructor( fun packagePartProto(packageFqName: FqName, members: Collection): ProtoBuf.Package.Builder { val builder = ProtoBuf.Package.newBuilder() - for (declaration in sort(members)) { + for (declaration in members) { when (declaration) { is PropertyDescriptor -> builder.addProperty(propertyProto(declaration)) is FunctionDescriptor -> builder.addFunction(functionProto(declaration)) diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt index 58df04da370..84deaeb6127 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/serialization/KonanSerializationUtil.kt @@ -8,9 +8,8 @@ package org.jetbrains.kotlin.backend.konan.serialization import org.jetbrains.kotlin.backend.konan.Context import org.jetbrains.kotlin.backend.konan.descriptors.isExpectMember import org.jetbrains.kotlin.backend.konan.library.LinkData -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.ModuleDescriptor +import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.BinaryVersion import org.jetbrains.kotlin.metadata.konan.KonanProtoBuf import org.jetbrains.kotlin.name.FqName @@ -18,6 +17,9 @@ import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.checkers.ExpectedActualDeclarationChecker import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.CALLABLES +import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter.Companion.CLASSIFIERS +import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered import org.jetbrains.kotlin.serialization.KonanDescriptorSerializer /* @@ -31,39 +33,57 @@ import org.jetbrains.kotlin.serialization.KonanDescriptorSerializer * with MemberDeserializer class. */ -internal class KonanSerializationUtil(val context: Context, metadataVersion: BinaryVersion) { +internal class KonanSerializationUtil(val context: Context, private val metadataVersion: BinaryVersion) { - val serializerExtension = KonanSerializerExtension(context, metadataVersion) - val topSerializer = KonanDescriptorSerializer.createTopLevel(context, serializerExtension) - var classSerializer: KonanDescriptorSerializer = topSerializer + lateinit var serializerContext: SerializerContext - fun serializeClass(packageName: FqName, - builder: KonanProtoBuf.LinkDataClasses.Builder, - classDescriptor: ClassDescriptor) { + data class SerializerContext( + val serializerExtension: KonanSerializerExtension, + val topSerializer: KonanDescriptorSerializer, + var classSerializer: KonanDescriptorSerializer = topSerializer + ) - val previousSerializer = classSerializer - - // TODO: this is to filter out object{}. Change me. - if (classDescriptor.isExported()) - classSerializer = KonanDescriptorSerializer.create(context, classDescriptor, serializerExtension) - - val classProto = classSerializer.classProto(classDescriptor).build() - ?: error("Class not serialized: $classDescriptor") - - builder.addClasses(classProto) - val index = classSerializer.stringTable.getFqNameIndex(classDescriptor) - builder.addClassName(index) - - serializeClasses(packageName, builder, - classDescriptor.unsubstitutedInnerClassesScope - .getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS)) - - classSerializer = previousSerializer + private fun createNewContext(): SerializerContext { + val extension = KonanSerializerExtension(context, metadataVersion) + return SerializerContext( + extension, + KonanDescriptorSerializer.createTopLevel(context, extension) + ) } - fun serializeClasses(packageName: FqName, - builder: KonanProtoBuf.LinkDataClasses.Builder, - descriptors: Collection) { + private inline fun withNewContext(crossinline block: SerializerContext.() -> T): T { + serializerContext = createNewContext() + return with(serializerContext, block) + } + + private fun serializeClass(packageName: FqName, + builder: KonanProtoBuf.LinkDataClasses.Builder, + classDescriptor: ClassDescriptor) { + with(serializerContext) { + val previousSerializer = classSerializer + + // TODO: this is to filter out object{}. Change me. + if (classDescriptor.isExported()) + classSerializer = KonanDescriptorSerializer.create(context, classDescriptor, serializerExtension) + + val classProto = classSerializer.classProto(classDescriptor).build() + ?: error("Class not serialized: $classDescriptor") + + builder.addClasses(classProto) + val index = classSerializer.stringTable.getFqNameIndex(classDescriptor) + builder.addClassName(index) + + serializeClasses(packageName, builder, + classDescriptor.unsubstitutedInnerClassesScope + .getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS)) + + classSerializer = previousSerializer + } + } + + private fun serializeClasses(packageName: FqName, + builder: KonanProtoBuf.LinkDataClasses.Builder, + descriptors: Collection) { for (descriptor in descriptors) { if (descriptor is ClassDescriptor) { @@ -72,51 +92,89 @@ internal class KonanSerializationUtil(val context: Context, metadataVersion: Bin } } - fun serializePackage(fqName: FqName, module: ModuleDescriptor) : - KonanProtoBuf.LinkDataPackageFragment? { + private fun serializePackage(fqName: FqName, module: ModuleDescriptor): + List { // TODO: ModuleDescriptor should be able to return // the package only with the contents of that module, without dependencies val fragments = module.getPackage(fqName).fragments.filter { it.module == module } - if (fragments.isEmpty()) return null + if (fragments.isEmpty()) return emptyList() val classifierDescriptors = KonanDescriptorSerializer.sort( fragments.flatMap { - it.getMemberScope().getContributedDescriptors(DescriptorKindFilter.CLASSIFIERS) + it.getMemberScope().getDescriptorsFiltered(CLASSIFIERS) }.filter { !it.isExpectMember || it.isSerializableExpectClass } ) - val members = fragments.flatMap { fragment -> - DescriptorUtils.getAllDescriptors(fragment.getMemberScope()) - }.filter { !it.isExpectMember } + val topLevelDescriptors = KonanDescriptorSerializer.sort( + fragments.flatMap { fragment -> + fragment.getMemberScope().getDescriptorsFiltered(CALLABLES) + }.filter { !it.isExpectMember } + ) - val classesBuilder = KonanProtoBuf.LinkDataClasses.newBuilder() + val result = mutableListOf() - serializeClasses(fqName, classesBuilder, classifierDescriptors) - val classesProto = classesBuilder.build() + result += classifierDescriptors.chunked(TOP_LEVEL_CLASS_DECLARATION_COUNT_PER_FILE) { descriptors -> - val packageProto = topSerializer.packagePartProto(fqName, members).build() - ?: error("Package fragments not serialized: $fragments") + withNewContext { - val strings = serializerExtension.stringTable - val (stringTableProto, nameTableProto) = strings.buildProto() + val classesProto = buildClassesProto { classesBuilder -> + serializeClasses(fqName, classesBuilder, descriptors) + } - val isEmpty = members.isEmpty() && classifierDescriptors.isEmpty() - val fragmentBuilder = KonanProtoBuf.LinkDataPackageFragment.newBuilder() + val typeAliases = descriptors.filterIsInstance() + val packageProto = + if (typeAliases.isNotEmpty()) buildPackageProto(fqName, typeAliases, fragments) + else emptyPackageProto() - val fragmentProto = fragmentBuilder - .setPackage(packageProto) - .setFqName(fqName.asString()) - .setClasses(classesProto) - .setStringTable(stringTableProto) - .setNameTable(nameTableProto) - .setIsEmpty(isEmpty) - .build() + buildFragment( + packageProto, + classesProto, + fqName, + descriptors.isEmpty() + ) + } + } - return fragmentProto + result += topLevelDescriptors.chunked(TOP_LEVEL_DECLARATION_COUNT_PER_FILE) { descriptors -> + withNewContext { + buildFragment( + buildPackageProto(fqName, descriptors, fragments), + buildClassesProto {}, + fqName, + descriptors.isEmpty()) + } + } + + if (result.isEmpty()) { + result += withNewContext { + buildFragment( + emptyPackageProto(), + buildClassesProto {}, + fqName, + true) + } + } + + return result } + + private fun buildClassesProto(action: (KonanProtoBuf.LinkDataClasses.Builder) -> Unit): KonanProtoBuf.LinkDataClasses { + val classesBuilder = KonanProtoBuf.LinkDataClasses.newBuilder() + action(classesBuilder) + return classesBuilder.build() + } + + private fun emptyPackageProto(): ProtoBuf.Package = ProtoBuf.Package.newBuilder().build() + + private fun SerializerContext.buildPackageProto( + fqName: FqName, + descriptors: List, + fragments: List) = topSerializer.packagePartProto(fqName, descriptors).build() + ?: error("Package fragments not serialized: $fragments") + private fun getPackagesFqNames(module: ModuleDescriptor): Set { val result = mutableSetOf() @@ -129,25 +187,47 @@ internal class KonanSerializationUtil(val context: Context, metadataVersion: Bin return result } + private fun SerializerContext.buildFragment( + packageProto: ProtoBuf.Package, + classesProto: KonanProtoBuf.LinkDataClasses, + fqName: FqName, + isEmpty: Boolean + ): KonanProtoBuf.LinkDataPackageFragment { + + val (stringTableProto, nameTableProto) = serializerExtension.stringTable.buildProto() + + return KonanProtoBuf.LinkDataPackageFragment.newBuilder() + .setPackage(packageProto) + .setClasses(classesProto) + .setIsEmpty(isEmpty) + .setFqName(fqName.asString()) + .setStringTable(stringTableProto) + .setNameTable(nameTableProto) + .build() + } + internal fun serializeModule(moduleDescriptor: ModuleDescriptor): LinkData { val libraryProto = KonanProtoBuf.LinkDataLibrary.newBuilder() libraryProto.moduleName = moduleDescriptor.name.asString() - val fragments = mutableListOf() + val fragments = mutableListOf>() val fragmentNames = mutableListOf() - getPackagesFqNames(moduleDescriptor).forEach iteration@ { - val packageProto = serializePackage(it, moduleDescriptor) - if (packageProto == null) return@iteration + getPackagesFqNames(moduleDescriptor).forEach iteration@{ packageFqName -> + val packageProtos = serializePackage(packageFqName, moduleDescriptor) + if (packageProtos.isEmpty()) return@iteration - libraryProto.addPackageFragmentName(it.asString()) - if (packageProto.isEmpty) { - libraryProto.addEmptyPackage(it.asString()) + val packageFqNameStr = packageFqName.asString() + libraryProto.addPackageFragmentName(packageFqNameStr) + if (packageProtos.all { it.isEmpty }) { + libraryProto.addEmptyPackage(packageFqNameStr) } - fragments.add(packageProto.toByteArray()) - fragmentNames.add(it.asString()) + fragments.add(packageProtos.map { it.toByteArray() }) + fragmentNames.add(packageFqNameStr) } val libraryAsByteArray = libraryProto.build().toByteArray() return LinkData(libraryAsByteArray, fragments, fragmentNames) } } +private const val TOP_LEVEL_DECLARATION_COUNT_PER_FILE = 128 +private const val TOP_LEVEL_CLASS_DECLARATION_COUNT_PER_FILE = 64 diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt index e91c8a18c23..767c43c7e8e 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanClassDataFinder.kt @@ -18,8 +18,9 @@ class KonanClassDataFinder( val nameList = proto.classNameList val index = nameList.indexOfFirst { nameResolver.getClassId(it) == classId } - if (index == -1) - error("Could not find serialized class $classId") + if (index == -1) { + return null + } val foundClass = proto.getClasses(index) ?: error("Could not find data for serialized class $classId") diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt index f22189f6b36..80f15b8746a 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/KonanPackageFragment.kt @@ -19,7 +19,8 @@ class KonanPackageFragment( private val library: KonanLibrary, private val packageAccessedHandler: PackageAccessedHandler?, storageManager: StorageManager, - module: ModuleDescriptor + module: ModuleDescriptor, + partName: String ) : DeserializedPackageFragment(fqName, storageManager, module) { lateinit var components: DeserializationComponents @@ -31,7 +32,7 @@ class KonanPackageFragment( // The proto field is lazy so that we can load only needed // packages from the library. private val protoForNames: KonanProtoBuf.LinkDataPackageFragment by lazy { - parsePackageFragment(library.packageMetadata(fqName.asString())) + parsePackageFragment(library.packageMetadata(fqName.asString(), partName)) } val proto: KonanProtoBuf.LinkDataPackageFragment diff --git a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt index 906c59b7f34..4a72c509d0e 100644 --- a/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt +++ b/extracted/konan.serializer/src/org/jetbrains/kotlin/serialization/konan/impl/KonanDeserializedPackageFragmentsFactoryImpl.kt @@ -29,8 +29,12 @@ internal object KonanDeserializedPackageFragmentsFactoryImpl: KonanDeserializedP moduleDescriptor: ModuleDescriptor, packageAccessedHandler: PackageAccessedHandler?, storageManager: StorageManager - ) = packageFragmentNames.map { - KonanPackageFragment(FqName(it), library, packageAccessedHandler, storageManager, moduleDescriptor) + ) = packageFragmentNames.flatMap { + val fqName = FqName(it) + val parts = library.packageMetadataParts(fqName.asString()) + parts.map { partName -> + KonanPackageFragment(fqName, library, packageAccessedHandler, storageManager, moduleDescriptor, partName) + } } override fun createSyntheticPackageFragments( diff --git a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/KlibPrinter.kt b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/KlibPrinter.kt index f269b64f387..0af1d70f34e 100644 --- a/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/KlibPrinter.kt +++ b/klib/src/main/kotlin/org/jetbrains/kotlin/cli/klib/KlibPrinter.kt @@ -23,7 +23,8 @@ class KlibPrinter(out: Appendable) { get() = kind == CallableMemberDescriptor.Kind.FAKE_OVERRIDE val DeclarationDescriptor.shouldBePrinted: Boolean - get() = this is ClassDescriptor && isPublicOrProtected || this is CallableMemberDescriptor && isPublicOrProtected && !isFakeOverride + get() = this is ClassifierDescriptorWithTypeParameters && isPublicOrProtected + || this is CallableMemberDescriptor && isPublicOrProtected && !isFakeOverride private fun Printer.printBody(header: CharSequence, block: () -> Unit) { println() @@ -35,7 +36,7 @@ class KlibPrinter(out: Appendable) { println() } - private fun ClassDescriptor.render(): String { + private fun ClassifierDescriptorWithTypeParameters.render(): String { val renderer = when (modality) { // Don't render 'final' modality Modality.FINAL -> Renderers.WITHOUT_MODALITY @@ -131,6 +132,10 @@ class KlibPrinter(out: Appendable) { override fun visitConstructorDescriptor(descriptor: ConstructorDescriptor, data: Unit) { printer.println(descriptor.render()) } + + override fun visitTypeAliasDescriptor(descriptor: TypeAliasDescriptor, data: Unit) { + printer.println(descriptor.render()) + } } object Renderers { diff --git a/klib/src/test/kotlin/org/jetbrains/kotlin/cli/klib/test/ContentsTest.kt b/klib/src/test/kotlin/org/jetbrains/kotlin/cli/klib/test/ContentsTest.kt index 434dc3f02f0..420a994c1fd 100644 --- a/klib/src/test/kotlin/org/jetbrains/kotlin/cli/klib/test/ContentsTest.kt +++ b/klib/src/test/kotlin/org/jetbrains/kotlin/cli/klib/test/ContentsTest.kt @@ -39,6 +39,12 @@ class ContentsTest { @Test fun topLevelFunctions() = klibContents(testLibrary("TopLevelFunctions")) { """ + package { + annotation class A constructor() : Annotation + annotation class B constructor() : Annotation + class Foo constructor() + } + package { @A @B fun a() fun f1(x: Foo) @@ -56,9 +62,6 @@ class ContentsTest { inline fun t4(x: T) fun t5(x: T) fun Foo.e() - annotation class A constructor() : Annotation - annotation class B constructor() : Annotation - class Foo constructor() } """.trimIndent() } @@ -285,6 +288,74 @@ class ContentsTest { """.trimIndent() } + @Test + fun topLevelPropertiesCustomPackage() = klibContents(testLibrary("TopLevelPropertiesCustomPackage")) { + """ + package custom.pkg { + typealias MyTransformer = (String) -> Int + } + + package custom.pkg { + val v1: Int = 1 + val v2: String = "hello" + val v3: (String) -> Int + val v4: MyTransformer /* = (String) -> Int */ + } + """.trimIndent() + } + + @Test + fun topLevelPropertiesRootPackage() = klibContents(testLibrary("TopLevelPropertiesRootPackage")) { + """ + package { + typealias MyTransformer = (String) -> Int + } + + package { + val v1: Int = 1 + val v2: String = "hello" + val v3: (String) -> Int + val v4: MyTransformer /* = (String) -> Int */ + } + """.trimIndent() + } + + @Test + fun topLevelPropertiesWithClassesCustomPackage() = klibContents(testLibrary("TopLevelPropertiesWithClassesCustomPackage")) { + """ + package custom.pkg { + object Bar + class Foo constructor() + typealias MyTransformer = (String) -> Int + } + + package custom.pkg { + val v1: Int = 1 + val v2: String = "hello" + val v3: (String) -> Int + val v4: MyTransformer /* = (String) -> Int */ + } + """.trimIndent() + } + + @Test + fun topLevelPropertiesWithClassesRootPackage() = klibContents(testLibrary("TopLevelPropertiesWithClassesRootPackage")) { + """ + package { + object Bar + class Foo constructor() + typealias MyTransformer = (String) -> Int + } + + package { + val v1: Int = 1 + val v2: String = "hello" + val v3: (String) -> Int + val v4: MyTransformer /* = (String) -> Int */ + } + """.trimIndent() + } + companion object { val LIBRARY_DIRECTORY = Paths.get("build/konan/libs").resolve( HostManager.hostName) } diff --git a/klib/src/test/testData/TopLevelPropertiesCustomPackage.kt b/klib/src/test/testData/TopLevelPropertiesCustomPackage.kt new file mode 100644 index 00000000000..a31ce844488 --- /dev/null +++ b/klib/src/test/testData/TopLevelPropertiesCustomPackage.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UNUSED_PARAMETER") + +package custom.pkg + +typealias MyTransformer = (String) -> Int + +// top-level properties +val v1 = 1 +val v2 = "hello" +val v3: (String) -> Int = { it.length } +val v4: MyTransformer = v3 + diff --git a/klib/src/test/testData/TopLevelPropertiesRootPackage.kt b/klib/src/test/testData/TopLevelPropertiesRootPackage.kt new file mode 100644 index 00000000000..1937b6b6aa1 --- /dev/null +++ b/klib/src/test/testData/TopLevelPropertiesRootPackage.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UNUSED_PARAMETER") + +typealias MyTransformer = (String) -> Int + +// top-level properties +val v1 = 1 +val v2 = "hello" +val v3: (String) -> Int = { it.length } +val v4: MyTransformer = v3 + diff --git a/klib/src/test/testData/TopLevelPropertiesWithClassesCustomPackage.kt b/klib/src/test/testData/TopLevelPropertiesWithClassesCustomPackage.kt new file mode 100644 index 00000000000..49b1fc1c07c --- /dev/null +++ b/klib/src/test/testData/TopLevelPropertiesWithClassesCustomPackage.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UNUSED_PARAMETER") + +package custom.pkg + +class Foo + +typealias MyTransformer = (String) -> Int + +// top-level properties +val v1 = 1 +val v2 = "hello" +val v3: (String) -> Int = { it.length } +val v4: MyTransformer = v3 + +object Bar diff --git a/klib/src/test/testData/TopLevelPropertiesWithClassesRootPackage.kt b/klib/src/test/testData/TopLevelPropertiesWithClassesRootPackage.kt new file mode 100644 index 00000000000..87e367adadb --- /dev/null +++ b/klib/src/test/testData/TopLevelPropertiesWithClassesRootPackage.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2010-2018 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("UNUSED_PARAMETER") + +class Foo + +typealias MyTransformer = (String) -> Int + +// top-level properties +val v1 = 1 +val v2 = "hello" +val v3: (String) -> Int = { it.length } +val v4: MyTransformer = v3 + +object Bar diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt index 44786fa2e73..f519d170979 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibrary.kt @@ -40,7 +40,8 @@ interface KonanLibrary { val dataFlowGraph: ByteArray? val moduleHeaderData: ByteArray - fun packageMetadata(fqName: String): ByteArray + fun packageMetadataParts(fqName: String): Set + fun packageMetadata(fqName: String, partName: String): ByteArray } val KonanLibrary.uniqueName @@ -63,7 +64,6 @@ val KonanLibrary.exportForwardDeclarations .split(' ').asSequence() .map { it.trim() } .filter { it.isNotEmpty() } - .map { it } .toList() val KonanLibrary.includedHeaders diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt index 79b653ff433..ad8fb71f9e0 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryLayout.kt @@ -39,6 +39,9 @@ interface KonanLibraryLayout { val dataFlowGraphFile get() = File(linkdataDir, "module_data_flow_graph") - fun packageFile(packageName: String) - = File(linkdataDir, if (packageName == "") "root_package.knm" else "package_$packageName.knm") + fun packageFragmentsDir(packageName: String) + = File(linkdataDir, if (packageName == "") "root_package" else "package_$packageName") + + fun packageFragmentFile(packageFqName: String, partName: String) = + File(packageFragmentsDir(packageFqName), "$partName$KLIB_METADATA_FILE_EXTENSION_WITH_DOT") } diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt index ebae056a999..d445fd20c0d 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanLibraryUtils.kt @@ -8,8 +8,11 @@ import org.jetbrains.kotlin.konan.library.impl.KonanLibraryImpl import org.jetbrains.kotlin.konan.library.impl.zippedKonanLibraryChecks import org.jetbrains.kotlin.konan.target.KonanTarget -val KLIB_FILE_EXTENSION = "klib" -val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION" +const val KLIB_FILE_EXTENSION = "klib" +const val KLIB_FILE_EXTENSION_WITH_DOT = ".$KLIB_FILE_EXTENSION" + +const val KLIB_METADATA_FILE_EXTENSION = "knm" +const val KLIB_METADATA_FILE_EXTENSION_WITH_DOT = ".$KLIB_METADATA_FILE_EXTENSION" fun File.unpackZippedKonanLibraryTo(newDir: File) { diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt index 2636010de06..57ae74f6e12 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/KonanMetadataReader.kt @@ -2,5 +2,5 @@ package org.jetbrains.kotlin.konan.library interface MetadataReader { fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray - fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String): ByteArray + fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String, partName: String): ByteArray } diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt index 564c6c6c4a4..f45a60f3272 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/DefaultMetadataReaderImpl.kt @@ -8,6 +8,6 @@ internal object DefaultMetadataReaderImpl : MetadataReader { override fun loadSerializedModule(libraryLayout: KonanLibraryLayout): ByteArray = libraryLayout.moduleHeaderFile.readBytes() - override fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String): ByteArray = - libraryLayout.packageFile(fqName).readBytes() + override fun loadSerializedPackageFragment(libraryLayout: KonanLibraryLayout, fqName: String, partName: String): ByteArray = + libraryLayout.packageFragmentFile(fqName, partName).readBytes() } diff --git a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt index ce5512f9692..9743695b60d 100644 --- a/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt +++ b/shared/src/library/kotlin/org/jetbrains/kotlin/konan/library/impl/KonanLibraryImpl.kt @@ -50,7 +50,24 @@ class KonanLibraryImpl( override val moduleHeaderData: ByteArray by lazy { layout.inPlace { metadataReader.loadSerializedModule(it) } } - override fun packageMetadata(fqName: String) = layout.inPlace { metadataReader.loadSerializedPackageFragment(it, fqName) } + override fun packageMetadata(packageFqName: String, partName: String) = + layout.inPlace { metadataReader.loadSerializedPackageFragment(it, packageFqName, partName) } + + override fun packageMetadataParts(fqName: String): Set = + layout.inPlace { inPlaceLayout -> + val fileList = + inPlaceLayout.packageFragmentsDir(fqName) + .listFiles + .mapNotNull { + it.name + .substringBeforeLast(KLIB_METADATA_FILE_EXTENSION_WITH_DOT, missingDelimiterValue = "") + .takeIf { it.isNotEmpty() } + } + + fileList.toSortedSet().also { + require(it.size == fileList.size) { "Duplicated names: ${fileList.groupingBy { it }.eachCount().filter { (_, count) -> count > 1 }}" } + } + } override fun toString() = "$libraryName[default=$isDefault]" } \ No newline at end of file