Make changelog and migration guide for kotlinx-metadata-jvm:0.7.0
This commit is contained in:
committed by
Space Team
parent
fdda394a77
commit
5424c54fae
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
```
|
||||
```
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user