kotlinx-metadata-jvm: Handle the jvmClassFlags extension

This commit is contained in:
Steven Schäfer
2021-10-12 12:17:42 +02:00
committed by Alexander Udalov
parent 9bc87b17bd
commit c552b5f820
6 changed files with 77 additions and 0 deletions
@@ -31,6 +31,31 @@ object JvmFlag {
val IS_MOVED_FROM_INTERFACE_COMPANION = booleanFlag(JF.IS_MOVED_FROM_INTERFACE_COMPANION)
}
/**
* JVM-specific class flags in addition to common class flags declared in [Flag.Class].
*/
object Class {
/**
* Applied to an interface compiled with -Xjvm-default=all or all-compatibility.
*
* Without this flag or a `@JvmDefault` annotation on individual interface methods
* the Kotlin compiler moves all interface method bodies into a nested `DefaultImpls`
* class.
*/
@JvmField
val HAS_METHOD_BODIES_IN_INTERFACE = booleanFlag(JF.ARE_INTERFACE_METHOD_BODIES_INSIDE)
/**
* Applied to an interface compiled with -Xjvm-default=all-compatibility.
*
* In compatibility mode we generate method bodies directly in the interface,
* but we also generate bridges in a nested `DefaultImpls` class for use by
* clients compiled without all-compatibility.
*/
@JvmField
val IS_COMPILED_IN_COMPATIBILITY_MODE = booleanFlag(JF.IS_ALL_COMPATIBILITY_MODE)
}
private fun booleanFlag(f: F.BooleanFlagField): Flag =
Flag(f.offset, f.bitWidth, 1)
}
@@ -31,6 +31,8 @@ internal class JvmMetadataExtensions : MetadataExtensions {
ext.visitModuleName(proto.getExtensionOrNull(JvmProtoBuf.classModuleName)?.let(c::get) ?: JvmProtoBufUtil.DEFAULT_MODULE_NAME)
proto.getExtensionOrNull(JvmProtoBuf.jvmClassFlags)?.let(ext::visitJvmFlags)
ext.visitEnd()
}
@@ -133,6 +135,12 @@ internal class JvmMetadataExtensions : MetadataExtensions {
proto.setExtension(JvmProtoBuf.classModuleName, c[name])
}
}
override fun visitJvmFlags(flags: Flags) {
if (flags != 0) {
proto.setExtension(JvmProtoBuf.jvmClassFlags, flags)
}
}
}
}
@@ -35,6 +35,7 @@ internal class JvmClassExtension : JvmClassExtensionVisitor(), KmClassExtension
val localDelegatedProperties: MutableList<KmProperty> = ArrayList(0)
var moduleName: String? = null
var anonymousObjectOriginName: String? = null
var jvmFlags: Flags = 0
override fun visitLocalDelegatedProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor =
KmProperty(flags, name, getterFlags, setterFlags).also { localDelegatedProperties.add(it) }
@@ -47,6 +48,10 @@ internal class JvmClassExtension : JvmClassExtensionVisitor(), KmClassExtension
this.anonymousObjectOriginName = internalName
}
override fun visitJvmFlags(flags: Flags) {
this.jvmFlags = flags
}
override fun accept(visitor: KmClassExtensionVisitor) {
require(visitor is JvmClassExtensionVisitor)
localDelegatedProperties.forEach {
@@ -54,6 +59,7 @@ internal class JvmClassExtension : JvmClassExtensionVisitor(), KmClassExtension
}
moduleName?.let(visitor::visitModuleName)
anonymousObjectOriginName?.let(visitor::visitAnonymousObjectOriginName)
jvmFlags.takeIf { it != 0 }?.let(visitor::visitJvmFlags)
visitor.visitEnd()
}
}
@@ -61,6 +61,13 @@ open class JvmClassExtensionVisitor @JvmOverloads constructor(
delegate?.visitAnonymousObjectOriginName(internalName)
}
/**
* Visits the JVM-specific flags of the class, consisting of [JvmFlag.Class] flags.
*/
open fun visitJvmFlags(flags: Flags) {
delegate?.visitJvmFlags(flags)
}
/**
* Visits the end of JVM extensions for the class.
*/
@@ -41,6 +41,15 @@ var KmClass.anonymousObjectOriginName: String?
jvm.anonymousObjectOriginName = value
}
/**
* JVM-specific flags of the class, consisting of [JvmFlag.Class] flags.
*/
var KmClass.jvmFlags: Flags
get() = jvm.jvmFlags
set(value) {
jvm.jvmFlags = value
}
/**
* Metadata of local delegated properties used somewhere inside this package fragment (but not in any class).
* Note that for classes produced by the Kotlin compiler, such properties will have default accessors.
@@ -214,4 +214,26 @@ class MetadataSmokeTest {
KotlinModuleMetadata.Writer().write(mv)
}
@Test
fun jvmClassFlags() {
// Test that we can (de-)serialize the jvmClassFlags extension. All the flags that currently
// exist are controlled by compiler options, so we have to manually create metadata with the
// flags set. Since the current flags only apply to interfaces with default functions we modify
// the metadata for the kotlin.coroutines.CoroutineContext interface.
val jvmClassFlags: Flags = flagsOf(
JvmFlag.Class.IS_COMPILED_IN_COMPATIBILITY_MODE,
JvmFlag.Class.HAS_METHOD_BODIES_IN_INTERFACE
)
val metadata = CoroutineContext::class.java.readMetadata()
val kmClass = (KotlinClassMetadata.read(metadata) as KotlinClassMetadata.Class).toKmClass()
kmClass.jvmFlags = jvmClassFlags
val kmClassCopy = KotlinClassMetadata.Class.Writer()
.also { kmClass.accept(it) }
.write(metadata.metadataVersion, metadata.extraInt)
.toKmClass()
assertEquals(kmClassCopy.jvmFlags, jvmClassFlags)
}
}