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 all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -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
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")
sandwwraith marked this conversation as resolved.
Show resolved Hide resolved
}
}

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

@Polymorphic
enum class EnumPolymorphic

@Serializable
enum class PlainEnum

Expand All @@ -72,17 +69,4 @@ class SerializersLookupEnumTest {
assertFails { serializer<EnumExternalClass>() }
}
}

@Test
fun testEnumPolymorphic() {
if (isJvm()) {
assertEquals(
PolymorphicSerializer(EnumPolymorphic::class).descriptor,
serializer<EnumPolymorphic>().descriptor
)
} else {
// FIXME serializer<PolymorphicEnum> is broken for K/JS and K/Native. Remove `assertFails` after fix
assertFails { serializer<EnumPolymorphic>() }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,6 @@ class SerializersLookupObjectTest {
}
}

@Polymorphic
object ObjectPolymorphic

@Serializable
object PlainObject

Expand Down Expand Up @@ -73,18 +70,4 @@ class SerializersLookupObjectTest {
assertIs<ObjectExternalClassSerializer>(serializer<ObjectExternalClass>())
}
}

@Test
fun testEnumPolymorphic() {
if (isJvm()) {
assertEquals(
PolymorphicSerializer(ObjectPolymorphic::class).descriptor,
serializer<ObjectPolymorphic>().descriptor
)
} else {
// FIXME serializer<PolymorphicObject> is broken for K/JS and K/Native. Remove `assertFails` after fix
assertFails { serializer<ObjectPolymorphic>() }
}

}
}
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")
}
}