diff --git a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt index 88ffdb78947..75b3f638272 100644 --- a/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt +++ b/libraries/kotlinx-metadata/jvm/src/kotlinx/metadata/jvm/KotlinClassMetadata.kt @@ -15,6 +15,7 @@ import kotlinx.metadata.impl.PackageWriter import kotlinx.metadata.impl.accept import kotlinx.metadata.jvm.impl.writeProtoBufData import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil +import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable import kotlin.LazyThreadSafetyMode.PUBLICATION /** @@ -47,7 +48,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { /** * A [KmClassVisitor] that generates the metadata of a Kotlin class. */ - class Writer : ClassWriter() { + class Writer : ClassWriter(JvmStringTable()) { /** * Returns the metadata of the class that was written with this writer. * @@ -96,7 +97,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { /** * A [KmPackageVisitor] that generates the metadata of a Kotlin file facade. */ - class Writer : PackageWriter() { + class Writer : PackageWriter(JvmStringTable()) { /** * Returns the metadata of the file facade that was written with this writer. * @@ -161,7 +162,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { * call [Writer.visitFunction] and [Writer.visitEnd] on a newly created instance of this writer. If these methods are not called, * the resulting metadata will represent a _non-lambda_ synthetic class. */ - class Writer : LambdaWriter() { + class Writer : LambdaWriter(JvmStringTable()) { /** * Returns the metadata of the synthetic class that was written with this writer. * @@ -264,7 +265,7 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) { /** * A [KmPackageVisitor] that generates the metadata of a multi-file class part. */ - class Writer : PackageWriter() { + class Writer : PackageWriter(JvmStringTable()) { /** * Returns the metadata of the multi-file class part that was written with this writer. * 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 94fbbfd16ae..3e551886e49 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 @@ -16,7 +16,6 @@ import org.jetbrains.kotlin.metadata.deserialization.TypeTable import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil -import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable import org.jetbrains.kotlin.metadata.serialization.StringTable internal class JvmMetadataExtensions : MetadataExtensions { @@ -69,8 +68,6 @@ internal class JvmMetadataExtensions : MetadataExtensions { ext.visitEnd() } - override fun createStringTable(): StringTable = JvmStringTable() - override fun writeFunctionExtensions( type: KmExtensionType, proto: ProtoBuf.Function.Builder, strings: StringTable ): KmFunctionExtensionVisitor? { 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 cda8e92d402..b7bdea8c68c 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/extensions/MetadataExtensions.kt @@ -23,8 +23,6 @@ interface MetadataExtensions { fun readTypeExtensions(v: KmTypeVisitor, proto: ProtoBuf.Type, strings: NameResolver) - fun createStringTable(): StringTable - fun writeFunctionExtensions(type: KmExtensionType, proto: ProtoBuf.Function.Builder, strings: StringTable): KmFunctionExtensionVisitor? fun writePropertyExtensions(type: KmExtensionType, proto: ProtoBuf.Property.Builder, strings: StringTable): KmPropertyExtensionVisitor? @@ -40,13 +38,14 @@ interface MetadataExtensions { fun writeTypeExtensions(type: KmExtensionType, proto: ProtoBuf.Type.Builder, strings: StringTable): KmTypeExtensionVisitor? companion object { - val INSTANCE: MetadataExtensions by lazy { - ServiceLoader.load(MetadataExtensions::class.java).toList().firstOrNull() - ?: error( - "No MetadataExtensions instances found in the classpath. Please ensure that the META-INF/services/ " + - "is not stripped from your application and that the Java virtual machine is not running " + - "under a security manager" - ) + val INSTANCES: List by lazy { + ServiceLoader.load(MetadataExtensions::class.java).toList().also { + if (it.isEmpty()) error( + "No MetadataExtensions instances found in the classpath. Please ensure that the META-INF/services/ " + + "is not stripped from your application and that the Java virtual machine is not running " + + "under a security manager" + ) + } } } } diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt index 6796a36e764..bdf80844b45 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/readers.kt @@ -18,7 +18,7 @@ class ReadContext( internal val versionRequirements: VersionRequirementTable, private val parent: ReadContext? = null ) { - internal val extensions = MetadataExtensions.INSTANCE + internal val extensions = MetadataExtensions.INSTANCES private val typeParameterNameToId = mutableMapOf() operator fun get(index: Int): String = @@ -137,7 +137,9 @@ private fun ProtoBuf.Constructor.accept(v: KmConstructorVisitor, c: ReadContext) v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } } - c.extensions.readConstructorExtensions(v, this, c.strings, c.types) + for (extension in c.extensions) { + extension.readConstructorExtensions(v, this, c.strings, c.types) + } v.visitEnd() } @@ -169,7 +171,9 @@ private fun ProtoBuf.Function.accept(v: KmFunctionVisitor, outer: ReadContext) { v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } } - c.extensions.readFunctionExtensions(v, this, c.strings, c.types) + for (extension in c.extensions) { + extension.readFunctionExtensions(v, this, c.strings, c.types) + } v.visitEnd() } @@ -198,7 +202,9 @@ private fun ProtoBuf.Property.accept(v: KmPropertyVisitor, outer: ReadContext) { v.visitVersionRequirement()?.let { acceptVersionRequirementVisitor(it, c) } } - c.extensions.readPropertyExtensions(v, this, c.strings, c.types) + for (extension in c.extensions) { + extension.readPropertyExtensions(v, this, c.strings, c.types) + } v.visitEnd() } @@ -259,7 +265,9 @@ private fun ProtoBuf.TypeParameter.accept(v: KmTypeParameterVisitor, c: ReadCont v.visitUpperBound(upperBound.typeFlags)?.let { upperBound.accept(it, c) } } - c.extensions.readTypeParameterExtensions(v, this, c.strings) + for (extension in c.extensions) { + extension.readTypeParameterExtensions(v, this, c.strings) + } v.visitEnd() } @@ -311,7 +319,9 @@ private fun ProtoBuf.Type.accept(v: KmTypeVisitor, c: ReadContext) { )?.let { upperBound.accept(it, c) } } - c.extensions.readTypeExtensions(v, this, c.strings) + for (extension in c.extensions) { + extension.readTypeExtensions(v, this, c.strings) + } v.visitEnd() } diff --git a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt index b48482ca233..d1662c0e37a 100644 --- a/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt +++ b/libraries/kotlinx-metadata/src/kotlinx/metadata/impl/writers.kt @@ -12,9 +12,8 @@ import org.jetbrains.kotlin.metadata.deserialization.VersionRequirement import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable import org.jetbrains.kotlin.metadata.serialization.StringTable -class WriteContext { - internal val extensions = MetadataExtensions.INSTANCE - val strings: StringTable = extensions.createStringTable() +class WriteContext(val strings: StringTable) { + internal val extensions = MetadataExtensions.INSTANCES val versionRequirements: MutableVersionRequirementTable = MutableVersionRequirementTable() operator fun get(string: String): Int = @@ -22,6 +21,18 @@ class WriteContext { fun getClassName(name: ClassName): Int = strings.getClassNameIndex(name) + + internal fun applySingleExtension(type: KmExtensionType, block: MetadataExtensions.() -> T?): T? { + var result: T? = null + for (extension in extensions) { + val current = block(extension) ?: continue + if (result != null) { + throw IllegalStateException("Multiple extensions handle the same extension type: $type") + } + result = current + } + return result + } } private fun writeTypeParameter( @@ -35,7 +46,9 @@ private fun writeTypeParameter( writeType(c, flags) { t.addUpperBound(it) } override fun visitExtensions(type: KmExtensionType): KmTypeParameterExtensionVisitor? = - c.extensions.writeTypeParameterExtensions(type, t, c.strings) + c.applySingleExtension(type) { + writeTypeParameterExtensions(type, t, c.strings) + } override fun visitEnd() { t.name = c[name] @@ -102,7 +115,9 @@ private fun writeType(c: WriteContext, flags: Flags, output: (ProtoBuf.Type.Buil } override fun visitExtensions(type: KmExtensionType): KmTypeExtensionVisitor? = - c.extensions.writeTypeExtensions(type, t, c.strings) + c.applySingleExtension(type) { + writeTypeExtensions(type, t, c.strings) + } override fun visitEnd() { if (Flag.Type.IS_NULLABLE(flags)) { @@ -127,7 +142,9 @@ private fun writeConstructor(c: WriteContext, flags: Flags, output: (ProtoBuf.Co writeVersionRequirement(c) { t.versionRequirement = it } override fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor? = - c.extensions.writeConstructorExtensions(type, t, c.strings) + c.applySingleExtension(type) { + writeConstructorExtensions(type, t, c.strings) + } override fun visitEnd() { if (flags != ProtoBuf.Constructor.getDefaultInstance().flags) { @@ -160,7 +177,9 @@ private fun writeFunction(c: WriteContext, flags: Flags, name: String, output: ( writeContract(c) { t.contract = it.build() } override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? = - c.extensions.writeFunctionExtensions(type, t, c.strings) + c.applySingleExtension(type) { + writeFunctionExtensions(type, t, c.strings) + } override fun visitEnd() { t.name = c[name] @@ -192,7 +211,9 @@ private fun writeProperty( writeVersionRequirement(c) { t.versionRequirement = it } override fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor? = - c.extensions.writePropertyExtensions(type, t, c.strings) + c.applySingleExtension(type) { + writePropertyExtensions(type, t, c.strings) + } override fun visitEnd() { t.name = c[name] @@ -384,9 +405,9 @@ private fun writeEffectExpression(c: WriteContext, output: (ProtoBuf.Expression. } } -open class ClassWriter : KmClassVisitor() { +open class ClassWriter(stringTable: StringTable) : KmClassVisitor() { val t = ProtoBuf.Class.newBuilder()!! - val c = WriteContext() + val c = WriteContext(stringTable) override fun visit(flags: Flags, name: ClassName) { if (flags != ProtoBuf.Class.getDefaultInstance().flags) { @@ -441,9 +462,9 @@ open class ClassWriter : KmClassVisitor() { } } -open class PackageWriter : KmPackageVisitor() { +open class PackageWriter(stringTable: StringTable) : KmPackageVisitor() { val t = ProtoBuf.Package.newBuilder()!! - val c = WriteContext() + val c = WriteContext(stringTable) override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? = writeFunction(c, flags, name) { t.addFunction(it) } @@ -461,9 +482,9 @@ open class PackageWriter : KmPackageVisitor() { } } -open class LambdaWriter : KmLambdaVisitor() { +open class LambdaWriter(stringTable: StringTable) : KmLambdaVisitor() { var t: ProtoBuf.Function.Builder? = null - val c = WriteContext() + val c = WriteContext(stringTable) override fun visitFunction(flags: Flags, name: String): KmFunctionVisitor? = writeFunction(c, flags, name) { t = it }