Support multiple MetadataExtensions instances in kotlinx-metadata

#KT-23198
This commit is contained in:
Alexander Udalov
2018-05-15 19:08:14 +02:00
parent c42001f550
commit 23d107c11d
5 changed files with 64 additions and 36 deletions
@@ -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.
*
@@ -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? {
@@ -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<MetadataExtensions> 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"
)
}
}
}
}
@@ -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<Int, Int>()
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()
}
@@ -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 <T : KmExtensionVisitor> 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 }