diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt index 86a2ae33d43..81466d839b2 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/impl/JvmMetadataExtensions.kt @@ -48,6 +48,9 @@ internal class JvmMetadataExtensions : MetadataExtensions { ext.visitEnd() } + // ModuleFragment is not used by JVM backend. + override fun readModuleFragmentExtensions(v: KmModuleFragmentVisitor, proto: ProtoBuf.PackageFragment, c: ReadContext) {} + override fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, c: ReadContext) { val ext = v.visitExtensions(JvmFunctionExtensionVisitor.TYPE) as? JvmFunctionExtensionVisitor ?: return ext.visit(JvmProtoBufUtil.getJvmMethodSignature(proto, c.strings, c.types)?.wrapAsPublic()) @@ -144,6 +147,13 @@ internal class JvmMetadataExtensions : MetadataExtensions { } } + // PackageFragment is not used by JVM backend. + override fun writeModuleFragmentExtensions( + type: KmExtensionType, + proto: ProtoBuf.PackageFragment.Builder, + c: WriteContext + ): KmModuleFragmentExtensionVisitor? = null + override fun writeFunctionExtensions( type: KmExtensionType, proto: ProtoBuf.Function.Builder, c: WriteContext ): KmFunctionExtensionVisitor? { @@ -262,6 +272,9 @@ internal class JvmMetadataExtensions : MetadataExtensions { override fun createPackageExtension(): KmPackageExtension = JvmPackageExtension() + override fun createModuleFragmentExtensions(): KmModuleFragmentExtension = + error("metadata-jvm doesn't have any extensions for module fragment!") + override fun createFunctionExtension(): KmFunctionExtension = JvmFunctionExtension() override fun createPropertyExtension(): KmPropertyExtension = JvmPropertyExtension() diff --git a/libraries/kotlinx-metadata/klib/build.gradle.kts b/libraries/kotlinx-metadata/klib/build.gradle.kts new file mode 100644 index 00000000000..e1ebed69985 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/build.gradle.kts @@ -0,0 +1,69 @@ +import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar + +description = "Kotlin Library (KLIB) metadata manipulation library" + +plugins { + kotlin("jvm") + id("jps-compatible") +} + +group = "org.jetbrains.kotlinx" + +val deployVersion = findProperty("kotlinxMetadataKlibDeployVersion") as String? +version = deployVersion ?: "0.0.1-SNAPSHOT" + +jvmTarget = "1.6" +javaHome = rootProject.extra["JDK_16"] as String + +sourceSets { + "main" { projectDefault() } +} + +val shadows by configurations.creating { + isTransitive = false +} +configurations.getByName("compileOnly").extendsFrom(shadows) +configurations.getByName("testCompile").extendsFrom(shadows) + +dependencies { + compile(kotlinStdlib()) + shadows(project(":kotlinx-metadata")) + shadows(project(":core:metadata")) + shadows(project(":compiler:serialization")) + shadows(project(":kotlin-util-klib-metadata")) + shadows(project(":kotlin-util-klib")) + shadows(protobufLite()) +} + +if (deployVersion != null) { + publish() +} + +noDefaultJar() + +tasks.register("shadowJar") { + callGroovy("manifestAttributes", manifest, project) + manifest.attributes["Implementation-Version"] = version + + from(mainSourceSet.output) + exclude("**/*.proto") + configurations = listOf(shadows) + relocate("org.jetbrains.kotlin", "kotlinx.metadata.internal") + + val artifactRef = outputs.files.singleFile + runtimeJarArtifactBy(this, artifactRef) + addArtifact("runtime", this, artifactRef) +} + +sourcesJar { + for (dependency in shadows.dependencies) { + if (dependency is ProjectDependency) { + val javaPlugin = dependency.dependencyProject.convention.findPlugin(JavaPluginConvention::class.java) + if (javaPlugin != null) { + from(javaPlugin.sourceSets["main"].allSource) + } + } + } +} + +javadocJar() \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/resources/META-INF/services/kotlinx.metadata.impl.extensions.MetadataExtensions b/libraries/kotlinx-metadata/klib/resources/META-INF/services/kotlinx.metadata.impl.extensions.MetadataExtensions new file mode 100644 index 00000000000..71c370e6b6d --- /dev/null +++ b/libraries/kotlinx-metadata/klib/resources/META-INF/services/kotlinx.metadata.impl.extensions.MetadataExtensions @@ -0,0 +1 @@ +kotlinx.metadata.klib.impl.KlibMetadataExtensions \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibHeader.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibHeader.kt new file mode 100644 index 00000000000..ae851e973b1 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibHeader.kt @@ -0,0 +1,16 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib + +import kotlinx.metadata.KmAnnotation + +class KlibHeader( + val moduleName: String, + val file: List, + val packageFragmentName: List, + val emptyPackage: List, + val annotation: List +) diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibModuleMetadata.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibModuleMetadata.kt new file mode 100644 index 00000000000..bd18db830ef --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibModuleMetadata.kt @@ -0,0 +1,137 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib + +import kotlinx.metadata.KmAnnotation +import kotlinx.metadata.KmModuleFragment +import kotlinx.metadata.impl.WriteContext +import kotlinx.metadata.impl.accept +import kotlinx.metadata.klib.impl.* +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataStringTable +import org.jetbrains.kotlin.library.metadata.parseModuleHeader +import org.jetbrains.kotlin.library.metadata.parsePackageFragment +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl + +/** + * Allows to modify the way fragments of the single package are read by [KlibModuleMetadata.read]. + * For example, it may be convenient to join fragments into a single one. + */ +interface KlibModuleFragmentReadStrategy { + fun processModuleParts(parts: List): List + + companion object { + val DEFAULT = object : KlibModuleFragmentReadStrategy { + override fun processModuleParts(parts: List) = + parts + } + } +} + +/** + * Allows to modify the way module fragments are written by [KlibModuleMetadata.write]. + * For example, splitting big fragments into several small one allows to improve IDE performance. + */ +interface KlibModuleFragmentWriteStrategy { + fun processPackageParts(parts: List): List + + companion object { + val DEFAULT = object : KlibModuleFragmentWriteStrategy { + override fun processPackageParts(parts: List): List = + parts + } + } +} + +/** + * Represents the parsed metadata of KLIB. + */ +class KlibModuleMetadata( + val name: String, + val fragments: List, + val annotations: List +) { + + /** + * Serialized representation of module metadata. + */ + class SerializedKlibMetadata( + val header: ByteArray, + val fragments: List>, + val fragmentNames: List + ) + + /** + * Specifies access to library's metadata. + */ + interface MetadataLibraryProvider { + val moduleHeaderData: ByteArray + fun packageMetadataParts(fqName: String): Set + fun packageMetadata(fqName: String, partName: String): ByteArray + } + + companion object { + /** + * Deserializes metadata from the given [library]. + * @param readStrategy specifies the way module fragments of a single package are modified (e.g. merged) after deserialization. + */ + fun read( + library: MetadataLibraryProvider, + readStrategy: KlibModuleFragmentReadStrategy = KlibModuleFragmentReadStrategy.DEFAULT + ): KlibModuleMetadata { + val moduleHeaderProto = parseModuleHeader(library.moduleHeaderData) + val nameResolver = NameResolverImpl(moduleHeaderProto.strings, moduleHeaderProto.qualifiedNames) + val moduleHeader = moduleHeaderProto.readHeader(nameResolver) + val fileIndex = SourceFileIndexReadExtension(moduleHeader.file) + val moduleFragments = moduleHeader.packageFragmentName.flatMap { packageFqName -> + library.packageMetadataParts(packageFqName).map { part -> + val packageFragment = parsePackageFragment(library.packageMetadata(packageFqName, part)) + KmModuleFragment().apply { packageFragment.accept(this, nameResolver, listOf(fileIndex)) } + }.let(readStrategy::processModuleParts) + } + return KlibModuleMetadata(moduleHeader.moduleName, moduleFragments, moduleHeader.annotation) + } + } + + /** + * Writes metadata back to serialized representation. + * @param writeStrategy specifies the way module fragments are modified (e.g. split) before serialization. + */ + fun write( + writeStrategy: KlibModuleFragmentWriteStrategy = KlibModuleFragmentWriteStrategy.DEFAULT + ): SerializedKlibMetadata { + val reverseIndex = ReverseSourceFileIndexWriteExtension() + val c = WriteContext(KlibMetadataStringTable(), listOf(reverseIndex)) + + val groupedFragments = fragments + .groupBy(KmModuleFragment::fqNameOrFail) + .mapValues { writeStrategy.processPackageParts(it.value) } + + val header = KlibHeader( + name, + reverseIndex.fileIndex, + groupedFragments.map { it.key }, + groupedFragments.filter { it.value.all(KmModuleFragment::isEmpty) }.map { it.key }, + annotations + ) + val groupedProtos = groupedFragments.mapValues { (_, fragments) -> + fragments.map { + KlibModuleFragmentWriter(c.strings as KlibMetadataStringTable, c.contextExtensions).also(it::accept).write() + } + } + return SerializedKlibMetadata( + header.writeHeader(c).build().toByteArray(), + groupedProtos.map { it.value.map(ProtoBuf.PackageFragment::toByteArray) }, + header.packageFragmentName + ) + } +} + +private fun KmModuleFragment.fqNameOrFail(): String = + fqName ?: error("Module fragment must have a fully-qualified name.") + +private fun KmModuleFragment.isEmpty(): Boolean = + classes.isEmpty() && (pkg?.let { it.functions.isEmpty() && it.properties.isEmpty() && it.typeAliases.isEmpty() } ?: true) \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibSourceFile.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibSourceFile.kt new file mode 100644 index 00000000000..cb233be369a --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/KlibSourceFile.kt @@ -0,0 +1,11 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib + +class KlibSourceFile( + val name: String +) + diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/UniqId.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/UniqId.kt new file mode 100644 index 00000000000..78285629523 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/UniqId.kt @@ -0,0 +1,8 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib + +class UniqId(val index: Long) \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibExtensionNodes.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibExtensionNodes.kt new file mode 100644 index 00000000000..635fb3e790d --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibExtensionNodes.kt @@ -0,0 +1,224 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib.impl + +import kotlinx.metadata.* +import kotlinx.metadata.impl.extensions.* +import kotlinx.metadata.klib.* + +internal val KmFunction.klibExtensions: KlibFunctionExtension + get() = visitExtensions(KlibFunctionExtensionVisitor.TYPE) as KlibFunctionExtension + +internal val KmClass.klibExtensions: KlibClassExtension + get() = visitExtensions(KlibClassExtensionVisitor.TYPE) as KlibClassExtension + +internal val KmType.klibExtensions: KlibTypeExtension + get() = visitExtensions(KlibTypeExtensionVisitor.TYPE) as KlibTypeExtension + +internal val KmProperty.klibExtensions: KlibPropertyExtension + get() = visitExtensions(KlibPropertyExtensionVisitor.TYPE) as KlibPropertyExtension + +internal val KmConstructor.klibExtensions: KlibConstructorExtension + get() = visitExtensions(KlibConstructorExtensionVisitor.TYPE) as KlibConstructorExtension + +internal val KmTypeParameter.klibExtensions: KlibTypeParameterExtension + get() = visitExtensions(KlibTypeParameterExtensionVisitor.TYPE) as KlibTypeParameterExtension + +internal val KmPackage.klibExtensions: KlibPackageExtension + get() = visitExtensions(KlibPackageExtensionVisitor.TYPE) as KlibPackageExtension + +internal val KmModuleFragment.klibExtensions: KlibModuleFragmentExtension + get() = visitExtensions(KlibModuleFragmentExtensionVisitor.TYPE) as KlibModuleFragmentExtension + +internal class KlibFunctionExtension : KlibFunctionExtensionVisitor(), KmFunctionExtension { + + val annotations: MutableList = mutableListOf() + var uniqId: UniqId? = null + var file: KlibSourceFile? = null + + override fun visitUniqId(uniqId: UniqId) { + this.uniqId = uniqId + } + + override fun visitAnnotation(annotation: KmAnnotation) { + annotations += annotation + } + + override fun visitFile(file: KlibSourceFile) { + this.file = file + } + + override fun accept(visitor: KmFunctionExtensionVisitor) { + require(visitor is KlibFunctionExtensionVisitor) + annotations.forEach(visitor::visitAnnotation) + uniqId?.let(visitor::visitUniqId) + file?.let(visitor::visitFile) + } +} + +internal class KlibClassExtension : KlibClassExtensionVisitor(), KmClassExtension { + + val annotations: MutableList = mutableListOf() + var uniqId: UniqId? = null + var file: KlibSourceFile? = null + + override fun visitAnnotation(annotation: KmAnnotation) { + annotations += annotation + } + + override fun visitUniqId(uniqId: UniqId) { + this.uniqId = uniqId + } + + override fun visitFile(file: KlibSourceFile) { + this.file = file + } + + override fun accept(visitor: KmClassExtensionVisitor) { + require(visitor is KlibClassExtensionVisitor) + annotations.forEach(visitor::visitAnnotation) + uniqId?.let(visitor::visitUniqId) + file?.let(visitor::visitFile) + } +} + +internal class KlibTypeExtension : KlibTypeExtensionVisitor(), KmTypeExtension { + + val annotations: MutableList = mutableListOf() + + override fun visitAnnotation(annotation: KmAnnotation) { + annotations += annotation + } + + override fun accept(visitor: KmTypeExtensionVisitor) { + require(visitor is KlibTypeExtensionVisitor) + annotations.forEach(visitor::visitAnnotation) + } +} + +internal class KlibPropertyExtension : KlibPropertyExtensionVisitor(), KmPropertyExtension { + + val annotations: MutableList = mutableListOf() + val getterAnnotations: MutableList = mutableListOf() + val setterAnnotations: MutableList = mutableListOf() + var uniqId: UniqId? = null + var file: Int? = null + var compileTimeValue: KmAnnotationArgument<*>? = null + + override fun visitAnnotation(annotation: KmAnnotation) { + annotations += annotation + } + + override fun visitGetterAnnotation(annotation: KmAnnotation) { + getterAnnotations += annotation + } + + override fun visitSetterAnnotation(annotation: KmAnnotation) { + setterAnnotations += annotation + } + + override fun visitFile(file: Int) { + this.file = file + } + + override fun visitUniqId(uniqId: UniqId) { + this.uniqId = uniqId + } + + override fun visitCompileTimeValue(value: KmAnnotationArgument<*>) { + this.compileTimeValue = value + } + + override fun accept(visitor: KmPropertyExtensionVisitor) { + require(visitor is KlibPropertyExtensionVisitor) + annotations.forEach(visitor::visitAnnotation) + getterAnnotations.forEach(visitor::visitGetterAnnotation) + setterAnnotations.forEach(visitor::visitSetterAnnotation) + file?.let(visitor::visitFile) + uniqId?.let(visitor::visitUniqId) + compileTimeValue?.let(visitor::visitCompileTimeValue) + } +} + +internal class KlibConstructorExtension : KlibConstructorExtensionVisitor(), KmConstructorExtension { + + val annotations: MutableList = mutableListOf() + var uniqId: UniqId? = null + + override fun visitAnnotation(annotation: KmAnnotation) { + annotations += annotation + } + + override fun visitUniqId(uniqId: UniqId) { + this.uniqId = uniqId + } + + override fun accept(visitor: KmConstructorExtensionVisitor) { + require(visitor is KlibConstructorExtensionVisitor) + annotations.forEach(visitor::visitAnnotation) + uniqId?.let(visitor::visitUniqId) + } +} + +internal class KlibTypeParameterExtension : KlibTypeParameterExtensionVisitor(), KmTypeParameterExtension { + + val annotations: MutableList = mutableListOf() + var uniqId: UniqId? = null + + override fun visitAnnotation(annotation: KmAnnotation) { + annotations += annotation + } + + override fun visitUniqId(uniqId: UniqId) { + this.uniqId = uniqId + } + + override fun accept(visitor: KmTypeParameterExtensionVisitor) { + require(visitor is KlibTypeParameterExtensionVisitor) + annotations.forEach(visitor::visitAnnotation) + uniqId?.let(visitor::visitUniqId) + } +} + +internal class KlibPackageExtension : KlibPackageExtensionVisitor(), KmPackageExtension { + + var fqName: String? = null + + override fun visitFqName(name: String) { + fqName = name + } + + override fun accept(visitor: KmPackageExtensionVisitor) { + require(visitor is KlibPackageExtensionVisitor) + fqName?.let(visitor::visitFqName) + } +} + +internal class KlibModuleFragmentExtension : KlibModuleFragmentExtensionVisitor(), KmModuleFragmentExtension { + + val moduleFragmentFiles: MutableList = ArrayList() + var fqName: String? = null + val className: MutableList = ArrayList() + + override fun visitFile(file: KlibSourceFile) { + moduleFragmentFiles += file + } + + override fun visitFqName(fqName: String) { + this.fqName = fqName + } + + override fun visitClassName(className: ClassName) { + this.className += className + } + + override fun accept(visitor: KmModuleFragmentExtensionVisitor) { + require(visitor is KlibModuleFragmentExtensionVisitor) + moduleFragmentFiles.forEach(visitor::visitFile) + fqName?.let(visitor::visitFqName) + className.forEach(visitor::visitClassName) + } +} \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibMetadataExtensions.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibMetadataExtensions.kt new file mode 100644 index 00000000000..13dce51f84b --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibMetadataExtensions.kt @@ -0,0 +1,350 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib.impl + +import kotlinx.metadata.* +import kotlinx.metadata.impl.* +import kotlinx.metadata.impl.extensions.* +import kotlinx.metadata.klib.* +import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolverImpl +import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull +import org.jetbrains.kotlin.name.ClassId +import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.serialization.StringTableImpl + +internal class KlibMetadataExtensions : MetadataExtensions { + + private fun ReadContext.getSourceFile(index: Int) = + contextExtensions.filterIsInstance().first().getSourceFile(index) + + private fun WriteContext.getIndexOf(file: KlibSourceFile) = + contextExtensions.filterIsInstance().first().getIndexOf(file) + + override fun readClassExtensions(v: KmClassVisitor, proto: ProtoBuf.Class, c: ReadContext) { + val extension = v.visitExtensions(KlibClassExtensionVisitor.TYPE) as? KlibClassExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.classAnnotation).forEach { annotation -> + extension.visitAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.classUniqId)?.let { descriptorUniqId -> + extension.visitUniqId(descriptorUniqId.readUniqId()) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.classFile)?.let { + extension.visitFile(c.getSourceFile(it)) + } + } + + override fun readPackageExtensions(v: KmPackageVisitor, proto: ProtoBuf.Package, c: ReadContext) { + val extension = v.visitExtensions(KlibPackageExtensionVisitor.TYPE) as? KlibPackageExtensionVisitor ?: return + + proto.getExtensionOrNull(KlibMetadataProtoBuf.packageFqName)?.let { + val fqName = (c.strings as NameResolverImpl).getPackageFqName(it) + extension.visitFqName(fqName) + } + } + + override fun readModuleFragmentExtensions(v: KmModuleFragmentVisitor, proto: ProtoBuf.PackageFragment, c: ReadContext) { + val extension = v.visitExtensions(KlibModuleFragmentExtensionVisitor.TYPE) as? KlibModuleFragmentExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.packageFragmentFiles) + .map { c.getSourceFile(it) } + .forEach(extension::visitFile) + proto.getExtensionOrNull(KlibMetadataProtoBuf.fqName)?.let(extension::visitFqName) + proto.getExtension(KlibMetadataProtoBuf.className) + .map(c.strings::getQualifiedClassName) + .forEach(extension::visitClassName) + } + + override fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, c: ReadContext) { + val extension = v.visitExtensions(KlibFunctionExtensionVisitor.TYPE) as? KlibFunctionExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.functionAnnotation).forEach { annotation -> + extension.visitAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.functionUniqId)?.let { descriptorUniqId -> + extension.visitUniqId(descriptorUniqId.readUniqId()) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.functionFile)?.let { + val file = c.getSourceFile(it) + extension.visitFile(file) + } + } + + override fun readPropertyExtensions(v: KmPropertyVisitor, proto: ProtoBuf.Property, c: ReadContext) { + val extension = v.visitExtensions(KlibPropertyExtensionVisitor.TYPE) as? KlibPropertyExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.propertyAnnotation).forEach { annotation -> + extension.visitAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtension(KlibMetadataProtoBuf.propertyGetterAnnotation).forEach { annotation -> + extension.visitGetterAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtension(KlibMetadataProtoBuf.propertySetterAnnotation).forEach { annotation -> + extension.visitSetterAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.propertyUniqId)?.let { descriptorUniqId -> + extension.visitUniqId(descriptorUniqId.readUniqId()) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.propertyFile)?.let(extension::visitFile) + proto.getExtensionOrNull(KlibMetadataProtoBuf.compileTimeValue)?.let { value -> + value.readAnnotationArgument(c.strings)?.let { extension.visitCompileTimeValue(it) } + } + } + + override fun readConstructorExtensions(v: KmConstructorVisitor, proto: ProtoBuf.Constructor, c: ReadContext) { + val extension = v.visitExtensions(KlibConstructorExtensionVisitor.TYPE) as? KlibConstructorExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.constructorAnnotation).forEach { annotation -> + extension.visitAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.constructorUniqId)?.let { descriptorUniqId -> + extension.visitUniqId(descriptorUniqId.readUniqId()) + } + } + + override fun readTypeParameterExtensions(v: KmTypeParameterVisitor, proto: ProtoBuf.TypeParameter, c: ReadContext) { + val extension = v.visitExtensions(KlibTypeParameterExtensionVisitor.TYPE) as? KlibTypeParameterExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.typeParameterAnnotation).forEach { annotation -> + extension.visitAnnotation(annotation.readAnnotation(c.strings)) + } + proto.getExtensionOrNull(KlibMetadataProtoBuf.typeParamUniqId)?.let { descriptorUniqId -> + extension.visitUniqId(descriptorUniqId.readUniqId()) + } + } + + override fun readTypeExtensions(v: KmTypeVisitor, proto: ProtoBuf.Type, c: ReadContext) { + val extension = v.visitExtensions(KlibTypeExtensionVisitor.TYPE) as? KlibTypeExtensionVisitor ?: return + + proto.getExtension(KlibMetadataProtoBuf.typeAnnotation).forEach { annotation -> + extension.visitAnnotation(annotation.readAnnotation(c.strings)) + } + } + + override fun writeClassExtensions(type: KmExtensionType, proto: ProtoBuf.Class.Builder, c: WriteContext): KmClassExtensionVisitor? { + if (type != KlibClassExtensionVisitor.TYPE) return null + return object : KlibClassExtensionVisitor() { + override fun visitAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.classAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitUniqId(uniqId: UniqId) { + proto.setExtension( + KlibMetadataProtoBuf.classUniqId, + uniqId.writeUniqId().build() + ) + } + + override fun visitFile(file: KlibSourceFile) { + val fileIdx = c.getIndexOf(file) + proto.setExtension(KlibMetadataProtoBuf.classFile, fileIdx) + } + } + } + + override fun writePackageExtensions( + type: KmExtensionType, + proto: ProtoBuf.Package.Builder, + c: WriteContext + ): KmPackageExtensionVisitor? { + if (type != KlibPackageExtensionVisitor.TYPE) return null + return object : KlibPackageExtensionVisitor() { + override fun visitFqName(name: String) { + val nameIdx = (c.strings as StringTableImpl).getPackageFqNameIndex(FqName(name)) + proto.setExtension(KlibMetadataProtoBuf.packageFqName, nameIdx) + } + } + } + + override fun writeModuleFragmentExtensions( + type: KmExtensionType, + proto: ProtoBuf.PackageFragment.Builder, + c: WriteContext + ): KmModuleFragmentExtensionVisitor? { + if (type != KlibModuleFragmentExtensionVisitor.TYPE) return null + return object : KlibModuleFragmentExtensionVisitor() { + override fun visitFile(file: KlibSourceFile) { + val fileIdx = c.getIndexOf(file) + proto.addExtension(KlibMetadataProtoBuf.packageFragmentFiles, fileIdx) + } + + override fun visitFqName(fqName: String) { + proto.setExtension(KlibMetadataProtoBuf.fqName, fqName) + } + + override fun visitClassName(className: ClassName) { + val classNameIdx = (c.strings as StringTableImpl).getQualifiedClassNameIndex(ClassId.fromString(className)) + proto.addExtension(KlibMetadataProtoBuf.className, classNameIdx) + } + } + } + + override fun writeFunctionExtensions( + type: KmExtensionType, + proto: ProtoBuf.Function.Builder, + c: WriteContext + ): KmFunctionExtensionVisitor? { + if (type != KlibFunctionExtensionVisitor.TYPE) return null + return object : KlibFunctionExtensionVisitor() { + override fun visitAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.functionAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitUniqId(uniqId: UniqId) { + proto.setExtension( + KlibMetadataProtoBuf.functionUniqId, + uniqId.writeUniqId().build() + ) + } + + override fun visitFile(file: KlibSourceFile) { + val index = c.getIndexOf(file) + proto.setExtension(KlibMetadataProtoBuf.functionFile, index) + } + } + } + + override fun writePropertyExtensions( + type: KmExtensionType, + proto: ProtoBuf.Property.Builder, + c: WriteContext + ): KmPropertyExtensionVisitor? { + if (type != KlibPropertyExtensionVisitor.TYPE) return null + return object : KlibPropertyExtensionVisitor() { + override fun visitAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.propertyAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitGetterAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.propertyGetterAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitSetterAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.propertySetterAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitUniqId(uniqId: UniqId) { + proto.setExtension( + KlibMetadataProtoBuf.propertyUniqId, + uniqId.writeUniqId().build() + ) + } + + override fun visitFile(file: Int) { + proto.setExtension( + KlibMetadataProtoBuf.propertyFile, + file + ) + } + + override fun visitCompileTimeValue(value: KmAnnotationArgument<*>) { + proto.setExtension( + KlibMetadataProtoBuf.compileTimeValue, + value.writeAnnotationArgument(c.strings).build() + ) + } + } + } + + override fun writeConstructorExtensions( + type: KmExtensionType, + proto: ProtoBuf.Constructor.Builder, + c: WriteContext + ): KmConstructorExtensionVisitor? { + if (type != KlibConstructorExtensionVisitor.TYPE) return null + return object : KlibConstructorExtensionVisitor() { + override fun visitAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.constructorAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitUniqId(uniqId: UniqId) { + proto.setExtension( + KlibMetadataProtoBuf.constructorUniqId, + uniqId.writeUniqId().build() + ) + } + } + } + + override fun writeTypeParameterExtensions( + type: KmExtensionType, + proto: ProtoBuf.TypeParameter.Builder, + c: WriteContext + ): KmTypeParameterExtensionVisitor? { + if (type != KlibTypeParameterExtensionVisitor.TYPE) return null + return object : KlibTypeParameterExtensionVisitor() { + override fun visitAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.typeParameterAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + + override fun visitUniqId(uniqId: UniqId) { + proto.setExtension( + KlibMetadataProtoBuf.typeParamUniqId, + uniqId.writeUniqId().build() + ) + } + } + } + + override fun writeTypeExtensions(type: KmExtensionType, proto: ProtoBuf.Type.Builder, c: WriteContext): KmTypeExtensionVisitor? { + if (type != KlibTypeExtensionVisitor.TYPE) return null + return object : KlibTypeExtensionVisitor() { + override fun visitAnnotation(annotation: KmAnnotation) { + proto.addExtension( + KlibMetadataProtoBuf.typeAnnotation, + annotation.writeAnnotation(c.strings).build() + ) + } + } + } + + override fun createClassExtension(): KmClassExtension = + KlibClassExtension() + + override fun createPackageExtension(): KmPackageExtension = + KlibPackageExtension() + + override fun createModuleFragmentExtensions(): KmModuleFragmentExtension = + KlibModuleFragmentExtension() + + override fun createFunctionExtension(): KmFunctionExtension = + KlibFunctionExtension() + + override fun createPropertyExtension(): KmPropertyExtension = + KlibPropertyExtension() + + override fun createConstructorExtension(): KmConstructorExtension = + KlibConstructorExtension() + + override fun createTypeParameterExtension(): KmTypeParameterExtension = + KlibTypeParameterExtension() + + override fun createTypeExtension(): KmTypeExtension = + KlibTypeExtension() +} \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibReadUtils.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibReadUtils.kt new file mode 100644 index 00000000000..6b8fa5945e2 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibReadUtils.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib.impl + +import kotlinx.metadata.impl.readAnnotation +import kotlinx.metadata.klib.KlibHeader +import kotlinx.metadata.klib.KlibSourceFile +import kotlinx.metadata.klib.UniqId +import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf +import org.jetbrains.kotlin.metadata.deserialization.NameResolver + +internal fun KlibMetadataProtoBuf.DescriptorUniqId.readUniqId(): UniqId = + UniqId(index) + +internal fun KlibMetadataProtoBuf.Header.readHeader(strings: NameResolver): KlibHeader = + KlibHeader( + moduleName, + fileList.map(KlibMetadataProtoBuf.File::readFile), + packageFragmentNameList, + emptyPackageList, + annotationList.map { it.readAnnotation(strings) } + ) + +internal fun KlibMetadataProtoBuf.File.readFile(): KlibSourceFile = + KlibSourceFile(name) diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibReaders.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibReaders.kt new file mode 100644 index 00000000000..00acd678ae8 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibReaders.kt @@ -0,0 +1,15 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib.impl + +import kotlinx.metadata.impl.ReadContextExtension +import kotlinx.metadata.klib.KlibSourceFile + +class SourceFileIndexReadExtension( + private val files: List +) : ReadContextExtension { + fun getSourceFile(index: Int): KlibSourceFile = files[index] +} \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibWriteUtils.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibWriteUtils.kt new file mode 100644 index 00000000000..1a5c21a8cdd --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibWriteUtils.kt @@ -0,0 +1,36 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib.impl + +import kotlinx.metadata.impl.WriteContext +import kotlinx.metadata.impl.writeAnnotation +import kotlinx.metadata.klib.KlibHeader +import kotlinx.metadata.klib.KlibSourceFile +import kotlinx.metadata.klib.UniqId +import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf +import org.jetbrains.kotlin.serialization.StringTableImpl + +internal fun UniqId.writeUniqId(): KlibMetadataProtoBuf.DescriptorUniqId.Builder = + KlibMetadataProtoBuf.DescriptorUniqId.newBuilder().apply { + index = this@writeUniqId.index + } + +internal fun KlibHeader.writeHeader(context: WriteContext): KlibMetadataProtoBuf.Header.Builder = + KlibMetadataProtoBuf.Header.newBuilder().also { proto -> + val (strings, qualifiedNames) = (context.strings as StringTableImpl).buildProto() + proto.moduleName = moduleName + proto.qualifiedNames = qualifiedNames + proto.strings = strings + proto.addAllPackageFragmentName(packageFragmentName) + proto.addAllFile(file.map { it.writeFile().build() }) + proto.addAllAnnotation(annotation.map { it.writeAnnotation(context.strings).build() }) + proto.addAllEmptyPackage(emptyPackage) + } + +internal fun KlibSourceFile.writeFile(): KlibMetadataProtoBuf.File.Builder = + KlibMetadataProtoBuf.File.newBuilder().also { proto -> + proto.name = name + } diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibWriters.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibWriters.kt new file mode 100644 index 00000000000..3c41c0ad853 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/impl/klibWriters.kt @@ -0,0 +1,45 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib.impl + +import kotlinx.metadata.impl.* +import kotlinx.metadata.klib.KlibSourceFile +import org.jetbrains.kotlin.backend.common.serialization.metadata.KlibMetadataStringTable +import org.jetbrains.kotlin.library.metadata.KlibMetadataProtoBuf +import org.jetbrains.kotlin.metadata.ProtoBuf + +class ReverseSourceFileIndexWriteExtension : WriteContextExtension { + private val filesReverseIndex = mutableMapOf() + + val fileIndex: List + get() = filesReverseIndex + .map { (file, index) -> index to file } + .sortedBy { it.first } + .map { it.second } + + fun getIndexOf(file: KlibSourceFile): Int = filesReverseIndex.getOrPut(file) { + filesReverseIndex.size + } +} + +class KlibModuleFragmentWriter( + stringTable: KlibMetadataStringTable, + contextExtensions: List = emptyList() +) : ModuleFragmentWriter(stringTable, contextExtensions) { + + fun write(): ProtoBuf.PackageFragment = + t.build() + + override fun visitEnd() { + val isPackageEmpty = if (t.`package` == null) { + true + } else { + t.`package`.let { it.functionCount == 0 && it.propertyCount == 0 && it.typeAliasCount == 0 } + } + val isEmpty = t.class_Count == 0 && isPackageEmpty + t.setExtension(KlibMetadataProtoBuf.isEmpty, isEmpty) + } +} \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/klibExtensions.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/klibExtensions.kt new file mode 100644 index 00000000000..673c43ab34e --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/klibExtensions.kt @@ -0,0 +1,105 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib + +import kotlinx.metadata.* +import kotlinx.metadata.klib.impl.klibExtensions + +val KmFunction.annotations: MutableList + get() = klibExtensions.annotations + +var KmFunction.uniqId: UniqId? + get() = klibExtensions.uniqId + set(value) { + klibExtensions.uniqId = value + } + +var KmFunction.file: KlibSourceFile? + get() = klibExtensions.file + set(value) { + klibExtensions.file = value + } + +val KmClass.annotations: MutableList + get() = klibExtensions.annotations + +var KmClass.uniqId: UniqId? + get() = klibExtensions.uniqId + set(value) { + klibExtensions.uniqId = value + } + +var KmClass.file: KlibSourceFile? + get() = klibExtensions.file + set(value) { + klibExtensions.file = value + } + +val KmProperty.annotations: MutableList + get() = klibExtensions.annotations + +val KmProperty.setterAnnotations: MutableList + get() = klibExtensions.setterAnnotations + +val KmProperty.getterAnnotations: MutableList + get() = klibExtensions.getterAnnotations + +var KmProperty.uniqId: UniqId? + get() = klibExtensions.uniqId + set(value) { + klibExtensions.uniqId = value + } + +var KmProperty.file: Int? + get() = klibExtensions.file + set(value) { + klibExtensions.file = value + } + +var KmProperty.compileTimeValue: KmAnnotationArgument<*>? + get() = klibExtensions.compileTimeValue + set(value) { + klibExtensions.compileTimeValue = value + } + +val KmType.annotations: MutableList + get() = klibExtensions.annotations + +val KmConstructor.annotations: MutableList + get() = klibExtensions.annotations + +var KmConstructor.uniqId: UniqId? + get() = klibExtensions.uniqId + set(value) { + klibExtensions.uniqId = value + } + +var KmPackage.fqName: String? + get() = klibExtensions.fqName + set(value) { + klibExtensions.fqName = value + } + +var KmModuleFragment.fqName: String? + get() = klibExtensions.fqName + set(value) { + klibExtensions.fqName = value + } + +val KmModuleFragment.className: MutableList? + get() = klibExtensions.className + +val KmModuleFragment.moduleFragmentFiles: MutableList? + get() = klibExtensions.moduleFragmentFiles + +val KmTypeParameter.annotations: MutableList + get() = klibExtensions.annotations + +var KmTypeParameter.uniqId: UniqId? + get() = klibExtensions.uniqId + set(value) { + klibExtensions.uniqId = value + } \ No newline at end of file diff --git a/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/klibMetadataExtensionVisitors.kt b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/klibMetadataExtensionVisitors.kt new file mode 100644 index 00000000000..1c5e479ae09 --- /dev/null +++ b/libraries/kotlinx-metadata/klib/src/kotlinx/metadata/klib/klibMetadataExtensionVisitors.kt @@ -0,0 +1,130 @@ +/* + * Copyright 2010-2019 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 kotlinx.metadata.klib + +import kotlinx.metadata.* + +abstract class KlibFunctionExtensionVisitor : KmFunctionExtensionVisitor { + + abstract fun visitAnnotation(annotation: KmAnnotation) + + abstract fun visitUniqId(uniqId: UniqId) + + abstract fun visitFile(file: KlibSourceFile) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibFunctionExtensionVisitor::class) + } +} + +abstract class KlibClassExtensionVisitor : KmClassExtensionVisitor { + + abstract fun visitAnnotation(annotation: KmAnnotation) + + abstract fun visitUniqId(uniqId: UniqId) + + abstract fun visitFile(file: KlibSourceFile) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibClassExtensionVisitor::class) + } +} + +abstract class KlibTypeExtensionVisitor : KmTypeExtensionVisitor { + + abstract fun visitAnnotation(annotation: KmAnnotation) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibTypeExtensionVisitor::class) + } +} + +abstract class KlibPropertyExtensionVisitor : KmPropertyExtensionVisitor { + + abstract fun visitAnnotation(annotation: KmAnnotation) + + abstract fun visitGetterAnnotation(annotation: KmAnnotation) + + abstract fun visitSetterAnnotation(annotation: KmAnnotation) + + abstract fun visitFile(file: Int) + + abstract fun visitUniqId(uniqId: UniqId) + + abstract fun visitCompileTimeValue(value: KmAnnotationArgument<*>) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibPropertyExtensionVisitor::class) + } +} + +abstract class KlibConstructorExtensionVisitor : KmConstructorExtensionVisitor { + + abstract fun visitAnnotation(annotation: KmAnnotation) + + abstract fun visitUniqId(uniqId: UniqId) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibConstructorExtensionVisitor::class) + } +} + +abstract class KlibTypeParameterExtensionVisitor : KmTypeParameterExtensionVisitor { + + abstract fun visitAnnotation(annotation: KmAnnotation) + + abstract fun visitUniqId(uniqId: UniqId) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibTypeParameterExtensionVisitor::class) + } +} + +abstract class KlibPackageExtensionVisitor : KmPackageExtensionVisitor { + + abstract fun visitFqName(name: String) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibPackageExtensionVisitor::class) + } +} + +abstract class KlibModuleFragmentExtensionVisitor : KmModuleFragmentExtensionVisitor { + + abstract fun visitFile(file: KlibSourceFile) + + abstract fun visitFqName(fqName: String) + + abstract fun visitClassName(className: ClassName) + + override val type: KmExtensionType + get() = TYPE + + companion object { + val TYPE = KmExtensionType(KlibModuleFragmentExtensionVisitor::class) + } +} \ No newline at end of file diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt index bc17cae5d6e..c7e750b27ca 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/extensions.kt @@ -64,6 +64,11 @@ interface KmClassExtensionVisitor : KmDeclarationContainerExtensionVisitor */ interface KmPackageExtensionVisitor : KmDeclarationContainerExtensionVisitor +/** + * A visitor to visit platform-specific extensions for a module fragment. + */ +interface KmModuleFragmentExtensionVisitor : KmExtensionVisitor + /** * A visitor to visit platform-specific extensions for a function. */ diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt index 2a232c1dd25..5eb608e6350 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt @@ -16,6 +16,8 @@ interface MetadataExtensions { fun readPackageExtensions(v: KmPackageVisitor, proto: ProtoBuf.Package, c: ReadContext) + fun readModuleFragmentExtensions(v: KmModuleFragmentVisitor, proto: ProtoBuf.PackageFragment, c: ReadContext) + fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, c: ReadContext) fun readPropertyExtensions(v: KmPropertyVisitor, proto: ProtoBuf.Property, c: ReadContext) @@ -30,6 +32,10 @@ interface MetadataExtensions { fun writePackageExtensions(type: KmExtensionType, proto: ProtoBuf.Package.Builder, c: WriteContext): KmPackageExtensionVisitor? + fun writeModuleFragmentExtensions( + type: KmExtensionType, proto: ProtoBuf.PackageFragment.Builder, c: WriteContext + ): KmModuleFragmentExtensionVisitor? + fun writeFunctionExtensions(type: KmExtensionType, proto: ProtoBuf.Function.Builder, c: WriteContext): KmFunctionExtensionVisitor? fun writePropertyExtensions(type: KmExtensionType, proto: ProtoBuf.Property.Builder, c: WriteContext): KmPropertyExtensionVisitor? @@ -48,6 +54,8 @@ interface MetadataExtensions { fun createPackageExtension(): KmPackageExtension + fun createModuleFragmentExtensions(): KmModuleFragmentExtension + fun createFunctionExtension(): KmFunctionExtension fun createPropertyExtension(): KmPropertyExtension diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/extensionNodes.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/extensionNodes.kt index 00de66adf52..370ce88e455 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/extensionNodes.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/extensionNodes.kt @@ -15,6 +15,8 @@ interface KmClassExtension : KmClassExtensionVisitor, KmExtension +interface KmModuleFragmentExtension : KmModuleFragmentExtensionVisitor, KmExtension + interface KmFunctionExtension : KmFunctionExtensionVisitor, KmExtension interface KmPropertyExtension : KmPropertyExtensionVisitor, KmExtension diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt index 823badb9d1a..e807fbbc122 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readUtils.kt @@ -23,7 +23,7 @@ fun ProtoBuf.Annotation.readAnnotation(strings: NameResolver): KmAnnotation = }.toMap() ) -private fun ProtoBuf.Annotation.Argument.Value.readAnnotationArgument(strings: NameResolver): KmAnnotationArgument<*>? { +fun ProtoBuf.Annotation.Argument.Value.readAnnotationArgument(strings: NameResolver): KmAnnotationArgument<*>? { if (Flags.IS_UNSIGNED[flags]) { return when (type) { BYTE -> KmAnnotationArgument.UByteValue(intValue.toByte()) diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt index 73061bf54a8..99b1f2e9c03 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt @@ -11,15 +11,23 @@ import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.* import org.jetbrains.kotlin.metadata.deserialization.Flags as F +/** + * Allows to populate [BasicReadContext] with additional data + * that can be used when reading metadata in [MetadataExtensions]. + */ +interface ReadContextExtension + class ReadContext( val strings: NameResolver, val types: TypeTable, internal val versionRequirements: VersionRequirementTable, - private val parent: ReadContext? = null + private val parent: ReadContext? = null, + val contextExtensions: List = emptyList() ) { - internal val extensions = MetadataExtensions.INSTANCES private val typeParameterNameToId = mutableMapOf() + internal val extensions = MetadataExtensions.INSTANCES + operator fun get(index: Int): String = strings.getString(index) @@ -30,16 +38,24 @@ class ReadContext( typeParameterNameToId[name] ?: parent?.getTypeParameterId(name) fun withTypeParameters(typeParameters: List): ReadContext = - ReadContext(strings, types, versionRequirements, this).apply { + ReadContext(strings, types, versionRequirements, this, contextExtensions).apply { for (typeParameter in typeParameters) { typeParameterNameToId[typeParameter.name] = typeParameter.id } } } -fun ProtoBuf.Class.accept(v: KmClassVisitor, strings: NameResolver) { - val c = ReadContext(strings, TypeTable(typeTable), VersionRequirementTable.create(versionRequirementTable)) - .withTypeParameters(typeParameterList) +fun ProtoBuf.Class.accept( + v: KmClassVisitor, + strings: NameResolver, + contextExtensions: List = emptyList() +) { + val c = ReadContext( + strings, + TypeTable(typeTable), + VersionRequirementTable.create(versionRequirementTable), + contextExtensions = contextExtensions + ).withTypeParameters(typeParameterList) v.visit(flags, c.className(fqName)) @@ -87,8 +103,17 @@ fun ProtoBuf.Class.accept(v: KmClassVisitor, strings: NameResolver) { v.visitEnd() } -fun ProtoBuf.Package.accept(v: KmPackageVisitor, strings: NameResolver) { - val c = ReadContext(strings, TypeTable(typeTable), VersionRequirementTable.create(versionRequirementTable)) +fun ProtoBuf.Package.accept( + v: KmPackageVisitor, + strings: NameResolver, + contextExtensions: List = emptyList() +) { + val c = ReadContext( + strings, + TypeTable(typeTable), + VersionRequirementTable.create(versionRequirementTable), + contextExtensions = contextExtensions + ) v.visitDeclarations(functionList, propertyList, typeAliasList, c) @@ -99,6 +124,31 @@ fun ProtoBuf.Package.accept(v: KmPackageVisitor, strings: NameResolver) { v.visitEnd() } +fun ProtoBuf.PackageFragment.accept( + v: KmModuleFragmentVisitor, + strings: NameResolver, + contextExtensions: List = emptyList() +) { + val c = ReadContext( + strings, + TypeTable(ProtoBuf.TypeTable.newBuilder().build()), + VersionRequirementTable.EMPTY, + contextExtensions = contextExtensions + ) + + v.visitPackage()?.let { `package`.accept(it, strings, contextExtensions) } + + class_List.forEach { clazz -> + v.visitClass()?.let { clazz.accept(it, strings, contextExtensions) } + } + + for (extension in c.extensions) { + extension.readModuleFragmentExtensions(v, this, c) + } + + v.visitEnd() +} + private fun KmDeclarationContainerVisitor.visitDeclarations( functions: List, properties: List, diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writeUtils.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writeUtils.kt index 0c15cccb4bf..780a886c75a 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writeUtils.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writeUtils.kt @@ -24,7 +24,7 @@ fun KmAnnotation.writeAnnotation(strings: StringTable): ProtoBuf.Annotation.Buil } } -private fun KmAnnotationArgument<*>.writeAnnotationArgument(strings: StringTable): ProtoBuf.Annotation.Argument.Value.Builder = +fun KmAnnotationArgument<*>.writeAnnotationArgument(strings: StringTable): ProtoBuf.Annotation.Argument.Value.Builder = ProtoBuf.Annotation.Argument.Value.newBuilder().apply { when (this@writeAnnotationArgument) { is KmAnnotationArgument.ByteValue -> { diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt index 40ab536841d..0fdded00966 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt @@ -6,13 +6,20 @@ package kotlinx.metadata.impl import kotlinx.metadata.* +import kotlinx.metadata.impl.extensions.MetadataExtensions import kotlinx.metadata.impl.extensions.applySingleExtension import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable import org.jetbrains.kotlin.metadata.serialization.StringTable -class WriteContext(val strings: StringTable) { +/** + * Allows to populate [WriteContext] with additional data + * that can be used when writing metadata in [MetadataExtensions]. + */ +interface WriteContextExtension + +open class WriteContext(val strings: StringTable, val contextExtensions: List = emptyList()) { val versionRequirements: MutableVersionRequirementTable = MutableVersionRequirementTable() operator fun get(string: String): Int = @@ -393,9 +400,9 @@ private fun writeEffectExpression(c: WriteContext, output: (ProtoBuf.Expression. } } -open class ClassWriter(stringTable: StringTable) : KmClassVisitor() { +open class ClassWriter(stringTable: StringTable, contextExtensions: List = emptyList()) : KmClassVisitor() { protected val t = ProtoBuf.Class.newBuilder()!! - protected val c = WriteContext(stringTable) + protected val c: WriteContext = WriteContext(stringTable, contextExtensions) override fun visit(flags: Flags, name: ClassName) { if (flags != ProtoBuf.Class.getDefaultInstance().flags) { @@ -455,9 +462,9 @@ open class ClassWriter(stringTable: StringTable) : KmClassVisitor() { } } -open class PackageWriter(stringTable: StringTable) : KmPackageVisitor() { +open class PackageWriter(stringTable: StringTable, contextExtensions: List = emptyList()) : KmPackageVisitor() { protected val t = ProtoBuf.Package.newBuilder()!! - protected val c = WriteContext(stringTable) + protected val c: WriteContext = WriteContext(stringTable, contextExtensions) override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? = writeFunction(c, flags, name) { t.addFunction(it) } @@ -480,6 +487,31 @@ open class PackageWriter(stringTable: StringTable) : KmPackageVisitor() { } } +open class ModuleFragmentWriter(stringTable: StringTable, contextExtensions: List = emptyList()) : + KmModuleFragmentVisitor() { + protected val t = ProtoBuf.PackageFragment.newBuilder() + protected val c: WriteContext = WriteContext(stringTable, contextExtensions) + + override fun visitPackage(): KmPackageVisitor? = object : PackageWriter(c.strings, c.contextExtensions) { + override fun visitEnd() { + super.visitEnd() + this@ModuleFragmentWriter.t.setPackage(t) + } + } + + override fun visitClass(): KmClassVisitor? = object : ClassWriter(c.strings, c.contextExtensions) { + override fun visitEnd() { + super.visitEnd() + this@ModuleFragmentWriter.t.addClass_(t) + } + } + + override fun visitExtensions(type: KmExtensionType): KmModuleFragmentExtensionVisitor? = + applySingleExtension(type) { + writeModuleFragmentExtensions(type, t, c) + } +} + open class LambdaWriter(stringTable: StringTable) : KmLambdaVisitor() { protected var t: ProtoBuf.Function.Builder? = null protected val c = WriteContext(stringTable) diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/nodes.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/nodes.kt index cb5befb7936..525da1767df 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/nodes.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/nodes.kt @@ -217,6 +217,46 @@ class KmPackage : KmPackageVisitor(), KmDeclarationContainer { } } +/** + * Represents a Kotlin module fragment. This is used to represent metadata of a part of a module on platforms other than JVM. + */ +class KmModuleFragment : KmModuleFragmentVisitor() { + + /** + * Top-level functions, type aliases and properties in the module fragment. + */ + var pkg: KmPackage? = null + + /** + * Classes in the module fragment. + */ + val classes: MutableList = ArrayList() + + private val extensions: List = + MetadataExtensions.INSTANCES.map(MetadataExtensions::createModuleFragmentExtensions) + + override fun visitPackage(): KmPackageVisitor? = + KmPackage().also { pkg = it } + + override fun visitExtensions(type: KmExtensionType): KmModuleFragmentExtensionVisitor? = + extensions.singleOfType(type) + + override fun visitClass(): KmClassVisitor? = + KmClass().addTo(classes) + + /** + * Populates the given visitor with data in this module fragment. + * + * @param visitor the visitor which will visit data in the module fragment. + */ + fun accept(visitor: KmModuleFragmentVisitor) { + pkg?.let { visitor.visitPackage()?.let(it::accept) } + classes.forEach { visitor.visitClass()?.let(it::accept) } + extensions.forEach { visitor.visitExtensions(it.type)?.let(it::accept) } + visitor.visitEnd() + } +} + /** * Represents a synthetic class generated for a Kotlin lambda. */ diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt index 3fc592c6bdf..912e597e981 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/visitors.kt @@ -179,6 +179,42 @@ abstract class KmPackageVisitor @JvmOverloads constructor(delegate: KmPackageVis } } +/** + * A visitor to visit module fragments. The module fragment can have no more than one package, and any number of classes, + * and must have at least one declaration. + * + * When using this class, [visitEnd] must be called exactly once and after calls to all other visit* methods. + */ +abstract class KmModuleFragmentVisitor @JvmOverloads constructor(private val delegate: KmModuleFragmentVisitor? = null) { + + /** + * Visits a package within the module fragment. + */ + open fun visitPackage(): KmPackageVisitor? = + delegate?.visitPackage() + + /** + * Visits a class within the module fragment. + */ + open fun visitClass(): KmClassVisitor? = + delegate?.visitClass() + + /** + * Visits the extensions of the given type on the module fragment. + * + * @param type the type of extension visitor to be returned. + */ + open fun visitExtensions(type: KmExtensionType): KmModuleFragmentExtensionVisitor? = + delegate?.visitExtensions(type) + + /** + * Visits the end of the module fragment. + */ + open fun visitEnd() { + delegate?.visitEnd() + } +} + /** * A visitor to visit the metadata of a synthetic class generated for a Kotlin lambda. * diff --git a/settings.gradle b/settings.gradle index 055530ebb4d..8bc3b93dcc4 100644 --- a/settings.gradle +++ b/settings.gradle @@ -159,6 +159,7 @@ include ":kotlin-build-common", ":kotlin-source-map-loader", ":kotlinx-metadata", ":kotlinx-metadata-jvm", + ":kotlinx-metadata-klib", ":prepare:build.version", ":kotlin-build-common", ":prepare:formatter", @@ -324,6 +325,7 @@ project(':kotlin-reflect').projectDir = "$rootDir/libraries/reflect" as File project(':kotlin-reflect-api').projectDir = "$rootDir/libraries/reflect/api" as File project(':kotlinx-metadata').projectDir = "$rootDir/libraries/kotlinx-metadata" as File project(':kotlinx-metadata-jvm').projectDir = "$rootDir/libraries/kotlinx-metadata/jvm" as File +project(':kotlinx-metadata-klib').projectDir = "$rootDir/libraries/kotlinx-metadata/klib" as File project(':kotlin-compiler').projectDir = "$rootDir/prepare/compiler" as File project(':kotlin-compiler-embeddable').projectDir = "$rootDir/prepare/compiler-embeddable" as File project(':kotlin-compiler-client-embeddable').projectDir = "$rootDir/prepare/compiler-client-embeddable" as File