From 0b015e13599a159cca198d5d5e633b90211e6f85 Mon Sep 17 00:00:00 2001 From: Leonid Startsev Date: Fri, 30 Aug 2024 19:04:24 +0200 Subject: [PATCH] Ignore NoClassDefFoundError when initializing builtins map for serializer() function. Normally they should not occur, but in rare setups when runtime stdlib may be lower than 2.0, we may want to ignore errors about experimental classes. Fixes #2803 --- .../serialization/internal/Primitives.kt | 35 ++---------- .../serialization/internal/Platform.kt | 36 +++++++++++++ .../serialization/internal/Platform.kt | 53 +++++++++++++++++++ .../serialization/internal/Platform.kt | 36 +++++++++++++ .../serialization/internal/Platform.kt | 36 +++++++++++++ 5 files changed, 164 insertions(+), 32 deletions(-) diff --git a/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt b/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt index 71181ded4e..c26a0b831f 100644 --- a/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt +++ b/core/commonMain/src/kotlinx/serialization/internal/Primitives.kt @@ -15,38 +15,9 @@ import kotlin.reflect.* import kotlin.time.Duration import kotlin.uuid.* -@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class) -private val BUILTIN_SERIALIZERS = mapOf( - String::class to String.serializer(), - Char::class to Char.serializer(), - CharArray::class to CharArraySerializer(), - Double::class to Double.serializer(), - DoubleArray::class to DoubleArraySerializer(), - Float::class to Float.serializer(), - FloatArray::class to FloatArraySerializer(), - Long::class to Long.serializer(), - LongArray::class to LongArraySerializer(), - ULong::class to ULong.serializer(), - ULongArray::class to ULongArraySerializer(), - Int::class to Int.serializer(), - IntArray::class to IntArraySerializer(), - UInt::class to UInt.serializer(), - UIntArray::class to UIntArraySerializer(), - Short::class to Short.serializer(), - ShortArray::class to ShortArraySerializer(), - UShort::class to UShort.serializer(), - UShortArray::class to UShortArraySerializer(), - Byte::class to Byte.serializer(), - ByteArray::class to ByteArraySerializer(), - UByte::class to UByte.serializer(), - UByteArray::class to UByteArraySerializer(), - Boolean::class to Boolean.serializer(), - BooleanArray::class to BooleanArraySerializer(), - Unit::class to Unit.serializer(), - Nothing::class to NothingSerializer(), - Duration::class to Duration.serializer(), - Uuid::class to Uuid.serializer() -) +private val BUILTIN_SERIALIZERS = initBuiltins() + +internal expect fun initBuiltins(): Map, KSerializer<*>> internal class PrimitiveSerialDescriptor( override val serialName: String, diff --git a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt index 571c3f7979..e423afca2a 100644 --- a/core/jsMain/src/kotlinx/serialization/internal/Platform.kt +++ b/core/jsMain/src/kotlinx/serialization/internal/Platform.kt @@ -5,7 +5,10 @@ package kotlinx.serialization.internal import kotlinx.serialization.* +import kotlinx.serialization.builtins.* import kotlin.reflect.* +import kotlin.time.* +import kotlin.uuid.* internal actual fun Array.getChecked(index: Int): T { if (index !in indices) throw IndexOutOfBoundsException("Index $index out of bounds $indices") @@ -77,3 +80,36 @@ private val KClass<*>.isInterface: Boolean if (this === Nothing::class) return false return js.asDynamic().`$metadata$`?.kind == "interface" } + +@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class) +internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf( + String::class to String.serializer(), + Char::class to Char.serializer(), + CharArray::class to CharArraySerializer(), + Double::class to Double.serializer(), + DoubleArray::class to DoubleArraySerializer(), + Float::class to Float.serializer(), + FloatArray::class to FloatArraySerializer(), + Long::class to Long.serializer(), + LongArray::class to LongArraySerializer(), + ULong::class to ULong.serializer(), + ULongArray::class to ULongArraySerializer(), + Int::class to Int.serializer(), + IntArray::class to IntArraySerializer(), + UInt::class to UInt.serializer(), + UIntArray::class to UIntArraySerializer(), + Short::class to Short.serializer(), + ShortArray::class to ShortArraySerializer(), + UShort::class to UShort.serializer(), + UShortArray::class to UShortArraySerializer(), + Byte::class to Byte.serializer(), + ByteArray::class to ByteArraySerializer(), + UByte::class to UByte.serializer(), + UByteArray::class to UByteArraySerializer(), + Boolean::class to Boolean.serializer(), + BooleanArray::class to BooleanArraySerializer(), + Unit::class to Unit.serializer(), + Nothing::class to NothingSerializer(), + Duration::class to Duration.serializer(), + Uuid::class to Uuid.serializer() +) diff --git a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt index 5cf32d0e5d..b838a0fe52 100644 --- a/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt +++ b/core/jvmMain/src/kotlinx/serialization/internal/Platform.kt @@ -5,8 +5,11 @@ package kotlinx.serialization.internal import kotlinx.serialization.* +import kotlinx.serialization.builtins.* import java.lang.reflect.* import kotlin.reflect.* +import kotlin.time.* +import kotlin.uuid.* @Suppress("NOTHING_TO_INLINE") internal actual inline fun Array.getChecked(index: Int): T { @@ -158,3 +161,53 @@ private fun Class.findObjectSerializer(): KSerializer? { } internal actual fun isReferenceArray(rootClass: KClass): Boolean = rootClass.java.isArray + +@OptIn(ExperimentalSerializationApi::class) +internal actual fun initBuiltins(): Map, KSerializer<*>> = buildMap { + // Standard classes are always present + put(String::class, String.serializer()) + put(Char::class, Char.serializer()) + put(CharArray::class, CharArraySerializer()) + put(Double::class, Double.serializer()) + put(DoubleArray::class, DoubleArraySerializer()) + put(Float::class, Float.serializer()) + put(FloatArray::class, FloatArraySerializer()) + put(Long::class, Long.serializer()) + put(LongArray::class, LongArraySerializer()) + put(ULong::class, ULong.serializer()) + put(Int::class, Int.serializer()) + put(IntArray::class, IntArraySerializer()) + put(UInt::class, UInt.serializer()) + put(Short::class, Short.serializer()) + put(ShortArray::class, ShortArraySerializer()) + put(UShort::class, UShort.serializer()) + put(Byte::class, Byte.serializer()) + put(ByteArray::class, ByteArraySerializer()) + put(UByte::class, UByte.serializer()) + put(Boolean::class, Boolean.serializer()) + put(BooleanArray::class, BooleanArraySerializer()) + put(Unit::class, Unit.serializer()) + put(Nothing::class, NothingSerializer()) + + // Duration is a stable class, but may be missing in very old stdlibs + loadSafe { put(Duration::class, Duration.serializer()) } + + // Experimental types that may be missing + @OptIn(ExperimentalUnsignedTypes::class) run { + loadSafe { put(ULongArray::class, ULongArraySerializer()) } + loadSafe { put(UIntArray::class, UIntArraySerializer()) } + loadSafe { put(UShortArray::class, UShortArraySerializer()) } + loadSafe { put(UByteArray::class, UByteArraySerializer()) } + } + @OptIn(ExperimentalUuidApi::class) + loadSafe { put(Uuid::class, Uuid.serializer()) } +} + +// Reference classes in [block] ignoring any exceptions related to class loading +private inline fun loadSafe(block: () -> Unit) { + try { + block() + } catch (_: NoClassDefFoundError) { + } catch (_: ClassNotFoundException) { + } +} diff --git a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt index ddf690c25c..c2e9fdd7e0 100644 --- a/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt +++ b/core/nativeMain/src/kotlinx/serialization/internal/Platform.kt @@ -5,7 +5,10 @@ package kotlinx.serialization.internal import kotlinx.serialization.* +import kotlinx.serialization.builtins.* import kotlin.reflect.* +import kotlin.time.* +import kotlin.uuid.* @Suppress("NOTHING_TO_INLINE") internal actual inline fun Array.getChecked(index: Int): T { @@ -71,3 +74,36 @@ internal actual fun ArrayList.toNativeArrayImpl(eClass: KCl private fun arrayOfAnyNulls(size: Int): Array = arrayOfNulls(size) as Array internal actual fun isReferenceArray(rootClass: KClass): Boolean = rootClass == Array::class + +@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class) +internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf( + String::class to String.serializer(), + Char::class to Char.serializer(), + CharArray::class to CharArraySerializer(), + Double::class to Double.serializer(), + DoubleArray::class to DoubleArraySerializer(), + Float::class to Float.serializer(), + FloatArray::class to FloatArraySerializer(), + Long::class to Long.serializer(), + LongArray::class to LongArraySerializer(), + ULong::class to ULong.serializer(), + ULongArray::class to ULongArraySerializer(), + Int::class to Int.serializer(), + IntArray::class to IntArraySerializer(), + UInt::class to UInt.serializer(), + UIntArray::class to UIntArraySerializer(), + Short::class to Short.serializer(), + ShortArray::class to ShortArraySerializer(), + UShort::class to UShort.serializer(), + UShortArray::class to UShortArraySerializer(), + Byte::class to Byte.serializer(), + ByteArray::class to ByteArraySerializer(), + UByte::class to UByte.serializer(), + UByteArray::class to UByteArraySerializer(), + Boolean::class to Boolean.serializer(), + BooleanArray::class to BooleanArraySerializer(), + Unit::class to Unit.serializer(), + Nothing::class to NothingSerializer(), + Duration::class to Duration.serializer(), + Uuid::class to Uuid.serializer() +) diff --git a/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt b/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt index 146845be3a..ecfee8ede8 100644 --- a/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt +++ b/core/wasmMain/src/kotlinx/serialization/internal/Platform.kt @@ -5,7 +5,10 @@ package kotlinx.serialization.internal import kotlinx.serialization.* +import kotlinx.serialization.builtins.* import kotlin.reflect.* +import kotlin.time.* +import kotlin.uuid.* @Suppress("NOTHING_TO_INLINE") internal actual inline fun Array.getChecked(index: Int): T { @@ -61,3 +64,36 @@ internal actual fun createParametrizedCache(factory: (KClass, List ArrayList.toNativeArrayImpl(eClass: KClass): Array = toTypedArray() internal actual fun isReferenceArray(rootClass: KClass): Boolean = rootClass == Array::class + +@OptIn(ExperimentalUnsignedTypes::class, ExperimentalUuidApi::class, ExperimentalSerializationApi::class) +internal actual fun initBuiltins(): Map, KSerializer<*>> = mapOf( + String::class to String.serializer(), + Char::class to Char.serializer(), + CharArray::class to CharArraySerializer(), + Double::class to Double.serializer(), + DoubleArray::class to DoubleArraySerializer(), + Float::class to Float.serializer(), + FloatArray::class to FloatArraySerializer(), + Long::class to Long.serializer(), + LongArray::class to LongArraySerializer(), + ULong::class to ULong.serializer(), + ULongArray::class to ULongArraySerializer(), + Int::class to Int.serializer(), + IntArray::class to IntArraySerializer(), + UInt::class to UInt.serializer(), + UIntArray::class to UIntArraySerializer(), + Short::class to Short.serializer(), + ShortArray::class to ShortArraySerializer(), + UShort::class to UShort.serializer(), + UShortArray::class to UShortArraySerializer(), + Byte::class to Byte.serializer(), + ByteArray::class to ByteArraySerializer(), + UByte::class to UByte.serializer(), + UByteArray::class to UByteArraySerializer(), + Boolean::class to Boolean.serializer(), + BooleanArray::class to BooleanArraySerializer(), + Unit::class to Unit.serializer(), + Nothing::class to NothingSerializer(), + Duration::class to Duration.serializer(), + Uuid::class to Uuid.serializer() +)