Support local delegated properties in kotlinx-metadata-jvm

This commit is contained in:
Alexander Udalov
2018-06-13 19:17:02 +02:00
parent b00f765255
commit fec2c14ea5
12 changed files with 330 additions and 20 deletions
@@ -1,5 +1,9 @@
# kotlinx-metadata-jvm
## 0.0.3
- Support metadata of local delegated properties (see `JvmDeclarationContainerExtensionVisitor.visitLocalDelegatedProperty`)
## 0.0.2
- Change group ID from `org.jetbrains.kotlin` to `org.jetbrains.kotlinx`
@@ -6,11 +6,8 @@
package kotlinx.metadata.jvm.impl
import kotlinx.metadata.*
import kotlinx.metadata.impl.ReadContext
import kotlinx.metadata.impl.WriteContext
import kotlinx.metadata.impl.*
import kotlinx.metadata.impl.extensions.MetadataExtensions
import kotlinx.metadata.impl.readAnnotation
import kotlinx.metadata.impl.writeAnnotation
import kotlinx.metadata.jvm.*
import org.jetbrains.kotlin.metadata.ProtoBuf
import org.jetbrains.kotlin.metadata.deserialization.getExtensionOrNull
@@ -25,6 +22,26 @@ internal class JvmMetadataExtensions : MetadataExtensions {
if (anonymousObjectOriginName != null) {
ext.visitAnonymousObjectOriginName(c[anonymousObjectOriginName])
}
for (property in proto.getExtension(JvmProtoBuf.classLocalVariable)) {
ext.visitLocalDelegatedProperty(
property.flags, c[property.name], property.getPropertyGetterFlags(), property.getPropertySetterFlags()
)?.let { property.accept(it, c) }
}
ext.visitEnd()
}
override fun readPackageExtensions(v: KmPackageVisitor, proto: ProtoBuf.Package, c: ReadContext) {
val ext = v.visitExtensions(JvmPackageExtensionVisitor.TYPE) as? JvmPackageExtensionVisitor ?: return
for (property in proto.getExtension(JvmProtoBuf.packageLocalVariable)) {
ext.visitLocalDelegatedProperty(
property.flags, c[property.name], property.getPropertyGetterFlags(), property.getPropertySetterFlags()
)?.let { property.accept(it, c) }
}
ext.visitEnd()
}
override fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, c: ReadContext) {
@@ -86,6 +103,25 @@ internal class JvmMetadataExtensions : MetadataExtensions {
override fun visitAnonymousObjectOriginName(internalName: String) {
proto.setExtension(JvmProtoBuf.anonymousObjectOriginName, c[internalName])
}
override fun visitLocalDelegatedProperty(
flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags
): KmPropertyVisitor = writeProperty(c, flags, name, getterFlags, setterFlags) {
proto.addExtension(JvmProtoBuf.classLocalVariable, it.build())
}
}
}
override fun writePackageExtensions(
type: KmExtensionType, proto: ProtoBuf.Package.Builder, c: WriteContext
): KmPackageExtensionVisitor? {
if (type != JvmPackageExtensionVisitor.TYPE) return null
return object : JvmPackageExtensionVisitor() {
override fun visitLocalDelegatedProperty(
flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags
): KmPropertyVisitor = writeProperty(c, flags, name, getterFlags, setterFlags) {
proto.addExtension(JvmProtoBuf.packageLocalVariable, it.build())
}
}
}
@@ -9,11 +9,40 @@ import kotlinx.metadata.*
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
/**
* A visitor to visit JVM extensions for a function.
* A visitor containing the common code to visit JVM extensions for Kotlin declaration containers, such as classes and package fragments.
*/
abstract class JvmDeclarationContainerExtensionVisitor @JvmOverloads constructor(
protected open val delegate: JvmDeclarationContainerExtensionVisitor? = null
) : KmDeclarationContainerExtensionVisitor {
/**
* Visits the metadata of a local delegated property used somewhere inside this container (but not in a nested declaration container).
* Note that for classes produced by the Kotlin compiler, such properties will have default accessors.
*
* The order of visited local delegated properties is important. The Kotlin compiler generates the corresponding property's index
* at the call site, so that reflection would be able to load the metadata of the property with that index at runtime.
* If an incorrect index is used, either the `KProperty<*>` object passed to delegate methods will point to the wrong property
* at runtime, or an exception will be thrown.
*
* @param flags property flags, consisting of [Flag.HAS_ANNOTATIONS], visibility flag, modality flag and [Flag.Property] flags
* @param name the name of the property
* @param getterFlags property accessor flags, consisting of [Flag.HAS_ANNOTATIONS], visibility flag, modality flag
* and [Flag.PropertyAccessor] flags
* @param setterFlags property accessor flags, consisting of [Flag.HAS_ANNOTATIONS], visibility flag, modality flag
* and [Flag.PropertyAccessor] flags
*/
open fun visitLocalDelegatedProperty(flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags): KmPropertyVisitor? =
delegate?.visitLocalDelegatedProperty(flags, name, getterFlags, setterFlags)
}
/**
* A visitor to visit JVM extensions for a class.
*/
open class JvmClassExtensionVisitor @JvmOverloads constructor(
private val delegate: JvmClassExtensionVisitor? = null
) : KmClassExtensionVisitor {
delegate: JvmClassExtensionVisitor? = null
) : KmClassExtensionVisitor, JvmDeclarationContainerExtensionVisitor(delegate) {
override val delegate: JvmClassExtensionVisitor?
get() = super.delegate as JvmClassExtensionVisitor?
/**
* Visits the JVM internal name of the original class this anonymous object is copied from. This method is called for
* anonymous objects copied from bodies of inline functions to the use site by the Kotlin compiler.
@@ -22,6 +51,13 @@ open class JvmClassExtensionVisitor @JvmOverloads constructor(
delegate?.visitAnonymousObjectOriginName(internalName)
}
/**
* Visits the end of JVM extensions for the class.
*/
open fun visitEnd() {
delegate?.visitEnd()
}
companion object {
/**
* The type of this extension visitor.
@@ -33,6 +69,33 @@ open class JvmClassExtensionVisitor @JvmOverloads constructor(
}
}
/**
* A visitor to visit JVM extensions for a package fragment.
*/
open class JvmPackageExtensionVisitor @JvmOverloads constructor(
delegate: JvmPackageExtensionVisitor? = null
) : KmPackageExtensionVisitor, JvmDeclarationContainerExtensionVisitor(delegate) {
override val delegate: JvmPackageExtensionVisitor?
get() = super.delegate as JvmPackageExtensionVisitor?
/**
* Visits the end of JVM extensions for the package fragment.
*/
open fun visitEnd() {
delegate?.visitEnd()
}
companion object {
/**
* The type of this extension visitor.
*
* @see KmExtensionType
*/
@JvmField
val TYPE: KmExtensionType = KmExtensionType(JvmPackageExtensionVisitor::class)
}
}
/**
* A visitor to visit JVM extensions for a function.
*/
@@ -35,10 +35,20 @@ data class KmExtensionType(val klass: KClass<out KmExtensionVisitor>)
*/
interface KmExtensionVisitor
/**
* A visitor to visit platform-specific extensions for a declaration container, such as a class or a package fragment.
*/
interface KmDeclarationContainerExtensionVisitor : KmExtensionVisitor
/**
* A visitor to visit platform-specific extensions for a class.
*/
interface KmClassExtensionVisitor : KmExtensionVisitor
interface KmClassExtensionVisitor : KmDeclarationContainerExtensionVisitor
/**
* A visitor to visit platform-specific extensions for a package fragment.
*/
interface KmPackageExtensionVisitor : KmDeclarationContainerExtensionVisitor
/**
* A visitor to visit platform-specific extensions for a function.
@@ -14,6 +14,8 @@ import java.util.*
interface MetadataExtensions {
fun readClassExtensions(v: KmClassVisitor, proto: ProtoBuf.Class, c: ReadContext)
fun readPackageExtensions(v: KmPackageVisitor, proto: ProtoBuf.Package, c: ReadContext)
fun readFunctionExtensions(v: KmFunctionVisitor, proto: ProtoBuf.Function, c: ReadContext)
fun readPropertyExtensions(v: KmPropertyVisitor, proto: ProtoBuf.Property, c: ReadContext)
@@ -26,6 +28,8 @@ interface MetadataExtensions {
fun writeClassExtensions(type: KmExtensionType, proto: ProtoBuf.Class.Builder, c: WriteContext): KmClassExtensionVisitor?
fun writePackageExtensions(type: KmExtensionType, proto: ProtoBuf.Package.Builder, c: WriteContext): KmPackageExtensionVisitor?
fun writeFunctionExtensions(type: KmExtensionType, proto: ProtoBuf.Function.Builder, c: WriteContext): KmFunctionExtensionVisitor?
fun writePropertyExtensions(type: KmExtensionType, proto: ProtoBuf.Property.Builder, c: WriteContext): KmPropertyExtensionVisitor?
@@ -93,6 +93,10 @@ fun ProtoBuf.Package.accept(v: KmPackageVisitor, strings: NameResolver) {
v.visitDeclarations(functionList, propertyList, typeAliasList, c)
for (extension in c.extensions) {
extension.readPackageExtensions(v, this, c)
}
v.visitEnd()
}
@@ -107,15 +111,8 @@ private fun KmDeclarationContainerVisitor.visitDeclarations(
}
for (property in properties) {
val flags = property.flags
val defaultAccessorFlags = F.getAccessorFlags(
F.HAS_ANNOTATIONS.get(flags), F.VISIBILITY.get(flags), F.MODALITY.get(flags), false, false, false
)
visitProperty(
flags,
c[property.name],
if (property.hasGetterFlags()) property.getterFlags else defaultAccessorFlags,
if (property.hasSetterFlags()) property.setterFlags else defaultAccessorFlags
property.flags, c[property.name], property.getPropertyGetterFlags(), property.getPropertySetterFlags()
)?.let { property.accept(it, c) }
}
@@ -182,7 +179,7 @@ private fun ProtoBuf.Function.accept(v: KmFunctionVisitor, outer: ReadContext) {
v.visitEnd()
}
private fun ProtoBuf.Property.accept(v: KmPropertyVisitor, outer: ReadContext) {
fun ProtoBuf.Property.accept(v: KmPropertyVisitor, outer: ReadContext) {
val c = outer.withTypeParameters(typeParameterList)
for (typeParameter in typeParameterList) {
@@ -425,3 +422,12 @@ private val ProtoBuf.Type.typeFlags: Flags
private val ProtoBuf.TypeParameter.typeParameterFlags: Flags
get() = if (reified) 1 else 0
fun ProtoBuf.Property.getPropertyGetterFlags(): Flags =
if (hasGetterFlags()) getterFlags else getDefaultPropertyAccessorFlags(flags)
fun ProtoBuf.Property.getPropertySetterFlags(): Flags =
if (hasSetterFlags()) setterFlags else getDefaultPropertyAccessorFlags(flags)
private fun getDefaultPropertyAccessorFlags(flags: Flags): Flags =
F.getAccessorFlags(F.HAS_ANNOTATIONS.get(flags), F.VISIBILITY.get(flags), F.MODALITY.get(flags), false, false, false)
@@ -190,7 +190,7 @@ private fun writeFunction(c: WriteContext, flags: Flags, name: String, output: (
}
}
private fun writeProperty(
fun writeProperty(
c: WriteContext, flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags, output: (ProtoBuf.Property.Builder) -> Unit
): KmPropertyVisitor = object : KmPropertyVisitor() {
val t = ProtoBuf.Property.newBuilder()
@@ -480,6 +480,11 @@ open class PackageWriter(stringTable: StringTable) : KmPackageVisitor() {
override fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor? =
writeTypeAlias(c, flags, name) { t.addTypeAlias(it) }
override fun visitExtensions(type: KmExtensionType): KmPackageExtensionVisitor? =
c.applySingleExtension(type) {
writePackageExtensions(type, t, c)
}
override fun visitEnd() {
c.versionRequirements.serialize()?.let {
t.versionRequirementTable = it
@@ -6,7 +6,7 @@
package kotlinx.metadata
/**
* A visitor to visit Kotlin declarations, which are containers of other declarations: functions, properties and type aliases.
* A visitor containing the common code to visit Kotlin declaration containers, such as classes and package fragments.
*/
abstract class KmDeclarationContainerVisitor @JvmOverloads constructor(protected open val delegate: KmDeclarationContainerVisitor? = null) {
/**
@@ -39,6 +39,13 @@ abstract class KmDeclarationContainerVisitor @JvmOverloads constructor(protected
*/
open fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor? =
delegate?.visitTypeAlias(flags, name)
/**
* Visits the extensions of the given type on the container.
*
* @param type the type of extension visitor to be returned
*/
abstract fun visitExtensions(type: KmExtensionType): KmDeclarationContainerExtensionVisitor?
}
/**
@@ -136,7 +143,7 @@ abstract class KmClassVisitor @JvmOverloads constructor(delegate: KmClassVisitor
*
* @param type the type of extension visitor to be returned
*/
open fun visitExtensions(type: KmExtensionType): KmClassExtensionVisitor? =
override fun visitExtensions(type: KmExtensionType): KmClassExtensionVisitor? =
delegate?.visitExtensions(type)
/**
@@ -156,6 +163,14 @@ abstract class KmPackageVisitor @JvmOverloads constructor(delegate: KmPackageVis
override val delegate: KmPackageVisitor?
get() = super.delegate as KmPackageVisitor?
/**
* Visits the extensions of the given type on the package fragment.
*
* @param type the type of extension visitor to be returned
*/
override fun visitExtensions(type: KmExtensionType): KmPackageExtensionVisitor? =
delegate?.visitExtensions(type)
/**
* Visits the end of the package fragment.
*/
@@ -509,6 +509,21 @@ private fun StringBuilder.appendFlags(flags: Flags, map: Map<Flag, String>) {
}
}
private fun StringBuilder.appendLocalDelegatedProperties(localDelegatedProperties: List<StringBuilder>) {
for ((i, sb) in localDelegatedProperties.withIndex()) {
appendln()
appendln(" // local delegated property #$i")
for (line in sb.lineSequence()) {
if (line.isBlank()) continue
// Comment all uncommented lines to not make it look like these properties are declared here
appendln(
if (line.startsWith(" ") && !line.startsWith(" //")) line.replaceFirst(" ", " // ")
else line
)
}
}
}
interface AbstractPrinter<in T : KotlinClassMetadata> {
fun print(klass: T): String
}
@@ -594,9 +609,21 @@ class ClassPrinter(private val settings: KotlinpSettings) : KmClassVisitor(), Ab
override fun visitExtensions(type: KmExtensionType): KmClassExtensionVisitor? {
if (type != JvmClassExtensionVisitor.TYPE) return null
return object : JvmClassExtensionVisitor() {
private val localDelegatedProperties = mutableListOf<StringBuilder>()
override fun visitAnonymousObjectOriginName(internalName: String) {
anonymousObjectOriginName = internalName
}
override fun visitLocalDelegatedProperty(
flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags
): KmPropertyVisitor? = visitProperty(
settings, StringBuilder().also { localDelegatedProperties.add(it) }, flags, name, getterFlags, setterFlags
)
override fun visitEnd() {
sb.appendLocalDelegatedProperties(localDelegatedProperties)
}
}
}
@@ -623,6 +650,23 @@ abstract class PackagePrinter(private val settings: KotlinpSettings) : KmPackage
override fun visitTypeAlias(flags: Flags, name: String): KmTypeAliasVisitor? =
visitTypeAlias(settings, sb, flags, name)
override fun visitExtensions(type: KmExtensionType): KmPackageExtensionVisitor? {
if (type != JvmPackageExtensionVisitor.TYPE) return null
return object : JvmPackageExtensionVisitor() {
private val localDelegatedProperties = mutableListOf<StringBuilder>()
override fun visitLocalDelegatedProperty(
flags: Flags, name: String, getterFlags: Flags, setterFlags: Flags
): KmPropertyVisitor? = visitProperty(
settings, StringBuilder().also { localDelegatedProperties.add(it) }, flags, name, getterFlags, setterFlags
)
override fun visitEnd() {
sb.appendLocalDelegatedProperties(localDelegatedProperties)
}
}
}
}
class FileFacadePrinter(settings: KotlinpSettings) : PackagePrinter(settings), AbstractPrinter<KotlinClassMetadata.FileFacade> {
@@ -44,6 +44,11 @@ public class KotlinpTestGenerated extends AbstractKotlinpTest {
runTest("libraries/tools/kotlinp/testData/LocalClass.kt");
}
@TestMetadata("LocalDelegatedProperties.kt")
public void testLocalDelegatedProperties() throws Exception {
runTest("libraries/tools/kotlinp/testData/LocalDelegatedProperties.kt");
}
@TestMetadata("MultiFileClass.kt")
public void testMultiFileClass() throws Exception {
runTest("libraries/tools/kotlinp/testData/MultiFileClass.kt");
@@ -0,0 +1,30 @@
import kotlin.reflect.KProperty
class Delegate<T>(val value: T? = null) {
operator fun getValue(instance: Any?, property: KProperty<*>): T = value!!
}
val nonLocal by Delegate<String>()
val init0 = run {
val local1 by Delegate<Double>()
val local2 by Delegate<Any>()
}
val init1 = run {
val local3 by Delegate<CharSequence?>()
}
class Class {
init {
val local4 by Delegate<Array<String>>()
}
fun f() {
val local5 by Delegate<List<Unit>?>()
fun g() {
val local6 by Delegate<Int>()
}
}
}
@@ -0,0 +1,88 @@
// Class.class
// ------------------------------------------
public final class Class : kotlin/Any {
// signature: <init>()V
public /* primary */ constructor()
// signature: f()V
public final fun f(): kotlin/Unit
// local delegated property #0
// field: local4:Lkotlin/Array;
// local final /* delegated */ val local4: kotlin/Array<kotlin/String>
// local final get
// local delegated property #1
// field: local5:Ljava/util/List;
// local final /* delegated */ val local5: kotlin/collections/List<kotlin/Unit>?
// local final get
// local delegated property #2
// field: local6:I
// local final /* delegated */ val local6: kotlin/Int
// local final get
}
// Class$f$1.class
// ------------------------------------------
lambda {
// signature: invoke()V
local final fun g(): kotlin/Unit
}
// Delegate.class
// ------------------------------------------
public final class Delegate<T#0 /* T */> : kotlin/Any {
// signature: <init>(Ljava/lang/Object;)V
public /* primary */ constructor(value: T#0? /* = ... */)
// signature: getValue(Ljava/lang/Object;Lkotlin/reflect/KProperty;)Ljava/lang/Object;
public final operator fun getValue(instance: kotlin/Any?, property: kotlin/reflect/KProperty<*>): T#0
// field: value:Ljava/lang/Object;
// getter: getValue()Ljava/lang/Object;
public final val value: T#0?
public final get
}
// LocalDelegatedPropertiesKt.class
// ------------------------------------------
package {
// field: init0:Lkotlin/Unit;
// getter: getInit0()Lkotlin/Unit;
public final val init0: kotlin/Unit
public final get
// field: init1:Lkotlin/Unit;
// getter: getInit1()Lkotlin/Unit;
public final val init1: kotlin/Unit
public final get
// field: nonLocal$delegate:LDelegate;
// getter: getNonLocal()Ljava/lang/String;
public final /* delegated */ val nonLocal: kotlin/String
public final /* non-default */ get
// local delegated property #0
// field: local1:D
// local final /* delegated */ val local1: kotlin/Double
// local final get
// local delegated property #1
// field: local2:Ljava/lang/Object;
// local final /* delegated */ val local2: kotlin/Any
// local final get
// local delegated property #2
// field: local3:Ljava/lang/CharSequence;
// local final /* delegated */ val local3: kotlin/CharSequence?
// local final get
}
// META-INF/test-module.kotlin_module
// ------------------------------------------
module {
package <root> {
LocalDelegatedPropertiesKt
}
}