Skip to content

Commit

Permalink
Add functions that can be used by compiler plugin for intrinsics
Browse files Browse the repository at this point in the history
  • Loading branch information
sandwwraith committed Sep 6, 2022
1 parent 49dad86 commit 51937bd
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 3 deletions.
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,15 @@ buildscript {
}
ext.experimentalsEnabled = ["-progressive", "-opt-in=kotlin.Experimental",
"-opt-in=kotlin.ExperimentalMultiplatform",
"-opt-in=kotlinx.serialization.InternalSerializationApi"
"-opt-in=kotlinx.serialization.InternalSerializationApi",
"-P", "plugin:org.jetbrains.kotlinx.serialization:disableIntrinsic=false"
]

ext.experimentalsInTestEnabled = ["-progressive", "-opt-in=kotlin.Experimental",
"-opt-in=kotlin.ExperimentalMultiplatform",
"-opt-in=kotlinx.serialization.ExperimentalSerializationApi",
"-opt-in=kotlinx.serialization.InternalSerializationApi"
"-opt-in=kotlinx.serialization.InternalSerializationApi",
"-P", "plugin:org.jetbrains.kotlinx.serialization:disableIntrinsic=false"
]
ext.koverEnabled = property('kover.enabled') ?: true

Expand Down
3 changes: 3 additions & 0 deletions core/api/kotlinx-serialization-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,9 @@ public abstract interface annotation class kotlinx/serialization/Serializer : ja
}

public final class kotlinx/serialization/SerializersKt {
public static final fun noCompiledSerializer (Ljava/lang/String;)Lkotlinx/serialization/KSerializer;
public static final fun noCompiledSerializer (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
public static final fun noCompiledSerializer (Lkotlinx/serialization/modules/SerializersModule;Lkotlin/reflect/KClass;[Lkotlinx/serialization/KSerializer;)Lkotlinx/serialization/KSerializer;
public static final fun serializer (Ljava/lang/reflect/Type;)Lkotlinx/serialization/KSerializer;
public static final fun serializer (Lkotlin/reflect/KClass;)Lkotlinx/serialization/KSerializer;
public static final fun serializer (Lkotlin/reflect/KType;)Lkotlinx/serialization/KSerializer;
Expand Down
32 changes: 32 additions & 0 deletions core/commonMain/src/kotlinx/serialization/Serializers.kt
Original file line number Diff line number Diff line change
Expand Up @@ -183,3 +183,35 @@ private fun <T : Any> KSerializer<T>.nullable(shouldBeNullable: Boolean): KSeria
if (shouldBeNullable) return nullable
return this as KSerializer<T?>
}


/**
* Overloads of [noCompiledSerializer] should never be called directly.
* Instead, compiler inserts calls to them when intrinsifying [serializer] function.
*
* If no serializer has been found in compile time, call to [noCompiledSerializer] inserted instead.
*/
@Suppress("unused")
@PublishedApi
internal fun noCompiledSerializer(forClass: String): KSerializer<*> {
throw SerializationException(
"Cannot find serializer for class $forClass.\n" +
"Make sure that this class marked with @Serializable annotation," +
"or provide serializer explicitly, or use proper SerializersModule"
)
}

// Used when compiler intrinsic is inserted
@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@PublishedApi
internal fun noCompiledSerializer(module: SerializersModule, kClass: KClass<*>): KSerializer<*> {
return module.getContextual(kClass) ?: kClass.serializerNotRegistered()
}

@OptIn(ExperimentalSerializationApi::class)
@Suppress("unused")
@PublishedApi
internal fun noCompiledSerializer(module: SerializersModule, kClass: KClass<*>, argSerializers: Array<KSerializer<*>>): KSerializer<*> {
return module.getContextual(kClass, argSerializers.asList()) ?: kClass.serializerNotRegistered()
}
20 changes: 20 additions & 0 deletions core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@

package kotlinx.serialization

import kotlinx.serialization.modules.*
import kotlinx.serialization.test.noJsLegacy
import kotlin.test.*
import kotlin.time.*

class CachedSerializersTest {
@Serializable
Expand Down Expand Up @@ -34,4 +36,22 @@ class CachedSerializersTest {
fun testAbstractSerializersAreSame() = noJsLegacy {
assertSame(Abstract.serializer(), Abstract.serializer())
}


@OptIn(ExperimentalTime::class)
@Test
@Ignore // TODO: Unignore after 1.8.0 update
fun testSerializersAreIntrinsified() {
val m = SerializersModule { }
val direct = measureTime {
Object.serializer()
}
val directMs = direct.inWholeMicroseconds
val indirect = measureTime {
m.serializer<Object>()
}
val indirectMs = indirect.inWholeMicroseconds
if (indirectMs > directMs + (directMs / 4)) error("Direct ($directMs) and indirect ($indirectMs) times are too far apart")
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class SerializersLookupEnumTest {
}

@Test
@Ignore // @Polymorphic enum doesn't make sense and is not supported in intrinsics
fun testEnumPolymorphic() {
if (isJvm()) {
assertEquals(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,8 @@ class SerializersLookupObjectTest {
}

@Test
fun testEnumPolymorphic() {
@Ignore // @Polymorphic object doesn't make sense and is not supported in intrinsics
fun testObjectPolymorphic() {
if (isJvm()) {
assertEquals(
PolymorphicSerializer(ObjectPolymorphic::class).descriptor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import org.junit.Test
import java.lang.reflect.*
import kotlin.reflect.*
import kotlin.test.*
import kotlin.time.*

class SerializerByTypeTest {

Expand Down Expand Up @@ -283,4 +284,19 @@ class SerializerByTypeTest {
serializer(typeTokenOf<Array<NonSerializable>>())
}
}

@OptIn(ExperimentalTime::class)
@Test
@Ignore // TODO: Unignore after 1.8.0 update
fun testSerializersAreIntrinsified() {
val direct = measureTime {
Json.encodeToString(IntData.serializer(), IntData(10))
}
val directMs = direct.inWholeMicroseconds
val indirect = measureTime {
Json.encodeToString(IntData(10))
}
val indirectMs = indirect.inWholeMicroseconds
if (indirectMs > directMs + (directMs / 4)) error("Direct ($directMs) and indirect ($indirectMs) times are too far apart")
}
}

0 comments on commit 51937bd

Please sign in to comment.