Skip to content

Commit d217ee0

Browse files
committed
KTOR-7620 Make Url class @serializable and JVM Serializable
In our project we had to define our own UrlSerializer. It would be much nicer to have this in the Ktor library itself, so it works out of the box (similar to how Cookie was recently extended). Also, types like Url and Cookie should be java.io.Serializable. Otherwise Android crashes when using those types as e.g. screen arguments. This happens very quickly when Url is used indirectly as part of a data class where we wanted type safety.
1 parent 3d71a28 commit d217ee0

File tree

14 files changed

+216
-10
lines changed

14 files changed

+216
-10
lines changed

ktor-http/api/ktor-http.api

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,7 @@ public final class io/ktor/http/ContentTypesKt {
262262
public static final fun withCharsetIfNeeded (Lio/ktor/http/ContentType;Ljava/nio/charset/Charset;)Lio/ktor/http/ContentType;
263263
}
264264

265-
public final class io/ktor/http/Cookie {
265+
public final class io/ktor/http/Cookie : java/io/Serializable {
266266
public static final field Companion Lio/ktor/http/Cookie$Companion;
267267
public fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/ktor/http/CookieEncoding;Ljava/lang/Integer;Lio/ktor/util/date/GMTDate;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/Map;)V
268268
public synthetic fun <init> (Ljava/lang/String;Ljava/lang/String;Lio/ktor/http/CookieEncoding;Ljava/lang/Integer;Lio/ktor/util/date/GMTDate;Ljava/lang/String;Ljava/lang/String;ZZLjava/util/Map;ILkotlin/jvm/internal/DefaultConstructorMarker;)V
@@ -966,7 +966,7 @@ public final class io/ktor/http/URLParserKt {
966966
public static final fun takeFrom (Lio/ktor/http/URLBuilder;Ljava/lang/String;)Lio/ktor/http/URLBuilder;
967967
}
968968

969-
public final class io/ktor/http/URLProtocol {
969+
public final class io/ktor/http/URLProtocol : java/io/Serializable {
970970
public static final field Companion Lio/ktor/http/URLProtocol$Companion;
971971
public fun <init> (Ljava/lang/String;I)V
972972
public final fun component1 ()Ljava/lang/String;
@@ -1026,7 +1026,7 @@ public final class io/ktor/http/UnsafeHeaderException : java/lang/IllegalArgumen
10261026
public fun <init> (Ljava/lang/String;)V
10271027
}
10281028

1029-
public final class io/ktor/http/Url {
1029+
public final class io/ktor/http/Url : java/io/Serializable {
10301030
public static final field Companion Lio/ktor/http/Url$Companion;
10311031
public fun equals (Ljava/lang/Object;)Z
10321032
public final fun getEncodedFragment ()Ljava/lang/String;
@@ -1053,13 +1053,23 @@ public final class io/ktor/http/Url {
10531053
}
10541054

10551055
public final class io/ktor/http/Url$Companion {
1056+
public final fun serializer ()Lkotlinx/serialization/KSerializer;
10561057
}
10571058

10581059
public final class io/ktor/http/UrlKt {
10591060
public static final fun getAuthority (Lio/ktor/http/Url;)Ljava/lang/String;
10601061
public static final fun getProtocolWithAuthority (Lio/ktor/http/Url;)Ljava/lang/String;
10611062
}
10621063

1064+
public final class io/ktor/http/UrlSerializer : kotlinx/serialization/KSerializer {
1065+
public fun <init> ()V
1066+
public fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Lio/ktor/http/Url;
1067+
public synthetic fun deserialize (Lkotlinx/serialization/encoding/Decoder;)Ljava/lang/Object;
1068+
public fun getDescriptor ()Lkotlinx/serialization/descriptors/SerialDescriptor;
1069+
public fun serialize (Lkotlinx/serialization/encoding/Encoder;Lio/ktor/http/Url;)V
1070+
public synthetic fun serialize (Lkotlinx/serialization/encoding/Encoder;Ljava/lang/Object;)V
1071+
}
1072+
10631073
public final class io/ktor/http/auth/AuthScheme {
10641074
public static final field Basic Ljava/lang/String;
10651075
public static final field Bearer Ljava/lang/String;

ktor-http/api/ktor-http.klib.api

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -573,7 +573,7 @@ final class io.ktor.http/ContentType : io.ktor.http/HeaderValueWithParameters {
573573
}
574574
}
575575

576-
final class io.ktor.http/Cookie { // io.ktor.http/Cookie|null[0]
576+
final class io.ktor.http/Cookie : io.ktor.utils.io/JvmSerializable { // io.ktor.http/Cookie|null[0]
577577
constructor <init>(kotlin/String, kotlin/String, io.ktor.http/CookieEncoding = ..., kotlin/Int? = ..., io.ktor.util.date/GMTDate? = ..., kotlin/String? = ..., kotlin/String? = ..., kotlin/Boolean = ..., kotlin/Boolean = ..., kotlin.collections/Map<kotlin/String, kotlin/String?> = ...) // io.ktor.http/Cookie.<init>|<init>(kotlin.String;kotlin.String;io.ktor.http.CookieEncoding;kotlin.Int?;io.ktor.util.date.GMTDate?;kotlin.String?;kotlin.String?;kotlin.Boolean;kotlin.Boolean;kotlin.collections.Map<kotlin.String,kotlin.String?>){}[0]
578578

579579
final val domain // io.ktor.http/Cookie.domain|{}domain[0]
@@ -1048,7 +1048,7 @@ final class io.ktor.http/URLParserException : kotlin/IllegalStateException { //
10481048
constructor <init>(kotlin/String, kotlin/Throwable) // io.ktor.http/URLParserException.<init>|<init>(kotlin.String;kotlin.Throwable){}[0]
10491049
}
10501050

1051-
final class io.ktor.http/URLProtocol { // io.ktor.http/URLProtocol|null[0]
1051+
final class io.ktor.http/URLProtocol : io.ktor.utils.io/JvmSerializable { // io.ktor.http/URLProtocol|null[0]
10521052
constructor <init>(kotlin/String, kotlin/Int) // io.ktor.http/URLProtocol.<init>|<init>(kotlin.String;kotlin.Int){}[0]
10531053

10541054
final val defaultPort // io.ktor.http/URLProtocol.defaultPort|{}defaultPort[0]
@@ -1085,7 +1085,7 @@ final class io.ktor.http/UnsafeHeaderException : kotlin/IllegalArgumentException
10851085
constructor <init>(kotlin/String) // io.ktor.http/UnsafeHeaderException.<init>|<init>(kotlin.String){}[0]
10861086
}
10871087

1088-
final class io.ktor.http/Url { // io.ktor.http/Url|null[0]
1088+
final class io.ktor.http/Url : io.ktor.utils.io/JvmSerializable { // io.ktor.http/Url|null[0]
10891089
final val encodedFragment // io.ktor.http/Url.encodedFragment|{}encodedFragment[0]
10901090
final fun <get-encodedFragment>(): kotlin/String // io.ktor.http/Url.encodedFragment.<get-encodedFragment>|<get-encodedFragment>(){}[0]
10911091
final val encodedPassword // io.ktor.http/Url.encodedPassword|{}encodedPassword[0]
@@ -1129,7 +1129,19 @@ final class io.ktor.http/Url { // io.ktor.http/Url|null[0]
11291129
final fun hashCode(): kotlin/Int // io.ktor.http/Url.hashCode|hashCode(){}[0]
11301130
final fun toString(): kotlin/String // io.ktor.http/Url.toString|toString(){}[0]
11311131

1132-
final object Companion // io.ktor.http/Url.Companion|null[0]
1132+
final object Companion { // io.ktor.http/Url.Companion|null[0]
1133+
final fun serializer(): kotlinx.serialization/KSerializer<io.ktor.http/Url> // io.ktor.http/Url.Companion.serializer|serializer(){}[0]
1134+
}
1135+
}
1136+
1137+
final class io.ktor.http/UrlSerializer : kotlinx.serialization/KSerializer<io.ktor.http/Url> { // io.ktor.http/UrlSerializer|null[0]
1138+
constructor <init>() // io.ktor.http/UrlSerializer.<init>|<init>(){}[0]
1139+
1140+
final val descriptor // io.ktor.http/UrlSerializer.descriptor|{}descriptor[0]
1141+
final fun <get-descriptor>(): kotlinx.serialization.descriptors/SerialDescriptor // io.ktor.http/UrlSerializer.descriptor.<get-descriptor>|<get-descriptor>(){}[0]
1142+
1143+
final fun deserialize(kotlinx.serialization.encoding/Decoder): io.ktor.http/Url // io.ktor.http/UrlSerializer.deserialize|deserialize(kotlinx.serialization.encoding.Decoder){}[0]
1144+
final fun serialize(kotlinx.serialization.encoding/Encoder, io.ktor.http/Url) // io.ktor.http/UrlSerializer.serialize|serialize(kotlinx.serialization.encoding.Encoder;io.ktor.http.Url){}[0]
11331145
}
11341146

11351147
sealed class io.ktor.http.auth/HttpAuthHeader { // io.ktor.http.auth/HttpAuthHeader|null[0]

ktor-http/build.gradle.kts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,12 @@ kotlin {
1414
api(libs.kotlinx.serialization.core)
1515
}
1616
}
17+
jvmTest {
18+
dependencies {
19+
implementation(project(":ktor-shared:ktor-junit"))
20+
implementation(project(":ktor-shared:ktor-serialization:ktor-serialization-kotlinx"))
21+
implementation(project(":ktor-shared:ktor-serialization:ktor-serialization-kotlinx:ktor-serialization-kotlinx-json"))
22+
}
23+
}
1724
}
1825
}

ktor-http/common/src/io/ktor/http/Cookie.kt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package io.ktor.http
66

77
import io.ktor.util.*
88
import io.ktor.util.date.*
9+
import io.ktor.utils.io.*
910
import kotlinx.serialization.*
1011
import kotlin.jvm.*
1112

@@ -37,7 +38,17 @@ public data class Cookie(
3738
val secure: Boolean = false,
3839
val httpOnly: Boolean = false,
3940
val extensions: Map<String, String?> = emptyMap()
40-
)
41+
) : JvmSerializable {
42+
private fun writeReplace(): Any = JvmSerializerReplacement(CookieJvmSerializer, this)
43+
}
44+
45+
internal object CookieJvmSerializer : JvmSerializer<Cookie> {
46+
override fun jvmSerialize(value: Cookie): ByteArray =
47+
renderSetCookieHeader(value).encodeToByteArray()
48+
49+
override fun jvmDeserialize(value: ByteArray): Cookie =
50+
parseServerSetCookieHeader(value.decodeToString())
51+
}
4152

4253
/**
4354
* Cooke encoding strategy

ktor-http/common/src/io/ktor/http/URLProtocol.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@
55
package io.ktor.http
66

77
import io.ktor.util.*
8+
import io.ktor.utils.io.*
89

910
/**
1011
* Represents URL protocol
1112
* @property name of protocol (schema)
1213
* @property defaultPort default port for protocol or `-1` if not known
1314
*/
14-
public data class URLProtocol(val name: String, val defaultPort: Int) {
15+
public data class URLProtocol(val name: String, val defaultPort: Int) : JvmSerializable {
1516
init {
1617
require(name.all { it.isLowerCase() }) { "All characters should be lower case" }
1718
}

ktor-http/common/src/io/ktor/http/Url.kt

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@
44

55
package io.ktor.http
66

7+
import io.ktor.utils.io.*
8+
import kotlinx.serialization.*
9+
import kotlinx.serialization.descriptors.*
10+
import kotlinx.serialization.encoding.*
11+
712
/**
813
* Represents an immutable URL
914
*
@@ -18,6 +23,7 @@ package io.ktor.http
1823
* @property password password part of URL
1924
* @property trailingQuery keep trailing question character even if there are no query parameters
2025
*/
26+
@Serializable(with = UrlSerializer::class)
2127
public class Url internal constructor(
2228
protocol: URLProtocol?,
2329
public val host: String,
@@ -29,7 +35,7 @@ public class Url internal constructor(
2935
public val password: String?,
3036
public val trailingQuery: Boolean,
3137
private val urlString: String
32-
) {
38+
) : JvmSerializable {
3339
init {
3440
require(specifiedPort in 0..65535) {
3541
"Port must be between 0 and 65535, or $DEFAULT_PORT if not set. Provided: $specifiedPort"
@@ -222,6 +228,8 @@ public class Url internal constructor(
222228
return urlString.hashCode()
223229
}
224230

231+
private fun writeReplace(): Any = JvmSerializerReplacement(UrlJvmSerializer, this)
232+
225233
public companion object
226234
}
227235

@@ -254,3 +262,22 @@ internal val Url.encodedUserAndPassword: String
254262
get() = buildString {
255263
appendUserAndPassword(encodedUser, encodedPassword)
256264
}
265+
266+
public class UrlSerializer : KSerializer<Url> {
267+
override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("Url", PrimitiveKind.STRING)
268+
269+
override fun deserialize(decoder: Decoder): Url =
270+
Url(decoder.decodeString())
271+
272+
override fun serialize(encoder: Encoder, value: Url) {
273+
encoder.encodeString(value.toString())
274+
}
275+
}
276+
277+
internal object UrlJvmSerializer : JvmSerializer<Url> {
278+
override fun jvmSerialize(value: Url): ByteArray =
279+
value.toString().encodeToByteArray()
280+
281+
override fun jvmDeserialize(value: ByteArray): Url =
282+
Url(value.decodeToString())
283+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// ktlint-disable filename
2+
/*
3+
* Copyright 2014-2019 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
4+
*/
5+
6+
package io.ktor.tests.http
7+
8+
import io.ktor.http.*
9+
import io.ktor.junit.*
10+
import kotlin.test.*
11+
12+
class SerializableTest {
13+
@Test
14+
fun urlTest() {
15+
val url = Url("https://localhost/path?key=value#fragment")
16+
assertEquals(url, assertSerializable(url))
17+
}
18+
19+
@Test
20+
fun cookieTest() {
21+
val cookie = Cookie("key", "value")
22+
assertEquals(cookie, assertSerializable(cookie))
23+
}
24+
}

ktor-io/api/ktor-io.api

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,17 @@ public final class io/ktor/utils/io/CountedByteWriteChannelKt {
209209
public static final fun counted (Lio/ktor/utils/io/ByteWriteChannel;)Lio/ktor/utils/io/CountedByteWriteChannel;
210210
}
211211

212+
public final class io/ktor/utils/io/DefaultJvmSerializerReplacement : java/io/Externalizable {
213+
public static final field Companion Lio/ktor/utils/io/DefaultJvmSerializerReplacement$Companion;
214+
public fun <init> ()V
215+
public fun <init> (Lio/ktor/utils/io/JvmSerializer;Ljava/lang/Object;)V
216+
public fun readExternal (Ljava/io/ObjectInput;)V
217+
public fun writeExternal (Ljava/io/ObjectOutput;)V
218+
}
219+
220+
public final class io/ktor/utils/io/DefaultJvmSerializerReplacement$Companion {
221+
}
222+
212223
public final class io/ktor/utils/io/DeprecationKt {
213224
public static final fun readText (Lkotlinx/io/Source;)Ljava/lang/String;
214225
public static final fun release (Lkotlinx/io/Sink;)V
@@ -217,6 +228,15 @@ public final class io/ktor/utils/io/DeprecationKt {
217228
public abstract interface annotation class io/ktor/utils/io/InternalAPI : java/lang/annotation/Annotation {
218229
}
219230

231+
public final class io/ktor/utils/io/JvmSerializable_jvmKt {
232+
public static final fun JvmSerializerReplacement (Lio/ktor/utils/io/JvmSerializer;Ljava/lang/Object;)Ljava/lang/Object;
233+
}
234+
235+
public abstract interface class io/ktor/utils/io/JvmSerializer : java/io/Serializable {
236+
public abstract fun jvmDeserialize ([B)Ljava/lang/Object;
237+
public abstract fun jvmSerialize (Ljava/lang/Object;)[B
238+
}
239+
220240
public abstract interface annotation class io/ktor/utils/io/KtorDsl : java/lang/annotation/Annotation {
221241
}
222242

ktor-io/api/ktor-io.klib.api

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,11 @@ abstract interface <#A: kotlin/Any> io.ktor.utils.io.pool/ObjectPool : kotlin/Au
5353
open fun close() // io.ktor.utils.io.pool/ObjectPool.close|close(){}[0]
5454
}
5555

56+
abstract interface <#A: kotlin/Any?> io.ktor.utils.io/JvmSerializer : io.ktor.utils.io/JvmSerializable { // io.ktor.utils.io/JvmSerializer|null[0]
57+
abstract fun jvmDeserialize(kotlin/ByteArray): #A // io.ktor.utils.io/JvmSerializer.jvmDeserialize|jvmDeserialize(kotlin.ByteArray){}[0]
58+
abstract fun jvmSerialize(#A): kotlin/ByteArray // io.ktor.utils.io/JvmSerializer.jvmSerialize|jvmSerialize(1:0){}[0]
59+
}
60+
5661
abstract interface io.ktor.utils.io.core/Closeable { // io.ktor.utils.io.core/Closeable|null[0]
5762
abstract fun close() // io.ktor.utils.io.core/Closeable.close|close(){}[0]
5863
}
@@ -97,6 +102,8 @@ abstract interface io.ktor.utils.io/ChannelJob { // io.ktor.utils.io/ChannelJob|
97102
abstract fun <get-job>(): kotlinx.coroutines/Job // io.ktor.utils.io/ChannelJob.job.<get-job>|<get-job>(){}[0]
98103
}
99104

105+
abstract interface io.ktor.utils.io/JvmSerializable // io.ktor.utils.io/JvmSerializable|null[0]
106+
100107
abstract class <#A: kotlin/Any> io.ktor.utils.io.pool/DefaultPool : io.ktor.utils.io.pool/ObjectPool<#A> { // io.ktor.utils.io.pool/DefaultPool|null[0]
101108
constructor <init>(kotlin/Int) // io.ktor.utils.io.pool/DefaultPool.<init>|<init>(kotlin.Int){}[0]
102109

@@ -399,6 +406,7 @@ final fun (kotlinx.io/Source).io.ktor.utils.io.core/readTextExactCharacters(kotl
399406
final fun (kotlinx.io/Source).io.ktor.utils.io.core/release() // io.ktor.utils.io.core/release|[email protected](){}[0]
400407
final fun (kotlinx.io/Source).io.ktor.utils.io.core/takeWhile(kotlin/Function1<kotlinx.io/Buffer, kotlin/Boolean>) // io.ktor.utils.io.core/takeWhile|[email protected](kotlin.Function1<kotlinx.io.Buffer,kotlin.Boolean>){}[0]
401408
final fun (kotlinx.io/Source).io.ktor.utils.io/readText(): kotlin/String // io.ktor.utils.io/readText|[email protected](){}[0]
409+
final fun <#A: kotlin/Any> io.ktor.utils.io/JvmSerializerReplacement(io.ktor.utils.io/JvmSerializer<#A>, #A): kotlin/Any // io.ktor.utils.io/JvmSerializerReplacement|JvmSerializerReplacement(io.ktor.utils.io.JvmSerializer<0:0>;0:0){0§<kotlin.Any>}[0]
402410
final fun <#A: kotlin/Any?> (kotlinx.io/Sink).io.ktor.utils.io.core/preview(kotlin/Function1<kotlinx.io/Source, #A>): #A // io.ktor.utils.io.core/preview|[email protected](kotlin.Function1<kotlinx.io.Source,0:0>){0§<kotlin.Any?>}[0]
403411
final fun <#A: kotlin/Any?> (kotlinx.io/Source).io.ktor.utils.io.core/preview(kotlin/Function1<kotlinx.io/Source, #A>): #A // io.ktor.utils.io.core/preview|[email protected](kotlin.Function1<kotlinx.io.Source,0:0>){0§<kotlin.Any?>}[0]
404412
final fun <#A: kotlin/Any?> io.ktor.utils.io.core/withMemory(kotlin/Int, kotlin/Function1<kotlin/ByteArray, #A>): #A // io.ktor.utils.io.core/withMemory|withMemory(kotlin.Int;kotlin.Function1<kotlin.ByteArray,0:0>){0§<kotlin.Any?>}[0]
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
/*
2+
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.utils.io
6+
7+
public expect interface JvmSerializable
8+
9+
public interface JvmSerializer<T> : JvmSerializable {
10+
public fun jvmSerialize(value: T): ByteArray
11+
public fun jvmDeserialize(value: ByteArray): T
12+
}
13+
14+
public expect fun <T : Any> JvmSerializerReplacement(serializer: JvmSerializer<T>, value: T): Any
15+
16+
internal object DummyJvmSimpleSerializerReplacement
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.utils.io
6+
7+
/** Alias for `java.io.Serializable` on JVM. Empty interface otherwise. */
8+
public actual interface JvmSerializable
9+
10+
public actual fun <T : Any> JvmSerializerReplacement(serializer: JvmSerializer<T>, value: T): Any =
11+
DummyJvmSimpleSerializerReplacement
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.utils.io
6+
7+
import java.io.*
8+
9+
public actual typealias JvmSerializable = Serializable
10+
11+
@Suppress("UNCHECKED_CAST")
12+
public actual fun <T : Any> JvmSerializerReplacement(serializer: JvmSerializer<T>, value: T): Any =
13+
DefaultJvmSerializerReplacement(serializer, value)
14+
15+
@PublishedApi // IMPORTANT: changing the class name would result in serialization incompatibility
16+
internal class DefaultJvmSerializerReplacement<T : Any>(
17+
private var serializer: JvmSerializer<T>?,
18+
private var value: T?
19+
) : Externalizable {
20+
constructor() : this(null, null)
21+
22+
override fun writeExternal(out: ObjectOutput) {
23+
out.writeObject(serializer)
24+
out.writeObject(serializer!!.jvmSerialize(value!!))
25+
}
26+
27+
@Suppress("UNCHECKED_CAST")
28+
override fun readExternal(`in`: ObjectInput) {
29+
serializer = `in`.readObject() as JvmSerializer<T>
30+
value = serializer!!.jvmDeserialize(`in`.readObject() as ByteArray)
31+
}
32+
33+
private fun readResolve(): Any =
34+
value!!
35+
36+
companion object {
37+
private const val serialVersionUID: Long = 0L
38+
}
39+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
/*
2+
* Copyright 2014-2024 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license.
3+
*/
4+
5+
package io.ktor.utils.io
6+
7+
/** Alias for `java.io.Serializable` on JVM. Empty interface otherwise. */
8+
public actual interface JvmSerializable
9+
10+
public actual fun <T : Any> JvmSerializerReplacement(serializer: JvmSerializer<T>, value: T): Any =
11+
DummyJvmSimpleSerializerReplacement

0 commit comments

Comments
 (0)