diff --git a/build.gradle b/build.gradle index 0d21932ae..b1694c9c6 100644 --- a/build.gradle +++ b/build.gradle @@ -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 diff --git a/core/api/kotlinx-serialization-core.api b/core/api/kotlinx-serialization-core.api index bc7bbcedc..5cddb226d 100644 --- a/core/api/kotlinx-serialization-core.api +++ b/core/api/kotlinx-serialization-core.api @@ -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; diff --git a/core/commonMain/src/kotlinx/serialization/Serializers.kt b/core/commonMain/src/kotlinx/serialization/Serializers.kt index ff35a9f15..a7f892719 100644 --- a/core/commonMain/src/kotlinx/serialization/Serializers.kt +++ b/core/commonMain/src/kotlinx/serialization/Serializers.kt @@ -183,3 +183,35 @@ private fun KSerializer.nullable(shouldBeNullable: Boolean): KSeria if (shouldBeNullable) return nullable return this as KSerializer } + + +/** + * 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<*> { + return module.getContextual(kClass, argSerializers.asList()) ?: kClass.serializerNotRegistered() +} diff --git a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt index d04390af2..77fa593e9 100644 --- a/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt +++ b/core/commonTest/src/kotlinx/serialization/CachedSerializersTest.kt @@ -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 @@ -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() + } + val indirectMs = indirect.inWholeMicroseconds + if (indirectMs > directMs + (directMs / 4)) error("Direct ($directMs) and indirect ($indirectMs) times are too far apart") + } } + diff --git a/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt b/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt index 4fb61b043..42294b7e9 100644 --- a/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt +++ b/core/commonTest/src/kotlinx/serialization/SerializersLookupEnumTest.kt @@ -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( diff --git a/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt b/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt index 49efb9120..7117d37b0 100644 --- a/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt +++ b/core/commonTest/src/kotlinx/serialization/SerializersLookupObjectTest.kt @@ -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, diff --git a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt index 5227ca43a..450b3f42b 100644 --- a/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt +++ b/formats/json-tests/jvmTest/src/kotlinx/serialization/features/SerializerByTypeTest.kt @@ -15,6 +15,7 @@ import org.junit.Test import java.lang.reflect.* import kotlin.reflect.* import kotlin.test.* +import kotlin.time.* class SerializerByTypeTest { @@ -283,4 +284,19 @@ class SerializerByTypeTest { serializer(typeTokenOf>()) } } + + @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") + } }