Implement writing visitors for kotlinx-metadata

#KT-23198
This commit is contained in:
Alexander Udalov
2018-03-16 15:47:22 +01:00
parent 4b284a4890
commit a751d02306
10 changed files with 998 additions and 6 deletions
@@ -24,6 +24,9 @@ dependencies {
shadows(project(":core:metadata.jvm")) shadows(project(":core:metadata.jvm"))
shadows(protobufLite()) shadows(protobufLite())
testCompile(commonDep("junit:junit")) testCompile(commonDep("junit:junit"))
testCompile(intellijDep()) { includeJars("asm-all", rootProject = rootProject) }
testCompileOnly(project(":kotlin-reflect-api"))
testRuntime(projectDist(":kotlin-reflect"))
} }
noDefaultJar() noDefaultJar()
@@ -9,7 +9,11 @@ import kotlinx.metadata.InconsistentKotlinMetadataException
import kotlinx.metadata.KmClassVisitor import kotlinx.metadata.KmClassVisitor
import kotlinx.metadata.KmLambdaVisitor import kotlinx.metadata.KmLambdaVisitor
import kotlinx.metadata.KmPackageVisitor import kotlinx.metadata.KmPackageVisitor
import kotlinx.metadata.impl.ClassWriter
import kotlinx.metadata.impl.LambdaWriter
import kotlinx.metadata.impl.PackageWriter
import kotlinx.metadata.impl.accept 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.deserialization.JvmProtoBufUtil
import kotlin.LazyThreadSafetyMode.PUBLICATION import kotlin.LazyThreadSafetyMode.PUBLICATION
@@ -39,6 +43,34 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) {
val (strings, proto) = classData val (strings, proto) = classData
proto.accept(v, strings) proto.accept(v, strings)
} }
/**
* A [KmClassVisitor] that generates the metadata of a Kotlin class.
*/
class Writer : ClassWriter() {
/**
* Returns the metadata of the class that was written with this writer.
*
* @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]),
* [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default
* @param bytecodeVersion bytecode version to be written to the metadata (see [KotlinClassHeader.bytecodeVersion]),
* [KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION] by default
* @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]),
* 0 by default
*/
@JvmOverloads
fun write(
metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION,
bytecodeVersion: IntArray = KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION,
extraInt: Int = 0
): KotlinClassMetadata.Class {
val (d1, d2) = writeProtoBufData(t.build(), c)
val metadata = KotlinClassHeader(
KotlinClassHeader.CLASS_KIND, metadataVersion, bytecodeVersion, d1, d2, null, null, extraInt
)
return KotlinClassMetadata.Class(metadata)
}
}
} }
/** /**
@@ -60,6 +92,34 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) {
val (strings, proto) = packageData val (strings, proto) = packageData
proto.accept(v, strings) proto.accept(v, strings)
} }
/**
* A [KmPackageVisitor] that generates the metadata of a Kotlin file facade.
*/
class Writer : PackageWriter() {
/**
* Returns the metadata of the file facade that was written with this writer.
*
* @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]),
* [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default
* @param bytecodeVersion bytecode version to be written to the metadata (see [KotlinClassHeader.bytecodeVersion]),
* [KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION] by default
* @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]),
* 0 by default
*/
@JvmOverloads
fun write(
metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION,
bytecodeVersion: IntArray = KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION,
extraInt: Int = 0
): KotlinClassMetadata.FileFacade {
val (d1, d2) = writeProtoBufData(t.build(), c)
val metadata = KotlinClassHeader(
KotlinClassHeader.FILE_FACADE_KIND, metadataVersion, bytecodeVersion, d1, d2, null, null, extraInt
)
return KotlinClassMetadata.FileFacade(metadata)
}
}
} }
/** /**
@@ -95,6 +155,39 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) {
val (strings, proto) = functionData!! val (strings, proto) = functionData!!
proto.accept(v, strings) proto.accept(v, strings)
} }
/**
* A [KmLambdaVisitor] that generates the metadata of a synthetic class. To generate metadata of a Kotlin lambda,
* 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() {
/**
* Returns the metadata of the synthetic class that was written with this writer.
*
* @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]),
* [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default
* @param bytecodeVersion bytecode version to be written to the metadata (see [KotlinClassHeader.bytecodeVersion]),
* [KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION] by default
* @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]),
* 0 by default
*/
@JvmOverloads
fun write(
metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION,
bytecodeVersion: IntArray = KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION,
extraInt: Int = 0
): KotlinClassMetadata.SyntheticClass {
val proto = t?.build()
val (d1, d2) =
if (proto != null) writeProtoBufData(proto, c)
else Pair(emptyArray(), emptyArray())
val metadata = KotlinClassHeader(
KotlinClassHeader.SYNTHETIC_CLASS_KIND, metadataVersion, bytecodeVersion, d1, d2, null, null, extraInt
)
return KotlinClassMetadata.SyntheticClass(metadata)
}
}
} }
/** /**
@@ -107,6 +200,36 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) {
* JVM internal names of the part classes which this multi-file class combines. * JVM internal names of the part classes which this multi-file class combines.
*/ */
val partClassNames: List<String> = header.data1.asList() val partClassNames: List<String> = header.data1.asList()
/**
* A writer that generates the metadata of a multi-file class facade.
*/
class Writer {
/**
* Returns the metadata of the multi-file class facade that was written with this writer.
*
* @param partClassNames JVM internal names of the part classes which this multi-file class combines
* @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]),
* [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default
* @param bytecodeVersion bytecode version to be written to the metadata (see [KotlinClassHeader.bytecodeVersion]),
* [KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION] by default
* @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]),
* 0 by default
*/
@JvmOverloads
fun write(
partClassNames: List<String>,
metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION,
bytecodeVersion: IntArray = KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION,
extraInt: Int = 0
): KotlinClassMetadata.MultiFileClassFacade {
val metadata = KotlinClassHeader(
KotlinClassHeader.MULTI_FILE_CLASS_FACADE_KIND, metadataVersion, bytecodeVersion, partClassNames.toTypedArray(),
null, null, null, extraInt
)
return KotlinClassMetadata.MultiFileClassFacade(metadata)
}
}
} }
/** /**
@@ -137,6 +260,36 @@ sealed class KotlinClassMetadata(val header: KotlinClassHeader) {
val (strings, proto) = packageData val (strings, proto) = packageData
proto.accept(v, strings) proto.accept(v, strings)
} }
/**
* A [KmPackageVisitor] that generates the metadata of a multi-file class part.
*/
class Writer : PackageWriter() {
/**
* Returns the metadata of the multi-file class part that was written with this writer.
*
* @param facadeClassName JVM internal name of the corresponding multi-file class facade
* @param metadataVersion metadata version to be written to the metadata (see [KotlinClassHeader.metadataVersion]),
* [KotlinClassHeader.COMPATIBLE_METADATA_VERSION] by default
* @param bytecodeVersion bytecode version to be written to the metadata (see [KotlinClassHeader.bytecodeVersion]),
* [KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION] by default
* @param extraInt the value of the class-level flags to be written to the metadata (see [KotlinClassHeader.extraInt]),
* 0 by default
*/
@JvmOverloads
fun write(
facadeClassName: String,
metadataVersion: IntArray = KotlinClassHeader.COMPATIBLE_METADATA_VERSION,
bytecodeVersion: IntArray = KotlinClassHeader.COMPATIBLE_BYTECODE_VERSION,
extraInt: Int = 0
): KotlinClassMetadata.MultiFileClassPart {
val (d1, d2) = writeProtoBufData(t.build(), c)
val metadata = KotlinClassHeader(
KotlinClassHeader.MULTI_FILE_CLASS_PART_KIND, metadataVersion, bytecodeVersion, d1, d2, facadeClassName, null, extraInt
)
return KotlinClassMetadata.MultiFileClassPart(metadata)
}
}
} }
/** /**
@@ -8,6 +8,7 @@ package kotlinx.metadata.jvm.impl
import kotlinx.metadata.* import kotlinx.metadata.*
import kotlinx.metadata.impl.extensions.MetadataExtensions import kotlinx.metadata.impl.extensions.MetadataExtensions
import kotlinx.metadata.impl.readAnnotation import kotlinx.metadata.impl.readAnnotation
import kotlinx.metadata.impl.writeAnnotation
import kotlinx.metadata.jvm.* import kotlinx.metadata.jvm.*
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.NameResolver import org.jetbrains.kotlin.metadata.deserialization.NameResolver
@@ -15,6 +16,8 @@ import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf import org.jetbrains.kotlin.metadata.jvm.JvmProtoBuf
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil 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 { internal class JvmMetadataExtensions : MetadataExtensions {
override fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, strings: NameResolver, types: TypeTable) { override fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, strings: NameResolver, types: TypeTable) {
@@ -65,4 +68,115 @@ internal class JvmMetadataExtensions : MetadataExtensions {
} }
ext.visitEnd() ext.visitEnd()
} }
override fun createStringTable(): StringTable = JvmStringTable()
override fun writeFunctionExtensions(
type: KmExtensionType, proto: ProtoBuf.Function.Builder, strings: StringTable
): KmFunctionExtensionVisitor? {
if (type != JvmFunctionExtensionVisitor.TYPE) return null
return object : JvmFunctionExtensionVisitor() {
override fun visit(desc: String?) {
if (desc != null) {
proto.setExtension(JvmProtoBuf.methodSignature, desc.toJvmMethodSignature(strings))
}
}
}
}
override fun writePropertyExtensions(
type: KmExtensionType, proto: ProtoBuf.Property.Builder, strings: StringTable
): KmPropertyExtensionVisitor? {
if (type != JvmPropertyExtensionVisitor.TYPE) return null
return object : JvmPropertyExtensionVisitor() {
var signature: JvmProtoBuf.JvmPropertySignature.Builder? = null
override fun visit(fieldName: String?, fieldTypeDesc: String?, getterDesc: String?, setterDesc: String?) {
if (fieldName == null && fieldTypeDesc == null && getterDesc == null && setterDesc == null) return
if (signature == null) {
signature = JvmProtoBuf.JvmPropertySignature.newBuilder()
}
signature!!.apply {
if (fieldName != null || fieldTypeDesc != null) {
field = JvmProtoBuf.JvmFieldSignature.newBuilder().also { field ->
if (fieldName != null) {
field.name = strings.getStringIndex(fieldName)
}
if (fieldTypeDesc != null) {
field.desc = strings.getStringIndex(fieldTypeDesc)
}
}.build()
}
if (getterDesc != null) {
getter = getterDesc.toJvmMethodSignature(strings)
}
if (setterDesc != null) {
setter = setterDesc.toJvmMethodSignature(strings)
}
}
}
override fun visitSyntheticMethodForAnnotations(desc: String?) {
if (desc == null) return
if (signature == null) {
signature = JvmProtoBuf.JvmPropertySignature.newBuilder()
}
signature!!.syntheticMethod = desc.toJvmMethodSignature(strings)
}
override fun visitEnd() {
if (signature != null) {
proto.setExtension(JvmProtoBuf.propertySignature, signature!!.build())
}
}
}
}
override fun writeConstructorExtensions(
type: KmExtensionType, proto: ProtoBuf.Constructor.Builder, strings: StringTable
): KmConstructorExtensionVisitor? {
if (type != JvmConstructorExtensionVisitor.TYPE) return null
return object : JvmConstructorExtensionVisitor() {
override fun visit(desc: String?) {
if (desc != null) {
proto.setExtension(JvmProtoBuf.constructorSignature, desc.toJvmMethodSignature(strings))
}
}
}
}
override fun writeTypeParameterExtensions(
type: KmExtensionType, proto: ProtoBuf.TypeParameter.Builder, strings: StringTable
): KmTypeParameterExtensionVisitor? {
if (type != JvmTypeParameterExtensionVisitor.TYPE) return null
return object : JvmTypeParameterExtensionVisitor() {
override fun visitAnnotation(annotation: KmAnnotation) {
proto.addExtension(JvmProtoBuf.typeParameterAnnotation, annotation.writeAnnotation(strings).build())
}
}
}
override fun writeTypeExtensions(type: KmExtensionType, proto: ProtoBuf.Type.Builder, strings: StringTable): KmTypeExtensionVisitor? {
if (type != JvmTypeExtensionVisitor.TYPE) return null
return object : JvmTypeExtensionVisitor() {
override fun visit(isRaw: Boolean) {
if (isRaw) {
proto.setExtension(JvmProtoBuf.isRaw, true)
}
}
override fun visitAnnotation(annotation: KmAnnotation) {
proto.addExtension(JvmProtoBuf.typeAnnotation, annotation.writeAnnotation(strings).build())
}
}
}
private fun String.toJvmMethodSignature(strings: StringTable): JvmProtoBuf.JvmMethodSignature =
JvmProtoBuf.JvmMethodSignature.newBuilder().apply {
name = strings.getStringIndex(substringBefore('('))
desc = strings.getStringIndex("(" + substringAfter('('))
}.build()
} }
@@ -0,0 +1,19 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.jvm.impl
import kotlinx.metadata.impl.WriteContext
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
import org.jetbrains.kotlin.metadata.jvm.serialization.JvmStringTable
import org.jetbrains.kotlin.protobuf.MessageLite
internal fun writeProtoBufData(message: MessageLite, c: WriteContext): Pair<Array<String>, Array<String>> {
val strings = c.strings as JvmStringTable
return Pair(
JvmProtoBufUtil.writeData(message, strings),
strings.strings.toTypedArray()
)
}
@@ -6,11 +6,16 @@
package kotlinx.metadata.test package kotlinx.metadata.test
import kotlinx.metadata.* import kotlinx.metadata.*
import kotlinx.metadata.jvm.JvmConstructorExtensionVisitor
import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor import kotlinx.metadata.jvm.JvmFunctionExtensionVisitor
import kotlinx.metadata.jvm.KotlinClassHeader import kotlinx.metadata.jvm.KotlinClassHeader
import kotlinx.metadata.jvm.KotlinClassMetadata import kotlinx.metadata.jvm.KotlinClassMetadata
import org.jetbrains.org.objectweb.asm.ClassWriter
import org.jetbrains.org.objectweb.asm.Opcodes
import org.junit.Assert.assertEquals import org.junit.Assert.assertEquals
import org.junit.Test import org.junit.Test
import java.net.URLClassLoader
import kotlin.reflect.full.primaryConstructor
class MetadataSmokeTest { class MetadataSmokeTest {
@Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE") @Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
@@ -55,4 +60,92 @@ class MetadataSmokeTest {
inlineFunctions inlineFunctions
) )
} }
@Test
fun produceKotlinClassFile() {
// First, produce a KotlinMetadata instance with the kotlinx-metadata API, for the following class:
// class Hello {
// fun hello(): String = "Hello, world!"
// }
val header = KotlinClassMetadata.Class.Writer().run {
visit(Flags(Flags.IS_PUBLIC), "Hello")
visitConstructor(Flags(Flags.IS_PUBLIC, Flags.Constructor.IS_PRIMARY))!!.run {
(visitExtensions(JvmConstructorExtensionVisitor.TYPE) as JvmConstructorExtensionVisitor).run {
visit("<init>()V")
}
visitEnd()
}
visitFunction(Flags(Flags.IS_PUBLIC, Flags.Function.IS_DECLARATION), "hello")!!.run {
visitReturnType(0)!!.run {
visitClass("kotlin/String")
visitEnd()
}
(visitExtensions(JvmFunctionExtensionVisitor.TYPE) as JvmFunctionExtensionVisitor).run {
visit("hello()Ljava/lang/String;")
}
visitEnd()
}
visitEnd()
write().header
}
// Then, produce the bytecode of a .class file with ASM
val bytes = ClassWriter(0).run {
visit(Opcodes.V1_6, Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL + Opcodes.ACC_SUPER, "Hello", null, "java/lang/Object", null)
// Use the created KotlinMetadata instance to write @kotlin.Metadata annotation on the class file
visitAnnotation("Lkotlin/Metadata;", true).run {
visit("mv", header.metadataVersion)
visit("bv", header.bytecodeVersion)
visit("k", header.kind)
visitArray("d1").run {
header.data1.forEach { visit(null, it) }
visitEnd()
}
visitArray("d2").run {
header.data2.forEach { visit(null, it) }
visitEnd()
}
visitEnd()
}
visitMethod(Opcodes.ACC_PUBLIC + Opcodes.ACC_FINAL, "hello", "()Ljava/lang/String;", null, null).run {
visitAnnotation("Lorg/jetbrains/annotations/NotNull;", false).visitEnd()
visitCode()
visitLdcInsn("Hello, world!")
visitInsn(Opcodes.ARETURN)
visitMaxs(1, 1)
visitEnd()
}
visitMethod(Opcodes.ACC_PUBLIC, "<init>", "()V", null, null).run {
visitCode()
visitVarInsn(Opcodes.ALOAD, 0)
visitMethodInsn(Opcodes.INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false)
visitInsn(Opcodes.RETURN)
visitMaxs(1, 1)
visitEnd()
}
visitEnd()
toByteArray()
}
// Finally, load the generated class, create its instance and invoke the method via Kotlin reflection.
// Kotlin reflection loads the metadata and builds a mapping from Kotlin symbols to JVM, so if the call succeeds,
// we can be sure that the metadata is consistent
val classLoader = object : URLClassLoader(emptyArray()) {
override fun findClass(name: String): Class<*> =
if (name == "Hello") defineClass(name, bytes, 0, bytes.size) else super.findClass(name)
}
val kClass = classLoader.loadClass("Hello").kotlin
val hello = kClass.primaryConstructor!!.call()
val result = kClass.members.single { it.name == "hello" }.call(hello) as String
assertEquals("Hello, world!", result)
}
} }
@@ -9,6 +9,7 @@ import kotlinx.metadata.*
import org.jetbrains.kotlin.metadata.ProtoBuf import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.NameResolver import org.jetbrains.kotlin.metadata.deserialization.NameResolver
import org.jetbrains.kotlin.metadata.deserialization.TypeTable import org.jetbrains.kotlin.metadata.deserialization.TypeTable
import org.jetbrains.kotlin.metadata.serialization.StringTable
import java.util.* import java.util.*
interface MetadataExtensions { interface MetadataExtensions {
@@ -22,6 +23,22 @@ interface MetadataExtensions {
fun readTypeExtensions(v: KmTypeVisitor, proto: ProtoBuf.Type, strings: NameResolver) 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?
fun writeConstructorExtensions(
type: KmExtensionType, proto: ProtoBuf.Constructor.Builder, strings: StringTable
): KmConstructorExtensionVisitor?
fun writeTypeParameterExtensions(
type: KmExtensionType, proto: ProtoBuf.TypeParameter.Builder, strings: StringTable
): KmTypeParameterExtensionVisitor?
fun writeTypeExtensions(type: KmExtensionType, proto: ProtoBuf.Type.Builder, strings: StringTable): KmTypeExtensionVisitor?
companion object { companion object {
val INSTANCE: MetadataExtensions by lazy { val INSTANCE: MetadataExtensions by lazy {
ServiceLoader.load(MetadataExtensions::class.java).toList().firstOrNull() ServiceLoader.load(MetadataExtensions::class.java).toList().firstOrNull()
@@ -0,0 +1,91 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.impl
import kotlinx.metadata.ClassName
import kotlinx.metadata.KmAnnotation
import kotlinx.metadata.KmAnnotationArgument
import kotlinx.metadata.isLocal
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.serialization.StringTable
fun KmAnnotation.writeAnnotation(strings: StringTable): ProtoBuf.Annotation.Builder =
ProtoBuf.Annotation.newBuilder().apply {
id = strings.getClassNameIndex(className)
for ((name, argument) in arguments) {
addArgument(ProtoBuf.Annotation.Argument.newBuilder().apply {
nameId = strings.getStringIndex(name)
value = argument.writeAnnotationArgument(strings).build()
})
}
}
private fun KmAnnotationArgument<*>.writeAnnotationArgument(strings: StringTable): ProtoBuf.Annotation.Argument.Value.Builder =
ProtoBuf.Annotation.Argument.Value.newBuilder().apply {
when (this@writeAnnotationArgument) {
is KmAnnotationArgument.ByteValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.BYTE
this.intValue = value.toLong()
}
is KmAnnotationArgument.CharValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.CHAR
this.intValue = value.toLong()
}
is KmAnnotationArgument.ShortValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.SHORT
this.intValue = value.toLong()
}
is KmAnnotationArgument.IntValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.INT
this.intValue = value.toLong()
}
is KmAnnotationArgument.LongValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.LONG
this.intValue = value
}
is KmAnnotationArgument.FloatValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.FLOAT
this.floatValue = value
}
is KmAnnotationArgument.DoubleValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.DOUBLE
this.doubleValue = value
}
is KmAnnotationArgument.BooleanValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.BOOLEAN
this.intValue = if (value) 1 else 0
}
is KmAnnotationArgument.StringValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.STRING
this.stringValue = strings.getStringIndex(value)
}
is KmAnnotationArgument.KClassValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.CLASS
this.classId = strings.getClassNameIndex(value)
}
is KmAnnotationArgument.EnumValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.ENUM
this.classId = strings.getClassNameIndex(enumClassName)
this.enumValueId = strings.getStringIndex(enumEntryName)
}
is KmAnnotationArgument.AnnotationValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.ANNOTATION
this.annotation = value.writeAnnotation(strings).build()
}
is KmAnnotationArgument.ArrayValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.ARRAY
for (element in value) {
this.addArrayElement(element.writeAnnotationArgument(strings))
}
}
}
}
internal fun StringTable.getClassNameIndex(name: ClassName): Int =
if (name.isLocal)
getQualifiedClassNameIndex(name.substring(1), true)
else
getQualifiedClassNameIndex(name, false)
@@ -0,0 +1,470 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. 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.impl
import kotlinx.metadata.*
import kotlinx.metadata.impl.extensions.MetadataExtensions
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 {
internal val extensions = MetadataExtensions.INSTANCE
val strings: StringTable = extensions.createStringTable()
val versionRequirements: MutableVersionRequirementTable = MutableVersionRequirementTable()
operator fun get(string: String): Int =
strings.getStringIndex(string)
fun getClassName(name: ClassName): Int =
strings.getClassNameIndex(name)
}
private fun writeTypeParameter(
c: WriteContext, flags: Int, name: String, id: Int, variance: KmVariance,
output: (ProtoBuf.TypeParameter.Builder) -> Unit
): KmTypeParameterVisitor =
object : KmTypeParameterVisitor() {
private val t = ProtoBuf.TypeParameter.newBuilder()
override fun visitUpperBound(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.addUpperBound(it) }
override fun visitExtensions(type: KmExtensionType): KmTypeParameterExtensionVisitor? =
c.extensions.writeTypeParameterExtensions(type, t, c.strings)
override fun visitEnd() {
t.name = c[name]
t.id = id
val reified = Flags.TypeParameter.IS_REIFIED(flags)
if (reified != ProtoBuf.TypeParameter.getDefaultInstance().reified) {
t.reified = reified
}
if (variance == KmVariance.IN) {
t.variance = ProtoBuf.TypeParameter.Variance.IN
} else if (variance == KmVariance.OUT) {
t.variance = ProtoBuf.TypeParameter.Variance.OUT
}
output(t)
}
}
private fun writeType(c: WriteContext, flags: Int, output: (ProtoBuf.Type.Builder) -> Unit): KmTypeVisitor =
object : KmTypeVisitor() {
private val t = ProtoBuf.Type.newBuilder()
override fun visitClass(name: ClassName) {
t.className = c.getClassName(name)
}
override fun visitTypeAlias(name: ClassName) {
t.typeAliasName = c.getClassName(name)
}
override fun visitStarProjection() {
t.addArgument(ProtoBuf.Type.Argument.newBuilder().apply {
projection = ProtoBuf.Type.Argument.Projection.STAR
})
}
override fun visitArgument(flags: Int, variance: KmVariance): KmTypeVisitor? =
writeType(c, flags) { argument ->
t.addArgument(ProtoBuf.Type.Argument.newBuilder().apply {
if (variance == KmVariance.IN) {
projection = ProtoBuf.Type.Argument.Projection.IN
} else if (variance == KmVariance.OUT) {
projection = ProtoBuf.Type.Argument.Projection.OUT
}
type = argument.build()
})
}
override fun visitTypeParameter(id: Int) {
t.typeParameter = id
}
override fun visitAbbreviatedType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.abbreviatedType = it.build() }
override fun visitOuterType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.outerType = it.build() }
override fun visitFlexibleTypeUpperBound(flags: Int, typeFlexibilityId: String?): KmTypeVisitor? =
writeType(c, flags) {
if (typeFlexibilityId != null) {
t.flexibleTypeCapabilitiesId = c[typeFlexibilityId]
}
t.flexibleUpperBound = it.build()
}
override fun visitExtensions(type: KmExtensionType): KmTypeExtensionVisitor? =
c.extensions.writeTypeExtensions(type, t, c.strings)
override fun visitEnd() {
if (Flags.Type.IS_NULLABLE(flags)) {
t.nullable = true
}
val flagsToWrite = flags shr 1
if (flagsToWrite != ProtoBuf.Type.getDefaultInstance().flags) {
t.flags = flagsToWrite
}
output(t)
}
}
private fun writeConstructor(c: WriteContext, flags: Int, output: (ProtoBuf.Constructor.Builder) -> Unit): KmConstructorVisitor =
object : KmConstructorVisitor() {
val t = ProtoBuf.Constructor.newBuilder()
override fun visitValueParameter(flags: Int, name: String): KmValueParameterVisitor? =
writeValueParameter(c, flags, name) { t.addValueParameter(it.build()) }
override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
writeVersionRequirement(c) { t.versionRequirement = it }
override fun visitExtensions(type: KmExtensionType): KmConstructorExtensionVisitor? =
c.extensions.writeConstructorExtensions(type, t, c.strings)
override fun visitEnd() {
if (flags != ProtoBuf.Constructor.getDefaultInstance().flags) {
t.flags = flags
}
output(t)
}
}
private fun writeFunction(c: WriteContext, flags: Int, name: String, output: (ProtoBuf.Function.Builder) -> Unit): KmFunctionVisitor =
object : KmFunctionVisitor() {
val t = ProtoBuf.Function.newBuilder()
override fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? =
writeTypeParameter(c, flags, name, id, variance) { t.addTypeParameter(it) }
override fun visitReceiverParameterType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.receiverType = it.build() }
override fun visitValueParameter(flags: Int, name: String): KmValueParameterVisitor? =
writeValueParameter(c, flags, name) { t.addValueParameter(it) }
override fun visitReturnType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.returnType = it.build() }
override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
writeVersionRequirement(c) { t.versionRequirement = it }
override fun visitContract(): KmContractVisitor? =
writeContract(c) { t.contract = it.build() }
override fun visitExtensions(type: KmExtensionType): KmFunctionExtensionVisitor? =
c.extensions.writeFunctionExtensions(type, t, c.strings)
override fun visitEnd() {
t.name = c[name]
if (flags != ProtoBuf.Function.getDefaultInstance().flags) {
t.flags = flags
}
output(t)
}
}
private fun writeProperty(
c: WriteContext, flags: Int, name: String, getterFlags: Int, setterFlags: Int, output: (ProtoBuf.Property.Builder) -> Unit
): KmPropertyVisitor = object : KmPropertyVisitor() {
val t = ProtoBuf.Property.newBuilder()
override fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? =
writeTypeParameter(c, flags, name, id, variance) { t.addTypeParameter(it) }
override fun visitReceiverParameterType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.receiverType = it.build() }
override fun visitSetterParameter(flags: Int, name: String): KmValueParameterVisitor? =
writeValueParameter(c, flags, name) { t.setterValueParameter = it.build() }
override fun visitReturnType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.returnType = it.build() }
override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
writeVersionRequirement(c) { t.versionRequirement = it }
override fun visitExtensions(type: KmExtensionType): KmPropertyExtensionVisitor? =
c.extensions.writePropertyExtensions(type, t, c.strings)
override fun visitEnd() {
t.name = c[name]
if (flags != ProtoBuf.Property.getDefaultInstance().flags) {
t.flags = flags
}
// TODO: do not write getterFlags/setterFlags if not needed
t.getterFlags = getterFlags
t.setterFlags = setterFlags
output(t)
}
}
private fun writeValueParameter(
c: WriteContext, flags: Int, name: String,
output: (ProtoBuf.ValueParameter.Builder) -> Unit
): KmValueParameterVisitor = object : KmValueParameterVisitor() {
val t = ProtoBuf.ValueParameter.newBuilder()
override fun visitType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.type = it.build() }
override fun visitVarargElementType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.varargElementType = it.build() }
override fun visitEnd() {
if (flags != ProtoBuf.ValueParameter.getDefaultInstance().flags) {
t.flags = flags
}
t.name = c[name]
output(t)
}
}
private fun writeTypeAlias(
c: WriteContext, flags: Int, name: String,
output: (ProtoBuf.TypeAlias.Builder) -> Unit
): KmTypeAliasVisitor = object : KmTypeAliasVisitor() {
val t = ProtoBuf.TypeAlias.newBuilder()
override fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? =
writeTypeParameter(c, flags, name, id, variance) { t.addTypeParameter(it) }
override fun visitUnderlyingType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.underlyingType = it.build() }
override fun visitExpandedType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.expandedType = it.build() }
override fun visitAnnotation(annotation: KmAnnotation) {
t.addAnnotation(annotation.writeAnnotation(c.strings))
}
override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
writeVersionRequirement(c) { t.versionRequirement = it }
override fun visitEnd() {
if (flags != ProtoBuf.TypeAlias.getDefaultInstance().flags) {
t.flags = flags
}
t.name = c[name]
output(t)
}
}
private fun writeVersionRequirement(
c: WriteContext, output: (Int) -> Unit
): KmVersionRequirementVisitor = object : KmVersionRequirementVisitor() {
var t: ProtoBuf.VersionRequirement.Builder? = null
override fun visit(kind: KmVersionRequirementVersionKind, level: KmVersionRequirementLevel, errorCode: Int?, message: String?) {
t = ProtoBuf.VersionRequirement.newBuilder().apply {
if (kind != defaultInstanceForType.versionKind) {
this.versionKind = when (kind) {
KmVersionRequirementVersionKind.LANGUAGE_VERSION -> ProtoBuf.VersionRequirement.VersionKind.LANGUAGE_VERSION
KmVersionRequirementVersionKind.COMPILER_VERSION -> ProtoBuf.VersionRequirement.VersionKind.COMPILER_VERSION
KmVersionRequirementVersionKind.API_VERSION -> ProtoBuf.VersionRequirement.VersionKind.API_VERSION
}
}
if (level != defaultInstanceForType.level) {
this.level = when (level) {
KmVersionRequirementLevel.WARNING -> ProtoBuf.VersionRequirement.Level.WARNING
KmVersionRequirementLevel.ERROR -> ProtoBuf.VersionRequirement.Level.ERROR
KmVersionRequirementLevel.HIDDEN -> ProtoBuf.VersionRequirement.Level.HIDDEN
}
}
if (errorCode != null) {
this.errorCode = errorCode
}
if (message != null) {
this.message = c[message]
}
}
}
override fun visitVersion(major: Int, minor: Int, patch: Int) {
if (t == null) {
throw IllegalStateException("KmVersionRequirementVisitor.visit has not been called")
}
VersionRequirement.Version(major, minor, patch).encode(
writeVersion = { t!!.version = it },
writeVersionFull = { t!!.versionFull = it }
)
}
override fun visitEnd() {
if (t == null) {
throw IllegalStateException("KmVersionRequirementVisitor.visit has not been called")
}
output(c.versionRequirements[t!!])
}
}
private fun writeContract(c: WriteContext, output: (ProtoBuf.Contract.Builder) -> Unit): KmContractVisitor =
object : KmContractVisitor() {
val t = ProtoBuf.Contract.newBuilder()
override fun visitEffect(type: KmEffectType, invocationKind: KmEffectInvocationKind?): KmEffectVisitor? =
writeEffect(c, type, invocationKind) { t.addEffect(it) }
override fun visitEnd() {
output(t)
}
}
private fun writeEffect(
c: WriteContext, type: KmEffectType, invocationKind: KmEffectInvocationKind?,
output: (ProtoBuf.Effect.Builder) -> Unit
): KmEffectVisitor = object : KmEffectVisitor() {
val t = ProtoBuf.Effect.newBuilder()
override fun visitConstructorArgument(): KmEffectExpressionVisitor? =
writeEffectExpression(c) { t.addEffectConstructorArgument(it) }
override fun visitConclusionOfConditionalEffect(): KmEffectExpressionVisitor? =
writeEffectExpression(c) { t.conclusionOfConditionalEffect = it.build() }
@Suppress("UNUSED_VARIABLE") // force exhaustive whens
override fun visitEnd() {
val unused = when (type) {
KmEffectType.RETURNS_CONSTANT -> t.effectType = ProtoBuf.Effect.EffectType.RETURNS_CONSTANT
KmEffectType.CALLS -> t.effectType = ProtoBuf.Effect.EffectType.CALLS
KmEffectType.RETURNS_NOT_NULL -> t.effectType = ProtoBuf.Effect.EffectType.RETURNS_NOT_NULL
}
val unused2 = when (invocationKind) {
KmEffectInvocationKind.AT_MOST_ONCE -> t.kind = ProtoBuf.Effect.InvocationKind.AT_MOST_ONCE
KmEffectInvocationKind.EXACTLY_ONCE -> t.kind = ProtoBuf.Effect.InvocationKind.EXACTLY_ONCE
KmEffectInvocationKind.AT_LEAST_ONCE -> t.kind = ProtoBuf.Effect.InvocationKind.AT_LEAST_ONCE
null -> null
}
output(t)
}
}
private fun writeEffectExpression(c: WriteContext, output: (ProtoBuf.Expression.Builder) -> Unit): KmEffectExpressionVisitor =
object : KmEffectExpressionVisitor() {
val t = ProtoBuf.Expression.newBuilder()
override fun visit(flags: Int, parameterIndex: Int?) {
if (flags != ProtoBuf.Expression.getDefaultInstance().flags) {
t.flags = flags
}
if (parameterIndex != null) {
t.valueParameterReference = parameterIndex
}
}
override fun visitConstantValue(value: Any?) {
@Suppress("UNUSED_VARIABLE") // force exhaustive when
val unused = when (value) {
true -> t.constantValue = ProtoBuf.Expression.ConstantValue.TRUE
false -> t.constantValue = ProtoBuf.Expression.ConstantValue.FALSE
null -> null
else -> throw IllegalArgumentException("Only true, false or null constant values are allowed for effects (was=$value)")
}
}
override fun visitIsInstanceType(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.isInstanceType = it.build() }
override fun visitAndArgument(): KmEffectExpressionVisitor? =
writeEffectExpression(c) { t.addAndArgument(it) }
override fun visitOrArgument(): KmEffectExpressionVisitor? =
writeEffectExpression(c) { t.addOrArgument(it) }
override fun visitEnd() {
output(t)
}
}
open class ClassWriter : KmClassVisitor() {
val t = ProtoBuf.Class.newBuilder()!!
val c = WriteContext()
override fun visit(flags: Int, name: ClassName) {
if (flags != ProtoBuf.Class.getDefaultInstance().flags) {
t.flags = flags
}
t.fqName = c.getClassName(name)
}
override fun visitTypeParameter(flags: Int, name: String, id: Int, variance: KmVariance): KmTypeParameterVisitor? =
writeTypeParameter(c, flags, name, id, variance) { t.addTypeParameter(it) }
override fun visitSupertype(flags: Int): KmTypeVisitor? =
writeType(c, flags) { t.addSupertype(it) }
override fun visitConstructor(flags: Int): KmConstructorVisitor? =
writeConstructor(c, flags) { t.addConstructor(it) }
override fun visitFunction(flags: Int, name: String): KmFunctionVisitor? =
writeFunction(c, flags, name) { t.addFunction(it) }
override fun visitProperty(flags: Int, name: String, getterFlags: Int, setterFlags: Int): KmPropertyVisitor? =
writeProperty(c, flags, name, getterFlags, setterFlags) { t.addProperty(it) }
override fun visitTypeAlias(flags: Int, name: String): KmTypeAliasVisitor? =
writeTypeAlias(c, flags, name) { t.addTypeAlias(it) }
override fun visitCompanionObject(name: String) {
t.companionObjectName = c[name]
}
override fun visitNestedClass(name: String) {
t.addNestedClassName(c[name])
}
override fun visitEnumEntry(name: String) {
t.addEnumEntry(ProtoBuf.EnumEntry.newBuilder().also { enumEntry ->
enumEntry.name = c[name]
})
}
override fun visitSealedSubclass(name: ClassName) {
t.addSealedSubclassFqName(c.getClassName(name))
}
override fun visitVersionRequirement(): KmVersionRequirementVisitor? =
writeVersionRequirement(c) { t.versionRequirement = it }
override fun visitEnd() {
c.versionRequirements.serialize()?.let {
t.versionRequirementTable = it
}
}
}
open class PackageWriter : KmPackageVisitor() {
val t = ProtoBuf.Package.newBuilder()!!
val c = WriteContext()
override fun visitFunction(flags: Int, name: String): KmFunctionVisitor? =
writeFunction(c, flags, name) { t.addFunction(it) }
override fun visitProperty(flags: Int, name: String, getterFlags: Int, setterFlags: Int): KmPropertyVisitor? =
writeProperty(c, flags, name, getterFlags, setterFlags) { t.addProperty(it) }
override fun visitTypeAlias(flags: Int, name: String): KmTypeAliasVisitor? =
writeTypeAlias(c, flags, name) { t.addTypeAlias(it) }
override fun visitEnd() {
c.versionRequirements.serialize()?.let {
t.versionRequirementTable = it
}
}
}
open class LambdaWriter : KmLambdaVisitor() {
var t: ProtoBuf.Function.Builder? = null
val c = WriteContext()
override fun visitFunction(flags: Int, name: String): KmFunctionVisitor? =
writeFunction(c, flags, name) { t = it }
}
@@ -10,6 +10,6 @@ import java.io.File
abstract class AbstractKotlinpTest : TestCaseWithTmpdir() { abstract class AbstractKotlinpTest : TestCaseWithTmpdir() {
protected fun doTest(fileName: String) { protected fun doTest(fileName: String) {
compileAndPrintAllFiles(File(fileName), testRootDisposable, tmpdir, compareWithTxt = true) compileAndPrintAllFiles(File(fileName), testRootDisposable, tmpdir, compareWithTxt = true, readWriteAndCompare = true)
} }
} }
@@ -6,6 +6,8 @@
package org.jetbrains.kotlin.kotlinp.test package org.jetbrains.kotlin.kotlinp.test
import com.intellij.openapi.Disposable import com.intellij.openapi.Disposable
import junit.framework.TestCase.assertEquals
import kotlinx.metadata.jvm.KotlinClassMetadata
import org.jetbrains.kotlin.checkers.setupLanguageVersionSettingsForCompilerTests import org.jetbrains.kotlin.checkers.setupLanguageVersionSettingsForCompilerTests
import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles import org.jetbrains.kotlin.cli.jvm.compiler.EnvironmentConfigFiles
import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment import org.jetbrains.kotlin.cli.jvm.compiler.KotlinCoreEnvironment
@@ -17,8 +19,9 @@ import org.jetbrains.kotlin.test.TestJdkKind
import java.io.File import java.io.File
import kotlin.test.fail import kotlin.test.fail
fun compileAndPrintAllFiles(file: File, disposable: Disposable, tmpdir: File, compareWithTxt: Boolean) { fun compileAndPrintAllFiles(file: File, disposable: Disposable, tmpdir: File, compareWithTxt: Boolean, readWriteAndCompare: Boolean) {
val sb = StringBuilder() val read = StringBuilder()
val readWriteRead = StringBuilder()
compile(file, disposable, tmpdir) { outputFile -> compile(file, disposable, tmpdir) { outputFile ->
when (outputFile.extension) { when (outputFile.extension) {
@@ -26,15 +29,24 @@ fun compileAndPrintAllFiles(file: File, disposable: Disposable, tmpdir: File, co
// TODO: support kotlin_module files // TODO: support kotlin_module files
} }
"class" -> { "class" -> {
sb.appendFileName(outputFile.relativeTo(tmpdir)) val classFile = Kotlinp.readClassFile(outputFile)!!
sb.append(Kotlinp.renderClassFile(Kotlinp.readClassFile(outputFile)!!)) val classFile2 = transformClassFile(classFile)
for ((sb, classFileToRender) in listOf(read to classFile, readWriteRead to classFile2)) {
sb.appendFileName(outputFile.relativeTo(tmpdir))
sb.append(Kotlinp.renderClassFile(classFileToRender))
}
} }
else -> fail("Unknown file: $outputFile") else -> fail("Unknown file: $outputFile")
} }
} }
if (compareWithTxt) { if (compareWithTxt) {
KotlinTestUtils.assertEqualsToFile(File(file.path.replace(".kt", ".txt")), sb.toString()) KotlinTestUtils.assertEqualsToFile(File(file.path.replace(".kt", ".txt")), read.toString())
}
if (readWriteAndCompare) {
assertEquals(read.toString(), readWriteRead.toString())
} }
} }
@@ -59,3 +71,23 @@ private fun StringBuilder.appendFileName(file: File) {
appendln("// $file") appendln("// $file")
appendln("// ------------------------------------------") appendln("// ------------------------------------------")
} }
// Reads the class file and writes it back with *Writer visitors.
// The resulting class file should be the same from the point of view of any metadata reader, including kotlinp
// (the exact bytes may differ though, because there are multiple ways to encode the same metadata)
private fun transformClassFile(classFile: KotlinClassMetadata): KotlinClassMetadata =
when (classFile) {
is KotlinClassMetadata.Class -> KotlinClassMetadata.Class.Writer().apply(classFile::accept).write()
is KotlinClassMetadata.FileFacade -> KotlinClassMetadata.FileFacade.Writer().apply(classFile::accept).write()
is KotlinClassMetadata.SyntheticClass -> {
val writer = KotlinClassMetadata.SyntheticClass.Writer()
if (classFile.isLambda) {
classFile.accept(writer)
}
writer.write()
}
is KotlinClassMetadata.MultiFileClassFacade -> KotlinClassMetadata.MultiFileClassFacade.Writer().write(classFile.partClassNames)
is KotlinClassMetadata.MultiFileClassPart ->
KotlinClassMetadata.MultiFileClassPart.Writer().apply(classFile::accept).write(classFile.facadeClassName)
else -> classFile
}