Make changelog and migration guide for kotlinx-metadata-jvm:0.7.0

This commit is contained in:
Leonid Startsev
2023-07-04 14:17:34 +02:00
committed by Space Team
parent fdda394a77
commit 5424c54fae
3 changed files with 142 additions and 16 deletions
+16 -2
View File
@@ -1,15 +1,29 @@
# kotlinx-metadata-jvm
## 0.7.0
This release features several significant API changes. To help with migration, we've prepared a special [guide](Migration.md#migrating-from-06x-to-070).
- Update to Kotlin 1.9 with metadata version 1.9, support reading/writing metadata of version 2.0 which will be used in Kotlin 2.0
- Rework flags API (see [migration from Flags API to Attributes API](Migration.md#migration-from-flags-api-to-attributes-api)).
- Restructure `KotlinClass(Module)Metadata.write/read` (see [changes in reading and writing API](Migration.md#changes-in-reading-and-writing-api)).
- Add `@JvmStatic` + `@JvmOverloads` to writing functions in `KotlinClassMetadata`
- Deprecate `KmModule.annotations` for removal because it is always empty and should not be used.
- Move `KmModuleFragment` to an `kotlinx.metadata.internal.common` package. This class is intended for internal use only. If you have use-cases for it, please report an issue to YouTrack.
- Improve `toString()` for `KmAnnotationArgument`
- Add missing deprecation for `KmExtensionType` and experimentality for `KmConstantValue`.
- Enhance kotlinx-metadata-jvm KDoc and set up Dokka.
## 0.6.2
This release uses Kotlin 1.8.20 with metadata version 1.8, and as a special case, is able to read metadata of version 2.0.
This is done as an incentive to test K2 compiler and 2.0 language version.
No other changes were made and no migration is needed.
Note: 0.6.1 was released with incorrect fix for this problem. Do not use 0.6.1.
Note: 0.6.1 was released with an incorrect fix for this problem. Do not use 0.6.1.
## 0.6.0
This release features several significant API changes. To help with migration, we've prepared a special [guide](Migration.md).
This release features several significant API changes. To help with migration, we've prepared a special [guide](Migration.md#migrating-from-050-to-06x).
- Update to Kotlin 1.8 with metadata version 1.8, support reading/writing metadata of version 1.9 which will be used in Kotlin 1.9
- Deprecate Visitors API
+116 -3
View File
@@ -1,9 +1,122 @@
# Kotlinx-metadata migration guide
Starting with 0.6.0 release, Kotlin team is focused on revisiting and improving kotlinx-metadata API, with an aim to provide a stable release
Starting with 0.6.0 release, Kotlin team is focused on revisiting and improving kotlinx-metadata-jvm API, with an aim to provide a stable release
in the near future. As a result, the API was reshaped, with cuts here and there, so we've provided a migration guide to help you with updates.
## Migrating from 0.5.0 to 0.6.0+
## Migrating from 0.6.x to 0.7.0
### Migration from Flags API to Attributes API
There are a lot of various modifiers that can be applied to various Kotlin declarations: `public`, `sealed`, `data`, `inline`, and so on.
Introspecting them is one of the major use cases for the kotlinx-metadata library.
In previous versions, they were represented as a bit mask and a `Flag.invoke` function:
```kotlin
fun nameOfPublicDataClass(kmClass: KmClass): ClassName? {
return if (Flag.Common.IS_PUBLIC(kmClass.flags) && Flag.Class.IS_DATA(kmClass.flags)) kmClass.name else null
}
```
Such an API is based on implementation details and has problems, such as:
* Discoverability; it is hard to stumble across this API while looking into autocompletion pop-up for `kmClass.`.
* Non-OOP style and counterintuitivity; naturally, one wants to call something like `function.isPublic()`, and not `isPublic(function)`.
* Applicability and soundness; it is not a compiler error to call `Flag.IS_PUBLIC(kmType.flags)`, while `KmType` obviously does not have a notion of visibility.
To solve these problems, Flags API **is deprecated completely** for replacement with the new Attributes API.
Attributes API is fairly simple and essentially is a broad set of extensions on Km nodes,
such as `KmClass.visibility`, `KmClass.isData`, `KmFunction.isInline`, and so on.
For almost every deprecated `Flag` instance, there is a corresponding mutable extension property.
There are some exceptions to this rule, notably visibility and modality.
For them, all flags are replaced with a single extension that returns an enum value.
For example, `Flag.IS_PUBLIC(): Boolean` and `Flag.IS_PRIVATE(): Boolean`
are both replaced by `KmClass.visibility: Visibility` or `KmFunction.visibility: Visibility`.
For migration, replace `Flag` usages with access to corresponding extension properties.
Deprecation message for a particular `Flag` instance should help you identify a correct extension.
The function above can now be rewritten in a more clear and idiomatic way:
```kotlin
fun nameOfPublicDataClass(kmClass: KmClass): ClassName? {
return if (kmClass.visibility == Visibility.PUBLIC && kmClass.isData) kmClass.name else null
}
```
### Changes in reading and writing API
After collecting some feedback from our users, we have decided to implement the following changes:
#### `KotlinClassMetadata.read()` now has a non-nullable return type of `KotlinClassMetadata`
Previously, `null` value was returned in case a metadata version was not compatible.
It was not very convenient, as `null` value does not state the exact version of the metadata and why it is incompatible (is it too old or too new).
Now, an `IllegalStateException` with appropriate message is thrown in this case.
To migrate, simply remove null-checks around `KotlinClassMetadata.read()`. In case you need special logic for the incompatible version case, add a try-catch block.
The same is applicable to `KotlinModuleMetadata.read()`.
#### Metadata validation moved from `toKmClass()` methods to `KotlinClassMetadata.read()`
Checks related to validation of metadata encoding that used to happen in conversion methods
(like `KotlinClassMetadata.Class.toKmClass()`, `KotlinClassMetadata.FileFacade.toKmPackage()`, etc)
are moved to the `KotlinClassMetadata.read()`.
As a result, these conversion methods are deprecated and replaced by the similarly named properties because they are no longer throw exceptions and simply return a cached result,
while actual conversion is moved to `KotlinClassMetadata.read()`.
To migrate, use provided replacements:
**Before:**
```kotlin
when (val metadata = KotlinClassMetadata.read(header)) {
is KotlinClassMetadata.Class -> handleClass(metadata.toKmClass())
is KotlinClassMetadata.FileFacade -> handleFileFacade(metadata.toKmPackage())
is KotlinClassMetadata.MultiFileClassPart -> handleMFClassPart(metadata.facadeClassName, metadata.toKmPackage())
...
}
```
**After:**
```kotlin
when (val metadata = KotlinClassMetadata.read(header)) {
is KotlinClassMetadata.Class -> handleClass(metadata.kmClass)
is KotlinClassMetadata.FileFacade -> handleFileFacade(metadata.kmPackage)
is KotlinClassMetadata.MultiFileClassPart -> handleMFClassPart(metadata.facadeClassName, metadata.kmPackage)
...
}
```
The same is applicable to `KotlinModuleMetadata.toKmModule()`.
#### Writing API returns the encoded result directly
`KotlinClassMetadata.writeClass` and other similar functions now return a `Metadata` instance directly
instead of returning a new `KotlinClassMetadata` instance.
Previous behavior caused confusion because it was not clear what operations are valid on a returned instance and
how exactly it is supposed to be used.
As a result, `KotlinClassMetadata.annotationData: Metadata` property has been made private because there is no longer need for it to be exposed.
To migrate, simply remove `.annotationData` access from your writing logic:
**Before:**
```kotlin
fun save(kmClass: KmClass) {
val metadata: Metadata = KotlinClassMetadata.writeClass(kmClass).annotationData
writeToClassFile(metadata)
}
```
**After:**
```kotlin
fun save(kmClass: KmClass) {
val metadata: Metadata = KotlinClassMetadata.writeClass(kmClass)
writeToClassFile(metadata)
}
```
The same is applicable to `KotlinModuleMetadata.write`: it returns `ByteArray` directly.
## Migrating from 0.5.0 to 0.6.x
There are several significant changes between 0.5.0 and 0.6.0:
@@ -91,4 +204,4 @@ fun saveClass(kmClass: KmClass) {
// Write Metadata.data1, data2, etc using ASM
}
```
```
+10 -11
View File
@@ -48,7 +48,7 @@ val metadata = KotlinClassMetadata.read(metadataAnnotation)
```
`KotlinClassMetadata` is a sealed class, with subclasses representing all the different kinds of classes generated by the Kotlin compiler.
Unless you're sure that you're reading a class of a specific kind and can do a simple cast, a `when` is a good choice to handle all the possibilities:
Unless you are sure that you are reading a class of a specific kind and can do a simple cast, a `when` is a good choice to handle all the possibilities:
```kotlin
when (metadata) {
@@ -61,7 +61,7 @@ when (metadata) {
}
```
Let's assume we've obtained an instance of `KotlinClassMetadata.Class`; other kinds of classes are handled similarly, except some of them have metadata in a slightly different form.
Let us assume we have obtained an instance of `KotlinClassMetadata.Class`; other kinds of classes are handled similarly, except some of them have metadata in a slightly different form.
The main way to make sense of the underlying metadata is to access the `kmClass` property, which returns an instance of `KmClass` (`Km` is a shorthand for “Kotlin metadata”):
```kotlin
@@ -72,29 +72,28 @@ println(klass.properties.map { it.name })
Please refer to [`MetadataSmokeTest.listInlineFunctions`](test/kotlinx/metadata/test/MetadataSmokeTest.kt) for an example where all inline functions are read from the class metadata along with their JVM signatures.
## Flags
## Attributes
Numerous objects have a property named `flags` of type `Flags`. These flags represent modifiers or other boolean attributes of a declaration or a type.
To check if a certain flag is present, call one of the flags in [`Flag`](../src/kotlinx/metadata/Flag.kt) on the given integer value.
The set of applicable flags is documented for each Node property which has type `Flags`.
For example, functions have common declaration flags (visibility, modality) plus `Flag.Function` flags:
Most of the Km nodes (`KmClass`, `KmFunction`, `KmType`, and so on) have a set of extension properties that allow to get and set various attributes.
Most of these attributes are boolean values, but some of them, such as visibility, are represented by enum classes.
For example, you can check function visibility and presence of `suspend` modifier with corresponding extension properties:
```kotlin
val function: KmFunction = ...
if (Flag.IS_PUBLIC(function.flags)) {
if (function.visibility == Visibility.PUBLIC) {
println("function ${function.name} is public")
}
if (Flag.Function.IS_SUSPEND(function.flags)) {
if (function.isSuspend) {
println("function ${function.name} has the 'suspend' modifier")
}
```
## Writing metadata
To create metadata of a Kotlin class file from scratch, construct an instance of `KmClass`/`KmPackage`/`KmLambda`, fill it with the data and call corresponding `KotlinClassMetadata.write` function.
To create metadata of a Kotlin class file from scratch, construct an instance of `KmClass`/`KmPackage`/`KmLambda`, fill it with the data, and call corresponding `KotlinClassMetadata.write` function.
Resulting `kotlin.Metadata` annotation can be written to a class file.
When using metadata writers from Kotlin source code, it's very convenient to use Kotlin scoping functions such as `apply` to reduce boilerplate:
When using metadata writers from Kotlin source code, it is very convenient to use Kotlin scoping functions such as `apply` to reduce boilerplate:
```kotlin
// Writing metadata of a class