Rename JvmMetadataVersion.CURRENT to JvmMetadataVersion.LATEST_STABLE_SUPPORTED.

This commit is contained in:
Leonid Startsev
2023-11-23 18:17:59 +01:00
committed by Space Team
parent 048802e143
commit a035533316
10 changed files with 29 additions and 30 deletions
+10 -10
View File
@@ -144,12 +144,12 @@ val klass = KmClass().apply {
```
Then, you can put a resulting `KmClass`/`KmPackage`/`KmLambda` into an appropriate container and write it.
Pay attention to the metadata version. Usually, `JvmMetadataVersion.CURRENT` is a good choice, but you may want to write a specific version you obtained from existing Kotlin classfiles in your project.
Pay attention to the metadata version. Usually, `JvmMetadataVersion.LATEST_STABLE_SUPPORTED` is a good choice, but you may want to write a specific version you obtained from existing Kotlin classfiles in your project.
You also have to set up the `flags`. For description of all the available flags, see [Metadata.extraInt](https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-metadata/extra-int.html).
`0` (no flags) may be a good choice, but in case you already have some ground truth files in your project, you may want to copy flags from them.
```kotlin
val classMetadata = KotlinClassMetadata.Class(klass, JvmMetadataVersion.CURRENT, 0)
val classMetadata = KotlinClassMetadata.Class(klass, JvmMetadataVersion.LATEST_STABLE_SUPPORTED, 0)
val newAnnotation: Metadata = classMetadata.write()
// Write annotation directly or use annotation.kind, annotation.data1, annotation.data2, etc.
```
@@ -169,26 +169,26 @@ It's also the only method that allows metadata transformation and `KotlinClassMe
`readLenient()`: This method allows you to read the metadata leniently.
If the metadata version is higher than what kotlinx-metadata-jvm can interpret, it may ignore parts of the metadata it doesn't understand.
Its more suitable when your tooling needs to read metadata of possibly newer Kotlin versions and can handle incomplete data, because it is interested only in part of it (e.g. visibility of declarations)
Its more suitable when your tooling needs to read metadata of possibly newer Kotlin versions and can handle incomplete data, because it is interested only in part of it (e.g. visibility of declarations).
Keep in mind that this method will still throw an exception if metadata is changed in an unpredictable way.
**Metadata read in lenient mode can not be written back.**
### Detailed explanation
Kotlin compiler and its features evolve over time, and so does its metadata format. Metadata format version is equal to the Kotlin compiler version.
Naturally, evolving metadata format usually involves adding new fields for new Kotlin language features. Therefore,
some problems may occur when you're reading new metadata with an older version of Kotlin compiler or kotlinx-metadata-jvm library.
Naturally, evolving the metadata format may result in various changes, from adding new information to changing metadata format entirely for the new Kotlin language features.
Therefore, some problems may occur when you're reading new metadata with an older version of Kotlin compiler or kotlinx-metadata-jvm library.
By default, the Kotlin/JVM compiler (and similar, kotlinx-metadata-jvm library) has [forward compatibility](https://kotlinlang.org/docs/kotlin-evolution.html#evolving-the-binary-format) for versions not higher than current + 1.
It means that Kotlin compiler 2.1 can read metadata from Kotlin compiler 2.2, but not 2.3. The same is true for `KotlinClassMetadata.readStrict()`
method: it will throw an exception if you try to read metadata with version higher than `COMPATIBLE_METADATA_VERSION` + 1.
Such restriction comes from the fact that higher metadata versions (e.g. 2.3) might have some unknown fields that we skip during reading; therefore, if we write
transformed metadata back, missing some fields may result in corrupted metadata that is no longer valid for version 2.3.
method: it will throw an exception if you try to read metadata with version higher than `JvmMetadataVersion.LATEST_STABLE_SUPPORTED` + 1.
Such restriction comes from the fact that higher metadata versions (e.g. 2.3) might have some unexpected format that can be read incorrectly; therefore, if we write
transformed metadata back, this can result in corrupted metadata that is no longer valid for version 2.3.
However, there are a lot of use cases for metadata introspection alone, without further transformations — for example, [binary-compatibility-validator](https://github.com/Kotlin/binary-compatibility-validator) which is interested only in visibility and modality of declarations.
For such use cases it seems overly restrictive to prohibit reading newer metadata versions (and therefore, requiring authors to do frequent updates of kotlinx-metadata-jvm dependency),
so there is a relaxed version of the reading method: `KotlinClassMetadata.readLenient()`. It is a best-effort reading method that will potentially skip unknown data,
but still provide some access to metadata. Keep in mind that this method has limitations:
so there is a relaxed version of the reading method: `KotlinClassMetadata.readLenient()`. It is a best-effort reading method that will try to build metadata based on the format we currently know,
and provide some access to it. Keep in mind that this method has limitations:
1. Metadata returned by this method can not be written back, because we are not sure if it is still valid format for newer versions. It is intended for introspection alone.
2. We cannot guarantee that metadata is not changed in the other unpredictable ways in the future. `readLenient()` tries its best, but still can throw a decoding exception or even return incorrect result.
@@ -1311,8 +1311,8 @@ public final class kotlinx/metadata/jvm/JvmMetadataUtil {
}
public final class kotlinx/metadata/jvm/JvmMetadataVersion : java/lang/Comparable {
public static final field CURRENT Lkotlinx/metadata/jvm/JvmMetadataVersion;
public static final field Companion Lkotlinx/metadata/jvm/JvmMetadataVersion$Companion;
public static final field LATEST_STABLE_SUPPORTED Lkotlinx/metadata/jvm/JvmMetadataVersion;
public fun <init> (II)V
public fun <init> (III)V
public synthetic fun compareTo (Ljava/lang/Object;)I
@@ -21,7 +21,7 @@ import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmMetadataVersion as C
* Note that the metadata version is 1.1 for Kotlin compilers from 1.0 until 1.4, and is 1.0 or less for pre-1.0 compilers.
* Metadata with versions less than 1.1 is considered incompatible and cannot be read or written.
*
* The library can read in strict mode only compatible versions of metadata. For definition of compatible version, see documentation for [CURRENT] property.
* The library can read in strict mode only compatible versions of metadata. For definition of a compatible version, see documentation for [LATEST_STABLE_SUPPORTED] property.
*
* @property major Major component of version
* @property minor Minor component of version
@@ -76,7 +76,7 @@ public class JvmMetadataVersion(public val major: Int, public val minor: Int, pu
}
/**
* Checks if the current JvmMetadataVersion object is equal to another object.
* Checks if this JvmMetadataVersion object is equal to [other] JvmMetadataVersion object.
*
* Instances of JvmMetadataVersion are equal if they have the same major, minor, and patch components.
*/
@@ -101,7 +101,7 @@ public class JvmMetadataVersion(public val major: Int, public val minor: Int, pu
* The latest stable metadata version supported by this version of the library.
* The library can read in strict mode Kotlin metadata produced by Kotlin compilers from 1.0 up to and including this version + 1 minor.
*
* In other words, the metadata version is supported if it is greater or equal than 1.1, and less or equal than [CURRENT] + 1 minor version.
* In other words, the metadata version is supported if it is greater or equal than 1.1, and less or equal than [LATEST_STABLE_SUPPORTED] + 1 minor version.
* Note that the metadata version is 1.1 for Kotlin from 1.0 until 1.4, and is equal to the language version starting from Kotlin 1.4.
*
* For example, if the latest supported stable Kotlin version is 1.7.0, kotlinx-metadata-jvm can read in strict mode binaries produced by Kotlin compilers from 1.0
@@ -111,6 +111,6 @@ public class JvmMetadataVersion(public val major: Int, public val minor: Int, pu
* @see KotlinClassMetadata.readStrict
*/
@JvmField
public val CURRENT: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE.toArray())
public val LATEST_STABLE_SUPPORTED: JvmMetadataVersion = JvmMetadataVersion(CompilerMetadataVersion.INSTANCE.toArray())
}
}
@@ -178,7 +178,7 @@ public final class KotlinClassHeader implements Metadata {
public static final int MULTI_FILE_CLASS_PART_KIND = 5;
/**
* Use {@link kotlinx.metadata.jvm.JvmMetadataVersion#CURRENT} instead
* Use {@link kotlinx.metadata.jvm.JvmMetadataVersion#LATEST_STABLE_SUPPORTED} instead
*/
@Deprecated
public static final int[] COMPATIBLE_METADATA_VERSION = Arrays.copyOf(JvmMetadataVersion.INSTANCE.toArray(), 3);
@@ -602,7 +602,7 @@ public sealed class KotlinClassMetadata {
@Deprecated("This declaration is intended for binary compatibility only", level = DeprecationLevel.HIDDEN)
internal companion object {
@JvmField // For binary compatibility with previous `data object Unknown`
public val INSTANCE: Unknown = Unknown(Metadata(kind = 99, metadataVersion = JvmMetadataVersion.CURRENT.toIntArray()), true)
public val INSTANCE: Unknown = Unknown(Metadata(kind = 99, metadataVersion = JvmMetadataVersion.LATEST_STABLE_SUPPORTED.toIntArray()), true)
}
}
@@ -780,14 +780,14 @@ public sealed class KotlinClassMetadata {
* [annotationData] may be obtained reflectively, constructed manually or with helper [kotlinx.metadata.jvm.Metadata] function,
* or equivalent [KotlinClassHeader] can be used.
*
* This method can read only supported metadata versions (see [JvmMetadataVersion.CURRENT] for definition).
* This method can read only supported metadata versions (see [JvmMetadataVersion.LATEST_STABLE_SUPPORTED] for definition).
* It will throw an exception if the metadata version is greater than what kotlinx-metadata-jvm understands.
* It is suitable when your tooling cannot tolerate reading potentially incomplete or incorrect information due to version differences.
* It is also the only method that allows metadata transformation and `KotlinClassMetadata.write` subsequent calls.
*
* @throws IllegalArgumentException if the metadata version is unsupported or if metadata is corrupted
*
* @see JvmMetadataVersion.CURRENT
* @see JvmMetadataVersion.LATEST_STABLE_SUPPORTED
*/
@JvmStatic
public fun readStrict(annotationData: Metadata): KotlinClassMetadata = readMetadataImpl(annotationData, lenient = false)
@@ -800,14 +800,14 @@ public sealed class KotlinClassMetadata {
* or equivalent [KotlinClassHeader] can be used.
*
* This method makes best effort to read unsupported metadata versions.
* If metadata version is greater than [JvmMetadataVersion.CURRENT] + 1, this method still attempts to read it and may ignore parts of the metadata it does not understand.
* If metadata version is greater than [JvmMetadataVersion.LATEST_STABLE_SUPPORTED] + 1, this method still attempts to read it and may ignore parts of the metadata it does not understand.
* Keep in mind that this method will still throw an exception if metadata is changed in an unpredictable way.
* Because obtained metadata can be incomplete, its [KotlinClassMetadata.write] method will throw an exception.
* This method still cannot read metadata produced by pre-1.0 compilers.
*
* @throws IllegalArgumentException if the metadata version is that of Kotlin 1.0 or metadata format has been changed in an unpredictable way and reading of incompatible metadata is not possible
*
* @see JvmMetadataVersion.CURRENT
* @see JvmMetadataVersion.LATEST_STABLE_SUPPORTED
*/
@JvmStatic
public fun readLenient(annotationData: Metadata): KotlinClassMetadata = readMetadataImpl(annotationData, lenient = true)
@@ -869,8 +869,8 @@ public sealed class KotlinClassMetadata {
* The latest stable metadata version supported by this version of the library.
* The library can read in strict mode Kotlin metadata produced by Kotlin compilers from 1.0 up to and including this version + 1 minor.
*
* In other words, metadata version is supported if it is greater or equal than 1.1, and less or equal than [COMPATIBLE_METADATA_VERSION] + 1 minor version.
* Note that metadata version is 1.1 for Kotlin < 1.4, and is equal to the language version starting from Kotlin 1.4.
* In other words, a metadata version is supported if it is greater or equal than 1.1, and less or equal than [COMPATIBLE_METADATA_VERSION] + 1 minor version.
* Note that a metadata version is 1.1 for Kotlin < 1.4, and is equal to the language version starting from Kotlin 1.4.
*
* For example, if the latest supported stable Kotlin version is 1.7.0, kotlinx-metadata-jvm can read binaries produced by Kotlin compilers from 1.0
* to 1.8.* inclusively. In this case, this property will have the value `[1, 7, 0]`.
@@ -878,7 +878,7 @@ public sealed class KotlinClassMetadata {
* @see Metadata.metadataVersion
*/
@JvmField
@Deprecated("Use JvmMetadataVersion.CURRENT instead", ReplaceWith("JvmMetadataVersion.CURRENT"), DeprecationLevel.WARNING)
@Deprecated("Use JvmMetadataVersion.LATEST_STABLE_SUPPORTED instead", ReplaceWith("JvmMetadataVersion.LATEST_STABLE_SUPPORTED"), DeprecationLevel.WARNING)
public val COMPATIBLE_METADATA_VERSION: IntArray = CompilerMetadataVersion.INSTANCE.toArray().copyOf()
}
@@ -171,7 +171,7 @@ public class KotlinModuleMetadata public constructor(
@Deprecated("Use a KotlinModuleMetadata instance and its write() member function", level = DeprecationLevel.WARNING)
@JvmStatic
@JvmOverloads
public fun write(kmModule: KmModule, metadataVersion: JvmMetadataVersion = JvmMetadataVersion.CURRENT): ByteArray = wrapWriteIntoIAE {
public fun write(kmModule: KmModule, metadataVersion: JvmMetadataVersion = JvmMetadataVersion.LATEST_STABLE_SUPPORTED): ByteArray = wrapWriteIntoIAE {
return KotlinModuleMetadata(kmModule, metadataVersion).write()
}
}
@@ -12,7 +12,6 @@ import kotlinx.metadata.jvm.KotlinClassHeader;
import kotlinx.metadata.jvm.KotlinClassMetadata;
import org.junit.Test;
import java.util.Arrays;
import java.util.Objects;
import static org.junit.Assert.*;
@@ -34,7 +33,7 @@ public class JavaUsageTest {
KotlinClassMetadata clazz1 = ((KotlinClassMetadata) Objects.requireNonNull(KotlinClassMetadata.readStrict(m)));
Metadata written = clazz1.write();
//noinspection KotlinInternalInJava
assertArrayEquals(written.mv(), JvmMetadataVersion.CURRENT.toIntArray());
assertArrayEquals(written.mv(), JvmMetadataVersion.LATEST_STABLE_SUPPORTED.toIntArray());
assertEquals(50, written.xi());
}
}
@@ -30,7 +30,7 @@ class MetadataExceptionsTest {
fun testWriteMalformedClass() {
val kmClass = KmClass() // kotlin.UninitializedPropertyAccessException: lateinit property name has not been initialized
val e = assertFailsWith<IllegalArgumentException> {
KotlinClassMetadata.Class(kmClass, JvmMetadataVersion.CURRENT, 0).write()
KotlinClassMetadata.Class(kmClass, JvmMetadataVersion.LATEST_STABLE_SUPPORTED, 0).write()
}
assertIs<UninitializedPropertyAccessException>(e.cause)
}
@@ -63,7 +63,7 @@ class MetadataSmokeTest {
}
}
val annotationData = KotlinClassMetadata.Class(klass, JvmMetadataVersion.CURRENT, 0).write()
val annotationData = KotlinClassMetadata.Class(klass, JvmMetadataVersion.LATEST_STABLE_SUPPORTED, 0).write()
// Then, produce the bytecode of a .class file with ASM