Skip to content

Commit 5d538ee

Browse files
committed
Introduce @AdvancedEncodingApi and @SealedSerializationApi annotations
to be used with @SubclassOptInRequired. These annotations allow for even more fine-grained API marking. We now can designate APIs as public for use, but closed for implementation (@SealedSerializationApi) — the case for SerialDescriptor, which is a non-sealed interface for technical reasons. The other annotation, @AdvancedEncodingApi is aimed to provide guidance on implementing custom encoders/decoders by pointing users to a documentation and guides. Fixes #2366
1 parent e3cc966 commit 5d538ee

File tree

34 files changed

+113
-10
lines changed

34 files changed

+113
-10
lines changed

buildSrc/src/main/kotlin/source-sets-conventions.gradle.kts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,10 @@ kotlin {
6666
progressiveMode = true
6767

6868
optIn("kotlin.ExperimentalMultiplatform")
69+
optIn("kotlin.ExperimentalSubclassOptIn")
70+
optIn("kotlin.ExperimentalStdlibApi")
6971
optIn("kotlinx.serialization.InternalSerializationApi")
72+
optIn("kotlinx.serialization.SealedSerializationApi")
7073
}
7174
}
7275

@@ -147,7 +150,9 @@ kotlin {
147150
sourceSets.matching({ it.name.contains("Test") }).configureEach {
148151
languageSettings {
149152
optIn("kotlinx.serialization.InternalSerializationApi")
153+
optIn("kotlinx.serialization.SealedSerializationApi")
150154
optIn("kotlinx.serialization.ExperimentalSerializationApi")
155+
optIn("kotlinx.serialization.encoding.AdvancedEncodingApi")
151156
}
152157
}
153158
}

core/api/kotlinx-serialization-core.api

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ public final class kotlinx/serialization/SealedClassSerializer : kotlinx/seriali
8686
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
8787
}
8888

89+
public abstract interface annotation class kotlinx/serialization/SealedSerializationApi : java/lang/annotation/Annotation {
90+
}
91+
8992
public abstract interface class kotlinx/serialization/SerialFormat {
9093
public abstract fun getSerializersModule ()Lkotlinx/serialization/modules/SerializersModule;
9194
}
@@ -424,6 +427,9 @@ public abstract class kotlinx/serialization/encoding/AbstractEncoder : kotlinx/s
424427
public fun shouldEncodeElementDefault (Lkotlinx/serialization/descriptors/SerialDescriptor;I)Z
425428
}
426429

430+
public abstract interface annotation class kotlinx/serialization/encoding/AdvancedEncodingApi : java/lang/annotation/Annotation {
431+
}
432+
427433
public abstract interface class kotlinx/serialization/encoding/ChunkedDecoder {
428434
public abstract fun decodeStringChunked (Lkotlin/jvm/functions/Function1;)V
429435
}

core/api/kotlinx-serialization-core.klib.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
// - Show declarations: true
88

99
// Library unique name: <org.jetbrains.kotlinx:kotlinx-serialization-core>
10+
open annotation class kotlinx.serialization.encoding/AdvancedEncodingApi : kotlin/Annotation { // kotlinx.serialization.encoding/AdvancedEncodingApi|null[0]
11+
constructor <init>() // kotlinx.serialization.encoding/AdvancedEncodingApi.<init>|<init>(){}[0]
12+
}
13+
1014
open annotation class kotlinx.serialization.internal/NamedCompanion : kotlin/Annotation { // kotlinx.serialization.internal/NamedCompanion|null[0]
1115
constructor <init>() // kotlinx.serialization.internal/NamedCompanion.<init>|<init>(){}[0]
1216
}
@@ -61,6 +65,10 @@ open annotation class kotlinx.serialization/Required : kotlin/Annotation { // ko
6165
constructor <init>() // kotlinx.serialization/Required.<init>|<init>(){}[0]
6266
}
6367

68+
open annotation class kotlinx.serialization/SealedSerializationApi : kotlin/Annotation { // kotlinx.serialization/SealedSerializationApi|null[0]
69+
constructor <init>() // kotlinx.serialization/SealedSerializationApi.<init>|<init>(){}[0]
70+
}
71+
6472
open annotation class kotlinx.serialization/SerialInfo : kotlin/Annotation { // kotlinx.serialization/SerialInfo|null[0]
6573
constructor <init>() // kotlinx.serialization/SerialInfo.<init>|<init>(){}[0]
6674
}

core/commonMain/src/kotlinx/serialization/Annotations.kt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,17 @@ public annotation class ExperimentalSerializationApi
377377
@Target(AnnotationTarget.CLASS, AnnotationTarget.PROPERTY, AnnotationTarget.FUNCTION, AnnotationTarget.TYPEALIAS)
378378
@RequiresOptIn(level = RequiresOptIn.Level.ERROR)
379379
public annotation class InternalSerializationApi
380+
381+
/**
382+
* Marks interfaces and non-final classes that can be freely referenced in users' code but should not be
383+
* implemented or inherited. Such declarations are effectively `sealed` and do not have this modifier purely for technical reasons.
384+
*
385+
* kotlinx.serialization library provides compatibility guarantees for existing signatures of such classes;
386+
* however, new functions or properties can be added to them in any release.
387+
*/
388+
@MustBeDocumented
389+
@Target(AnnotationTarget.CLASS)
390+
@RequiresOptIn(message = "This class or interface should not be inherited/implemented outside of kotlinx.serialization library. " +
391+
"Note it is still permitted to use it directly. Read its documentation about inheritance for details.", level = RequiresOptIn.Level.ERROR)
392+
public annotation class SealedSerializationApi
393+

core/commonMain/src/kotlinx/serialization/descriptors/SerialDescriptor.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ import kotlinx.serialization.encoding.*
142142
* might be added to this interface when kotlinx.serialization adds support for new Kotlin features.
143143
* This interface is safe to use and construct via [buildClassSerialDescriptor], [PrimitiveSerialDescriptor], and `SerialDescriptor` factory function.
144144
*/
145+
@SubclassOptInRequired(SealedSerializationApi::class)
145146
public interface SerialDescriptor {
146147
/**
147148
* Serial name of the descriptor that identifies a pair of the associated serializer and target class.

core/commonMain/src/kotlinx/serialization/encoding/AbstractDecoder.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import kotlinx.serialization.descriptors.*
1414
* See [Decoder] documentation for information about each particular `decode*` method.
1515
*/
1616
@ExperimentalSerializationApi
17+
@SubclassOptInRequired(AdvancedEncodingApi::class)
1718
public abstract class AbstractDecoder : Decoder, CompositeDecoder {
1819

1920
/**

core/commonMain/src/kotlinx/serialization/encoding/AbstractEncoder.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import kotlinx.serialization.internal.*
1616
* See [Encoder] documentation for information about each particular `encode*` method.
1717
*/
1818
@ExperimentalSerializationApi
19+
@SubclassOptInRequired(AdvancedEncodingApi::class)
1920
public abstract class AbstractEncoder : Encoder, CompositeEncoder {
2021

2122
override fun beginStructure(descriptor: SerialDescriptor): CompositeEncoder = this

core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ import kotlinx.serialization.modules.*
111111
* `Decoder` interface is not stable for inheritance in 3rd-party libraries, as new methods
112112
* might be added to this interface or contracts of the existing methods can be changed.
113113
*/
114+
@SubclassOptInRequired(AdvancedEncodingApi::class)
114115
public interface Decoder {
115116
/**
116117
* Context of the current serialization process, including contextual and polymorphic serialization and,
@@ -292,6 +293,7 @@ internal inline fun <T : Any> Decoder.decodeIfNullable(deserializer: Deserializa
292293
* `CompositeDecoder` interface is not stable for inheritance in 3rd party libraries, as new methods
293294
* might be added to this interface or contracts of the existing methods can be changed.
294295
*/
296+
@SubclassOptInRequired(AdvancedEncodingApi::class)
295297
public interface CompositeDecoder {
296298

297299
/**

core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,7 @@ import kotlinx.serialization.modules.*
107107
* `Encoder` interface is not stable for inheritance in 3rd party libraries, as new methods
108108
* might be added to this interface or contracts of the existing methods can be changed.
109109
*/
110+
@SubclassOptInRequired(AdvancedEncodingApi::class)
110111
public interface Encoder {
111112
/**
112113
* Context of the current serialization process, including contextual and polymorphic serialization and,
@@ -320,6 +321,7 @@ public interface Encoder {
320321
* `CompositeEncoder` interface is not stable for inheritance in 3rd party libraries, as new methods
321322
* might be added to this interface or contracts of the existing methods can be changed.
322323
*/
324+
@SubclassOptInRequired(AdvancedEncodingApi::class)
323325
public interface CompositeEncoder {
324326
/**
325327
* Context of the current serialization process, including contextual and polymorphic serialization and,
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
/*
2+
* Copyright 2017-2024 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package kotlinx.serialization.encoding
6+
7+
/**
8+
* Marks all encoding- and decoding-related interfaces in kotlinx.serialization.
9+
* These interfaces are used in serializers and have to be implemented only if you want to write
10+
* a custom serialization format. Since encoder/decoder invariants are quite complex,
11+
* it is recommended to start with reading their documentation: see [Encoder] and [Decoder],
12+
* and [kotlinx.serialization guide](https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/formats.md#custom-formats-experimental) about them.
13+
* There are also existing skeleton implementations that you may find useful: [AbstractEncoder] and [AbstractDecoder].
14+
*/
15+
@RequiresOptIn(
16+
"You should implement Encoder or Decoder only if you want to write a custom kotlinx.serialization format. " +
17+
"Before doing so, please consult official guide at https://github.com/Kotlin/kotlinx.serialization/blob/master/docs/formats.md#custom-formats-experimental",
18+
level = RequiresOptIn.Level.WARNING
19+
)
20+
public annotation class AdvancedEncodingApi

0 commit comments

Comments
 (0)