Extract separate ArrayKClassValue from KClassValue

Remove `data` from KmAnnotation
This commit is contained in:
Leonid Startsev
2023-01-19 18:52:31 +01:00
committed by Space Team
parent 7f8dc56d36
commit 64838d5ec9
5 changed files with 150 additions and 15 deletions
@@ -128,10 +128,6 @@ public final class kotlinx/metadata/InconsistentKotlinMetadataException : java/l
public final class kotlinx/metadata/KmAnnotation {
public fun <init> (Ljava/lang/String;Ljava/util/Map;)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()Ljava/util/Map;
public final fun copy (Ljava/lang/String;Ljava/util/Map;)Lkotlinx/metadata/KmAnnotation;
public static synthetic fun copy$default (Lkotlinx/metadata/KmAnnotation;Ljava/lang/String;Ljava/util/Map;ILjava/lang/Object;)Lkotlinx/metadata/KmAnnotation;
public fun equals (Ljava/lang/Object;)Z
public final fun getArguments ()Ljava/util/Map;
public final fun getClassName ()Ljava/lang/String;
@@ -153,6 +149,19 @@ public final class kotlinx/metadata/KmAnnotationArgument$AnnotationValue : kotli
public fun toString ()Ljava/lang/String;
}
public final class kotlinx/metadata/KmAnnotationArgument$ArrayKClassValue : kotlinx/metadata/KmAnnotationArgument {
public fun <init> (Ljava/lang/String;I)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()I
public final fun copy (Ljava/lang/String;I)Lkotlinx/metadata/KmAnnotationArgument$ArrayKClassValue;
public static synthetic fun copy$default (Lkotlinx/metadata/KmAnnotationArgument$ArrayKClassValue;Ljava/lang/String;IILjava/lang/Object;)Lkotlinx/metadata/KmAnnotationArgument$ArrayKClassValue;
public fun equals (Ljava/lang/Object;)Z
public final fun getArrayDimensionCount ()I
public final fun getClassName ()Ljava/lang/String;
public fun hashCode ()I
public fun toString ()Ljava/lang/String;
}
public final class kotlinx/metadata/KmAnnotationArgument$ArrayValue : kotlinx/metadata/KmAnnotationArgument {
public fun <init> (Ljava/util/List;)V
public final fun component1 ()Ljava/util/List;
@@ -250,6 +259,7 @@ public final class kotlinx/metadata/KmAnnotationArgument$IntValue : kotlinx/meta
}
public final class kotlinx/metadata/KmAnnotationArgument$KClassValue : kotlinx/metadata/KmAnnotationArgument {
public fun <init> (Ljava/lang/String;)V
public fun <init> (Ljava/lang/String;I)V
public final fun component1 ()Ljava/lang/String;
public final fun component2 ()I
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -9,22 +9,37 @@ package kotlinx.metadata
* Represents an annotation, written to the Kotlin metadata. Note that not all annotations are written to metadata on all platforms.
* For example, on JVM most of the annotations are written directly on the corresponding declarations in the class file,
* and entries in the metadata only have a flag ([Flag.HAS_ANNOTATIONS]) to signal if they do have annotations in the bytecode.
* On JVM, only annotations on type parameters and types are serialized to the Kotlin metadata.
* On JVM, only annotations on type parameters and types are serialized to the Kotlin metadata
* (see `KmType.annotations` and `KmTypeParameter.annotations` JVM extensions)
*
* @param className the fully qualified name of the annotation class
* @param arguments explicitly specified arguments to the annotation; does not include default values for annotation parameters
* (specified in the annotation class declaration)
*/
data class KmAnnotation(val className: ClassName, val arguments: Map<String, KmAnnotationArgument>)
class KmAnnotation(val className: ClassName, val arguments: Map<String, KmAnnotationArgument>) {
override fun equals(other: Any?): Boolean =
this === other || other is KmAnnotation && className == other.className && arguments == other.arguments
override fun hashCode(): Int = 31 * className.hashCode() + arguments.hashCode()
override fun toString(): String {
val args = arguments.toList().joinToString { (k, v) -> "$k = $v" }
return "@$className($args)"
}
}
/**
* Represents an argument to the annotation.
*/
@Suppress("IncorrectFormatting") // one-line KDoc
sealed class KmAnnotationArgument {
/**
* A kind of annotation argument, whose value is directly accessible via [value].
* This is possible for annotation arguments of primitive types, unsigned types and strings.
*
* For example, in `@Foo("bar")`, argument of `Foo` is a [StringValue] with [value] equal to `bar`.
*
* @param T the type of the value of this argument
*/
sealed class LiteralValue<out T : Any> : KmAnnotationArgument() {
@@ -34,29 +49,129 @@ sealed class KmAnnotationArgument {
abstract val value: T
}
// For all inheritors of LiteralValue: KDoc is automatically copied from base property `value`
// to the overridden one. However, it does not do this with classes, and we do not have `@inheritdoc` :(
/** An annotation argument with a [Byte] type. */
data class ByteValue(override val value: Byte) : LiteralValue<Byte>()
/** An annotation argument with a [Char] type. */
data class CharValue(override val value: Char) : LiteralValue<Char>()
/** An annotation argument with a [Short] type. */
data class ShortValue(override val value: Short) : LiteralValue<Short>()
/** An annotation argument with a [Int] type. */
data class IntValue(override val value: Int) : LiteralValue<Int>()
/** An annotation argument with a [Long] type. */
data class LongValue(override val value: Long) : LiteralValue<Long>()
/** An annotation argument with a [Float] type. */
data class FloatValue(override val value: Float) : LiteralValue<Float>()
/** An annotation argument with a [Double] type. */
data class DoubleValue(override val value: Double) : LiteralValue<Double>()
/** An annotation argument with a [Boolean] type. */
data class BooleanValue(override val value: Boolean) : LiteralValue<Boolean>()
/** An annotation argument with a [UByte] type. */
data class UByteValue(override val value: UByte) : LiteralValue<UByte>()
/** An annotation argument with a [UShort] type. */
data class UShortValue(override val value: UShort) : LiteralValue<UShort>()
/** An annotation argument with a [UInt] type. */
data class UIntValue(override val value: UInt) : LiteralValue<UInt>()
/** An annotation argument with a [ULong] type. */
data class ULongValue(override val value: ULong) : LiteralValue<ULong>()
/** An annotation argument with a [String] type. */
data class StringValue(override val value: String) : LiteralValue<String>()
data class KClassValue(val className: ClassName, val arrayDimensionCount: Int) : KmAnnotationArgument()
/**
* An annotation argument with an enumeration type.
*
* For example, in `@Foo(MyEnum.OPTION_A)`, argument of `Foo` is an `EnumValue`
* with [enumClassName] `MyEnum` and [enumEntryName] `OPTION_A`.
*
* @property enumClassName FQ name of the enum class
* @property enumEntryName Name of the enum entry
*/
data class EnumValue(val enumClassName: ClassName, val enumEntryName: String) : KmAnnotationArgument()
/**
* An annotation argument which is another annotation value.
*
* For example, with the following classes:
* ```
* annotation class Bar(val s: String)
*
* annotation class Foo(val b: Bar)
* ```
* It is possible to apply such annotation: `@Foo(Bar("baz"))`. In this case, argument `Foo.b` is represented by
* `AnnotationValue` which [annotation] property contains all necessary information: the fact that it is a `Bar` annotation ([KmAnnotation.className])
* and that it has a "baz" argument ([KmAnnotation.arguments]).
*
* @property annotation Annotation instance with all its arguments.
*/
data class AnnotationValue(val annotation: KmAnnotation) : KmAnnotationArgument()
/**
* An annotation argument with an array type, i.e. several values of one arbitrary type.
*
* For example, in `@Foo(["a", "b", "c"])` argument of `Foo` is an `ArrayValue` with [elements]
* being a list of three [StringValue] elements: "a", "b", and "c" (without quotes).
*
* Don't confuse with [ArrayKClassValue], which represents KClass value.
*
* @property elements Values of elements in the array.
*/
data class ArrayValue(val elements: List<KmAnnotationArgument>) : KmAnnotationArgument()
/**
* An annotation argument of KClass type.
*
* For example, in `@Foo(String::class)` argument of `Foo` is a `KClassValue` which [className] is `kotlin/String`.
*
* All the KClasses, except `kotlin.Array`, are represented by this class.
* Arrays are a specific case and represented by [ArrayKClassValue] — see its documentation for details.
*
* @property className FQ name of the referenced class.
*/
@Suppress("DEPRECATION_ERROR")
data class KClassValue @Deprecated(
"Use single-argument constructor instead or ArrayKClassValue",
level = DeprecationLevel.ERROR
) constructor(
val className: ClassName,
@Deprecated(
"Use ArrayKClassValue instead",
level = DeprecationLevel.ERROR
) val arrayDimensionCount: Int
) : KmAnnotationArgument() {
constructor(className: ClassName) : this(className, 0)
init {
require(arrayDimensionCount == 0) { "KClassValue must not have array dimensions. For Array<X>::class, use ArrayKClassValue." }
}
}
/**
* Annotation argument whose type is one of the `kotlin.Array` KClasses.
*
* Due to the nature of JVM, Arrays with different arguments are represented by different `kotlin.reflect.KClass` and `java.lang.Class` instances
* (while e.g. `List` has always one KClass instance regardless of generic arguments).
* As a result, Kotlin compiler allows to use generic arguments for the arrays in annotations: `@Foo(Array<Array<String>>::class)`.
* [ArrayKClassValue] allows to distinguish such arguments from regular [KClassValue].
*
* [className] is the array element type's fully qualified name — in the example above, it is `kotlin/String`.
* [arrayDimensionCount] is the dimension of the array — 1 for `Array<String>::class`, 2 for `Array<Array<String>>::class`, and so on.
* It is guaranteed to be at least 1.
*
* For platforms other than JVM, the value for [className] is always `kotlin/Any` and [arrayDimensionCount] is always 1.
* This represents untyped `Array::class` expression.
*
* See also: https://youtrack.jetbrains.com/issue/KT-31230
*
* @property className FQ name of the referenced array element type.
* @property arrayDimensionCount Referenced array dimension.
*/
data class ArrayKClassValue(val className: ClassName, val arrayDimensionCount: Int) : KmAnnotationArgument() {
init {
require(arrayDimensionCount > 0) { "ArrayKClassValue must have at least one dimension. For regular X::class argument, use KClassValue." }
}
}
}
@@ -1,5 +1,5 @@
/*
* Copyright 2010-2018 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
@@ -44,7 +44,12 @@ fun ProtoBuf.Annotation.Argument.Value.readAnnotationArgument(strings: NameResol
DOUBLE -> KmAnnotationArgument.DoubleValue(doubleValue)
BOOLEAN -> KmAnnotationArgument.BooleanValue(intValue != 0L)
STRING -> KmAnnotationArgument.StringValue(strings.getString(stringValue))
CLASS -> KmAnnotationArgument.KClassValue(strings.getClassName(classId), arrayDimensionCount)
CLASS -> strings.getClassName(classId).let { className ->
if (arrayDimensionCount == 0)
KmAnnotationArgument.KClassValue(className)
else
KmAnnotationArgument.ArrayKClassValue(className, arrayDimensionCount)
}
ENUM -> KmAnnotationArgument.EnumValue(strings.getClassName(classId), strings.getString(enumValueId))
ANNOTATION -> KmAnnotationArgument.AnnotationValue(annotation.readAnnotation(strings))
ARRAY -> KmAnnotationArgument.ArrayValue(arrayElementList.mapNotNull { it.readAnnotationArgument(strings) })
@@ -86,6 +86,10 @@ fun KmAnnotationArgument.writeAnnotationArgument(strings: StringTable): ProtoBuf
is KmAnnotationArgument.KClassValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.CLASS
this.classId = strings.getClassNameIndex(className)
}
is KmAnnotationArgument.ArrayKClassValue -> {
this.type = ProtoBuf.Annotation.Argument.Value.Type.CLASS
this.classId = strings.getClassNameIndex(className)
this.arrayDimensionCount = this@writeAnnotationArgument.arrayDimensionCount
}
is KmAnnotationArgument.EnumValue -> {
@@ -285,7 +285,8 @@ private fun renderAnnotationArgument(arg: KmAnnotationArgument): String =
is KmAnnotationArgument.ULongValue -> arg.value.toString() + "uL"
is KmAnnotationArgument.BooleanValue -> arg.value.toString()
is KmAnnotationArgument.StringValue -> "\"${arg.value.sanitize(quote = '"')}\""
is KmAnnotationArgument.KClassValue -> buildString {
is KmAnnotationArgument.KClassValue -> "${arg.className}::class"
is KmAnnotationArgument.ArrayKClassValue -> buildString {
repeat(arg.arrayDimensionCount) { append("kotlin/Array<") }
append(arg.className).append("::class")
repeat(arg.arrayDimensionCount) { append(">") }