From f0fd3a4adc1395dfde0af916dc6d561107bb62ed Mon Sep 17 00:00:00 2001 From: Vsevolod Tolstopyatov Date: Fri, 13 Nov 2020 11:23:06 +0300 Subject: [PATCH] Do not try to end structure in encode/decode stucture extensions if an exception has been thrown, so the original exception will be propagated Fixes #1187 --- .../serialization/encoding/Decoding.kt | 9 +++- .../serialization/encoding/Encoding.kt | 7 +++- .../serialization/EncodingExtensionsTest.kt | 41 +++++++++++++++++++ 3 files changed, 54 insertions(+), 3 deletions(-) create mode 100644 formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt diff --git a/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt b/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt index 20015e357c..2bc6aa87f6 100644 --- a/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt +++ b/core/commonMain/src/kotlinx/serialization/encoding/Decoding.kt @@ -5,9 +5,9 @@ package kotlinx.serialization.encoding import kotlinx.serialization.* +import kotlinx.serialization.builtins.* import kotlinx.serialization.descriptors.* import kotlinx.serialization.modules.* -import kotlinx.serialization.builtins.* /** * Decoder is a core deserialization primitive that encapsulates the knowledge of the underlying @@ -524,10 +524,15 @@ public inline fun Decoder.decodeStructure( block: CompositeDecoder.() -> T ): T { val composite = beginStructure(descriptor) + var ex: Throwable? = null try { return composite.block() + } catch (e: Throwable) { + ex = e + throw e } finally { - composite.endStructure(descriptor) + // End structure only if there is no exception, otherwise it can be swallowed + if (ex == null) composite.endStructure(descriptor) } } diff --git a/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt b/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt index 81191c126f..2547a27778 100644 --- a/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt +++ b/core/commonMain/src/kotlinx/serialization/encoding/Encoding.kt @@ -413,9 +413,14 @@ public interface CompositeEncoder { */ public inline fun Encoder.encodeStructure(descriptor: SerialDescriptor, block: CompositeEncoder.() -> Unit) { val composite = beginStructure(descriptor) + var ex: Throwable? = null try { composite.block() + } catch (e: Throwable) { + ex = e + throw e } finally { - composite.endStructure(descriptor) + // End structure only if there is no exception, otherwise it can be swallowed + if (ex == null) composite.endStructure(descriptor) } } diff --git a/formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt b/formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt new file mode 100644 index 0000000000..e8a0c48720 --- /dev/null +++ b/formats/json/commonTest/src/kotlinx/serialization/EncodingExtensionsTest.kt @@ -0,0 +1,41 @@ +package kotlinx.serialization + +import kotlinx.serialization.descriptors.* +import kotlinx.serialization.encoding.* +import kotlinx.serialization.json.* +import kotlin.test.* + +class EncodingExtensionsTest { + + @Serializable(with = BoxSerializer::class) + class Box(val i: Int) + + @Serializer(forClass = Box::class) + object BoxSerializer : KSerializer { + override val descriptor: SerialDescriptor = buildClassSerialDescriptor("Box") { + element("i") + } + + override fun serialize(encoder: Encoder, value: Box) { + encoder.encodeStructure(descriptor) { + throw ArithmeticException() + } + } + + override fun deserialize(decoder: Decoder): Box { + decoder.decodeStructure(descriptor) { + throw ArithmeticException() + } + } + } + + @Test + fun testEncodingExceptionNotSwallowed() { + assertFailsWith { Json.encodeToString(Box(1)) } + } + + @Test + fun testDecodingExceptionNotSwallowed() { + assertFailsWith { Json.decodeFromString("""{"i":1}""") } + } +}