Skip to content

Commit 3e8331c

Browse files
authored
Stabilize encoding and decoding of value classes (#1963)
1 parent 93a06df commit 3e8331c

File tree

14 files changed

+58
-81
lines changed

14 files changed

+58
-81
lines changed

core/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ kotlin {
2525
These manifest values help kotlinx.serialization compiler plugin determine if it is compatible with a given runtime library.
2626
Plugin reads them during compilation.
2727
28-
Implementation-Version is used to determine whether runtime library supports a given plugin feature (e.g. inline classes serialization
28+
Implementation-Version is used to determine whether runtime library supports a given plugin feature (e.g. value classes serialization
2929
in Kotlin 1.x may require runtime library version 1.y to work).
3030
Compiler plugin may enable or disable features by looking on Implementation-Version.
3131

core/commonMain/src/kotlinx/serialization/builtins/BuiltinSerializers.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -194,28 +194,24 @@ public fun <K, V> MapSerializer(
194194
/**
195195
* Returns serializer for [UInt].
196196
*/
197-
@ExperimentalSerializationApi
198197
@ExperimentalUnsignedTypes
199198
public fun UInt.Companion.serializer(): KSerializer<UInt> = UIntSerializer
200199

201200
/**
202201
* Returns serializer for [ULong].
203202
*/
204-
@ExperimentalSerializationApi
205203
@ExperimentalUnsignedTypes
206204
public fun ULong.Companion.serializer(): KSerializer<ULong> = ULongSerializer
207205

208206
/**
209207
* Returns serializer for [UByte].
210208
*/
211-
@ExperimentalSerializationApi
212209
@ExperimentalUnsignedTypes
213210
public fun UByte.Companion.serializer(): KSerializer<UByte> = UByteSerializer
214211

215212
/**
216213
* Returns serializer for [UShort].
217214
*/
218-
@ExperimentalSerializationApi
219215
@ExperimentalUnsignedTypes
220216
public fun UShort.Companion.serializer(): KSerializer<UShort> = UShortSerializer
221217

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -162,9 +162,9 @@ public interface SerialDescriptor {
162162
public val isNullable: Boolean get() = false
163163

164164
/**
165-
* Returns `true` if this descriptor describes a serializable inline class.
165+
* Returns `true` if this descriptor describes a serializable value class which underlying value
166+
* is serialized directly.
166167
*/
167-
@ExperimentalSerializationApi
168168
public val isInline: Boolean get() = false
169169

170170
/**

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ public abstract class AbstractDecoder : Decoder, CompositeDecoder {
3434
override fun decodeString(): String = decodeValue() as String
3535
override fun decodeEnum(enumDescriptor: SerialDescriptor): Int = decodeValue() as Int
3636

37-
override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder = this
37+
override fun decodeInline(descriptor: SerialDescriptor): Decoder = this
3838

3939
// overwrite by default
4040
public open fun <T : Any?> decodeSerializableValue(

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ public abstract class AbstractEncoder : Encoder, CompositeEncoder {
5151
override fun encodeString(value: String): Unit = encodeValue(value)
5252
override fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int): Unit = encodeValue(index)
5353

54-
override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder = this
54+
override fun encodeInline(descriptor: SerialDescriptor): Encoder = this
5555

5656
// Delegating implementation of CompositeEncoder
5757
final override fun encodeBooleanElement(descriptor: SerialDescriptor, index: Int, value: Boolean) { if (encodeElement(descriptor, index)) encodeBoolean(value) }

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

Lines changed: 20 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ import kotlinx.serialization.modules.*
108108
*
109109
* ### Not stable for inheritance
110110
*
111-
* `Decoder` interface is not stable for inheritance in 3rd party libraries, as new methods
111+
* `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
*/
114114
public interface Decoder {
@@ -211,27 +211,24 @@ public interface Decoder {
211211
public fun decodeEnum(enumDescriptor: SerialDescriptor): Int
212212

213213
/**
214-
* Returns [Decoder] for decoding an underlying type of an inline class.
215-
* [inlineDescriptor] describes a target inline class.
214+
* Returns [Decoder] for decoding an underlying type of a value class in an inline manner.
215+
* [descriptor] describes a target value class.
216216
*
217-
* Namely, for the `@Serializable inline class MyInt(val my: Int)`,
218-
* the following sequence is used:
217+
* Namely, for the `@Serializable @JvmInline value class MyInt(val my: Int)`, the following sequence is used:
219218
* ```
220219
* thisDecoder.decodeInline(MyInt.serializer().descriptor).decodeInt()
221220
* ```
222221
*
223-
* Current decoder may return any other instance of [Decoder] class,
224-
* depending on the provided [inlineDescriptor].
225-
* For example, when this function is called on Json decoder with
226-
* `UInt.serializer().descriptor`, the returned decoder is able
227-
* to decode unsigned integers.
222+
* Current decoder may return any other instance of [Decoder] class, depending on the provided [descriptor].
223+
* For example, when this function is called on `Json` decoder with
224+
* `UInt.serializer().descriptor`, the returned decoder is able to decode unsigned integers.
228225
*
229226
* Note that this function returns [Decoder] instead of the [CompositeDecoder]
230-
* because inline classes always have the single property.
231-
* Calling [Decoder.beginStructure] on returned instance leads to an undefined behavior.
227+
* because value classes always have the single property.
228+
*
229+
* Calling [Decoder.beginStructure] on returned instance leads to an unspecified behavior and, in general, is prohibited.
232230
*/
233-
@ExperimentalSerializationApi
234-
public fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder
231+
public fun decodeInline(descriptor: SerialDescriptor): Decoder
235232

236233
/**
237234
* Decodes the beginning of the nested structure in a serialized form
@@ -488,35 +485,34 @@ public interface CompositeDecoder {
488485
public fun decodeStringElement(descriptor: SerialDescriptor, index: Int): String
489486

490487
/**
491-
* Returns [Decoder] for decoding an underlying type of an inline class.
492-
* Serializable inline class is described by the [child descriptor][SerialDescriptor.getElementDescriptor]
488+
* Returns [Decoder] for decoding an underlying type of a value class in an inline manner.
489+
* Serializable value class is described by the [child descriptor][SerialDescriptor.getElementDescriptor]
493490
* of given [descriptor] at [index].
494491
*
495-
* Namely, for the `@Serializable inline class MyInt(val my: Int)`,
496-
* and `@Serializable class MyData(val myInt: MyInt)`
497-
* the following sequence is used:
492+
* Namely, for the `@Serializable @JvmInline value class MyInt(val my: Int)`,
493+
* and `@Serializable class MyData(val myInt: MyInt)` the following sequence is used:
498494
* ```
499495
* thisDecoder.decodeInlineElement(MyData.serializer().descriptor, 0).decodeInt()
500496
* ```
501497
*
502-
* This method provides an opportunity for the optimization and its invocation should be identical to
498+
* This method provides an opportunity for the optimization to avoid boxing of a carried value
499+
* and its invocation should be equivalent to the following:
503500
* ```
504501
* thisDecoder.decodeSerializableElement(MyData.serializer.descriptor, 0, MyInt.serializer())
505502
* ```
506503
*
507504
* Current decoder may return any other instance of [Decoder] class, depending on the provided descriptor.
508-
* For example, when this function is called on Json decoder with descriptor that has
505+
* For example, when this function is called on `Json` decoder with descriptor that has
509506
* `UInt.serializer().descriptor` at the given [index], the returned decoder is able
510507
* to decode unsigned integers.
511508
*
512509
* Note that this function returns [Decoder] instead of the [CompositeDecoder]
513-
* because inline classes always have the single property.
514-
* Calling [Decoder.beginStructure] on returned instance leads to an undefined behavior.
510+
* because value classes always have the single property.
511+
* Calling [Decoder.beginStructure] on returned instance leads to an unspecified behavior and, in general, is prohibited.
515512
*
516513
* @see Decoder.decodeInline
517514
* @see SerialDescriptor.getElementDescriptor
518515
*/
519-
@ExperimentalSerializationApi
520516
public fun decodeInlineElement(
521517
descriptor: SerialDescriptor,
522518
index: Int
@@ -571,6 +567,3 @@ public inline fun <T> Decoder.decodeStructure(
571567
composite.endStructure(descriptor)
572568
return result
573569
}
574-
575-
private const val decodeMethodDeprecated = "Please migrate to decodeElement method which accepts old value." +
576-
"Feel free to ignore it if your format does not support updates."

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

Lines changed: 19 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -207,27 +207,24 @@ public interface Encoder {
207207
public fun encodeEnum(enumDescriptor: SerialDescriptor, index: Int)
208208

209209
/**
210-
* Returns [Encoder] for encoding an underlying type of an inline class.
211-
* [inlineDescriptor] describes a serializable inline class.
210+
* Returns [Encoder] for encoding an underlying type of a value class in an inline manner.
211+
* [descriptor] describes a serializable value class.
212212
*
213-
* Namely, for the `@Serializable inline class MyInt(val my: Int)`,
213+
* Namely, for the `@Serializable @JvmInline value class MyInt(val my: Int)`,
214214
* the following sequence is used:
215215
* ```
216216
* thisEncoder.encodeInline(MyInt.serializer().descriptor).encodeInt(my)
217217
* ```
218218
*
219-
* Current encoder may return any other instance of [Encoder] class,
220-
* depending on the provided [inlineDescriptor].
221-
* For example, when this function is called on Json encoder with
222-
* `UInt.serializer().descriptor`, the returned encoder is able
219+
* Current encoder may return any other instance of [Encoder] class, depending on the provided [descriptor].
220+
* For example, when this function is called on Json encoder with `UInt.serializer().descriptor`, the returned encoder is able
223221
* to encode unsigned integers.
224222
*
225-
* Note that this function returns [Encoder] instead of [CompositeEncoder]
226-
* because inline classes always have one property.
227-
* Calling [Encoder.beginStructure] on returned instance leads to an undefined behavior.
223+
* Note that this function returns [Encoder] instead of the [CompositeEncoder]
224+
* because value classes always have the single property.
225+
* Calling [Encoder.beginStructure] on returned instance leads to an unspecified behavior and, in general, is prohibited.
228226
*/
229-
@ExperimentalSerializationApi
230-
public fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder
227+
public fun encodeInline(descriptor: SerialDescriptor): Encoder
231228

232229
/**
233230
* Encodes the beginning of the nested structure in a serialized form
@@ -411,36 +408,34 @@ public interface CompositeEncoder {
411408
public fun encodeStringElement(descriptor: SerialDescriptor, index: Int, value: String)
412409

413410
/**
414-
* Returns [Encoder] for decoding an underlying type of an inline class.
415-
* Serializable inline class is described by the [child descriptor][SerialDescriptor.getElementDescriptor]
411+
* Returns [Encoder] for decoding an underlying type of a value class in an inline manner.
412+
* Serializable value class is described by the [child descriptor][SerialDescriptor.getElementDescriptor]
416413
* of given [descriptor] at [index].
417414
*
418-
* Namely, for the `@Serializable inline class MyInt(val my: Int)`,
419-
* and `@Serializable class MyData(val myInt: MyInt)`
420-
* the following sequence is used:
415+
* Namely, for the `@Serializable @JvmInline value class MyInt(val my: Int)`,
416+
* and `@Serializable class MyData(val myInt: MyInt)` the following sequence is used:
421417
* ```
422418
* thisEncoder.encodeInlineElement(MyData.serializer.descriptor, 0).encodeInt(my)
423419
* ```
424420
*
425-
* This method is an optimization and its invocation should have the exact same result as
421+
* This method provides an opportunity for the optimization to avoid boxing of a carried value
422+
* and its invocation should be equivalent to the following:
426423
* ```
427424
* thisEncoder.encodeSerializableElement(MyData.serializer.descriptor, 0, MyInt.serializer(), myInt)
428425
* ```
429426
*
430-
* Current encoder may return any other instance of [Encoder] class,
431-
* depending on provided descriptor.
427+
* Current encoder may return any other instance of [Encoder] class, depending on provided descriptor.
432428
* For example, when this function is called on Json encoder with descriptor that has
433429
* `UInt.serializer().descriptor` at the given [index], the returned encoder is able
434430
* to encode unsigned integers.
435431
*
436-
* Note that this function returns [Encoder] instead of [CompositeEncoder]
437-
* because inline classes always have one property.
438-
* Calling [Encoder.beginStructure] on returned instance leads to an undefined behavior.
432+
* Note that this function returns [Encoder] instead of the [CompositeEncoder]
433+
* because value classes always have the single property.
434+
* Calling [Encoder.beginStructure] on returned instance leads to an unspecified behavior and, in general, is prohibited.
439435
*
440436
* @see Encoder.encodeInline
441437
* @see SerialDescriptor.getElementDescriptor
442438
*/
443-
@ExperimentalSerializationApi
444439
public fun encodeInlineElement(
445440
descriptor: SerialDescriptor,
446441
index: Int

core/commonMain/src/kotlinx/serialization/internal/InlineClassDescriptor.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import kotlinx.serialization.encoding.*
1010

1111
@Suppress("Unused")
1212
@PublishedApi
13-
@OptIn(ExperimentalSerializationApi::class)
1413
internal class InlineClassDescriptor(
1514
name: String,
1615
generatedSerializer: GeneratedSerializer<*>

core/commonMain/src/kotlinx/serialization/internal/Tagged.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public abstract class TaggedEncoder<Tag : Any?> : Encoder, CompositeEncoder {
5151
protected open fun encodeTaggedInline(tag: Tag, inlineDescriptor: SerialDescriptor): Encoder =
5252
this.apply { pushTag(tag) }
5353

54-
final override fun encodeInline(inlineDescriptor: SerialDescriptor): Encoder =
55-
encodeTaggedInline(popTag(), inlineDescriptor)
54+
final override fun encodeInline(descriptor: SerialDescriptor): Encoder =
55+
encodeTaggedInline(popTag(), descriptor)
5656

5757
// ---- Implementation of low-level API ----
5858

@@ -209,8 +209,8 @@ public abstract class TaggedDecoder<Tag : Any?> : Decoder, CompositeDecoder {
209209

210210
// ---- Implementation of low-level API ----
211211

212-
final override fun decodeInline(inlineDescriptor: SerialDescriptor): Decoder =
213-
decodeTaggedInline(popTag(), inlineDescriptor)
212+
final override fun decodeInline(descriptor: SerialDescriptor): Decoder =
213+
decodeTaggedInline(popTag(), descriptor)
214214

215215
// TODO this method should be overridden by any sane format that supports top-level nulls
216216
override fun decodeNotNullMark(): Boolean {

core/commonMain/src/kotlinx/serialization/internal/InlineClasses.kt renamed to core/commonMain/src/kotlinx/serialization/internal/ValueClasses.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import kotlinx.serialization.descriptors.*
1010
import kotlinx.serialization.encoding.*
1111

1212
@PublishedApi
13-
@ExperimentalSerializationApi
1413
@ExperimentalUnsignedTypes
1514
internal object UIntSerializer : KSerializer<UInt> {
1615
override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.UInt", Int.serializer())
@@ -25,7 +24,6 @@ internal object UIntSerializer : KSerializer<UInt> {
2524
}
2625

2726
@PublishedApi
28-
@ExperimentalSerializationApi
2927
@ExperimentalUnsignedTypes
3028
internal object ULongSerializer : KSerializer<ULong> {
3129
override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.ULong", Long.serializer())
@@ -40,7 +38,6 @@ internal object ULongSerializer : KSerializer<ULong> {
4038
}
4139

4240
@PublishedApi
43-
@ExperimentalSerializationApi
4441
@ExperimentalUnsignedTypes
4542
internal object UByteSerializer : KSerializer<UByte> {
4643
override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.UByte", Byte.serializer())
@@ -55,7 +52,6 @@ internal object UByteSerializer : KSerializer<UByte> {
5552
}
5653

5754
@PublishedApi
58-
@ExperimentalSerializationApi
5955
@ExperimentalUnsignedTypes
6056
internal object UShortSerializer : KSerializer<UShort> {
6157
override val descriptor: SerialDescriptor = InlinePrimitiveDescriptor("kotlin.UShort", Short.serializer())

0 commit comments

Comments
 (0)