Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functions that can be used by compiler plugin for intrinsics #2031

Merged
merged 2 commits into from Sep 15, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
6 changes: 4 additions & 2 deletions build.gradle
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
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
Expand Up @@ -216,3 +216,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
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")
sandwwraith marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Up @@ -74,6 +74,7 @@ class SerializersLookupEnumTest {
}

@Test
@Ignore // @Polymorphic enum doesn't make sense and is not supported in intrinsics
sandwwraith marked this conversation as resolved.
Show resolved Hide resolved
fun testEnumPolymorphic() {
if (isJvm()) {
assertEquals(
Expand Down
Expand Up @@ -75,7 +75,8 @@ class SerializersLookupObjectTest {
}

@Test
fun testEnumPolymorphic() {
@Ignore // @Polymorphic object doesn't make sense and is not supported in intrinsics
sandwwraith marked this conversation as resolved.
Show resolved Hide resolved
fun testObjectPolymorphic() {
if (isJvm()) {
assertEquals(
PolymorphicSerializer(ObjectPolymorphic::class).descriptor,
Expand Down
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")
}
}