[IR, Serialization] Support kotlinx-based (de)serialization of MFVC, nullable MFVC value assignment to nonnull variable

Signed-off-by: Evgeniy.Zhelenskiy <Evgeniy.Zhelenskiy@jetbrains.com>

#KT-1179
This commit is contained in:
Evgeniy.Zhelenskiy
2022-12-13 17:08:35 +01:00
parent 561df74419
commit f6c63c6e4f
9 changed files with 118 additions and 15 deletions
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.ir.builders.declarations.addFunction
import org.jetbrains.kotlin.ir.builders.declarations.addTypeParameter
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
import org.jetbrains.kotlin.ir.declarations.IrClass
import org.jetbrains.kotlin.ir.declarations.isSingleFieldValueClass
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.companionObject
import org.jetbrains.kotlin.name.Name
@@ -102,7 +103,7 @@ class IrPreGenerator(
if (irClass.hasCompanionObjectAsSerializer && irClass.companionObject()
?.findPluginGeneratedMethod(SerialEntityNames.LOAD, compilerContext.afterK2) == null
) return
if (irClass.isValue) return
if (irClass.isSingleFieldValueClass) return
if (irClass.findSerializableSyntheticConstructor() != null) return
val ctor = irClass.addConstructor {
origin = SERIALIZATION_PLUGIN_ORIGIN
@@ -121,7 +122,7 @@ class IrPreGenerator(
ctor.addValueParameter(prop.name, prop.type.makeNullableIfNotPrimitive(), SERIALIZATION_PLUGIN_ORIGIN)
}
ctor.addValueParameter(SerialEntityNames.dummyParamName, markerClassSymbol.defaultType, SERIALIZATION_PLUGIN_ORIGIN)
ctor.addValueParameter(SerialEntityNames.dummyParamName, markerClassSymbol.defaultType.makeNullable(), SERIALIZATION_PLUGIN_ORIGIN)
}
private fun IrType.makeNullableIfNotPrimitive() =
@@ -253,4 +253,4 @@ fun IrSimpleType.argumentTypesOrUpperBounds(): List<IrType> {
}
internal inline fun IrClass.shouldHaveSpecificSyntheticMethods(functionPresenceChecker: () -> IrSimpleFunction?) =
!isValue && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
!isSingleFieldValueClass && (isAbstractOrSealedSerializableClass || functionPresenceChecker() != null)
@@ -634,7 +634,7 @@ open class SerializerIrGenerator(
irClass,
context
)
serializableDesc.isValue -> SerializerForInlineClassGenerator(irClass, context)
serializableDesc.isSingleFieldValueClass -> SerializerForInlineClassGenerator(irClass, context)
else -> SerializerIrGenerator(irClass, context, metadataPlugin)
}
generator.generate()
@@ -12,7 +12,7 @@ import org.jetbrains.kotlin.diagnostics.*
import org.jetbrains.kotlin.fir.FirSession
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
import org.jetbrains.kotlin.fir.analysis.checkers.isValueClass
import org.jetbrains.kotlin.fir.analysis.checkers.isSingleFieldValueClass
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
import org.jetbrains.kotlin.fir.declarations.*
import org.jetbrains.kotlin.fir.declarations.utils.*
@@ -485,7 +485,7 @@ object FirSerializationPluginClassChecker : FirClassChecker() {
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
private val ConeKotlinType.isUnsupportedInlineType: Boolean
get() = isValueClass(session) && !isPrimitiveOrNullablePrimitive
get() = isSingleFieldValueClass(session) && !isPrimitiveOrNullablePrimitive
context(CheckerContext)
@Suppress("IncorrectFormatting") // KTIJ-22227
@@ -0,0 +1,68 @@
// TARGET_BACKEND: JVM_IR
// WITH_STDLIB
// LANGUAGE: +ValueClasses
import kotlinx.serialization.*
import kotlinx.serialization.json.*
import kotlinx.serialization.internal.*
@Serializable
sealed interface I
@Serializable
@JvmInline
value class DPoint(val x: Double, val y: Double): I
@Serializable
@JvmInline
value class DSegment(val p1: DPoint, val p2: DPoint): I
@Serializable
data class PointWrapper(val value: DPoint)
@Serializable
data class SegmentWrapper(val value: DSegment)
fun box(): String {
val p1 = DPoint(1.0, 2.0)
val dSegment = DSegment(p1, DPoint(3.0, 4.0))
run {
val s = Json.encodeToString(DPoint.serializer(), p1)
if (s != """{"x":1.0,"y":2.0}""") return s
val decoded = Json.decodeFromString(DPoint.serializer(), s)
if (p1 != decoded) return decoded.toString()
}
run {
val s = Json.encodeToString(DSegment.serializer(), dSegment)
if (s != """{"p1":{"x":1.0,"y":2.0},"p2":{"x":3.0,"y":4.0}}""") return s
val decoded = Json.decodeFromString(DSegment.serializer(), s)
if (dSegment != decoded) return decoded.toString()
}
run {
val pointWrapper = PointWrapper(p1)
val s = Json.encodeToString(PointWrapper.serializer(), pointWrapper)
if (s != """{"value":{"x":1.0,"y":2.0}}""") return s
val decoded = Json.decodeFromString(PointWrapper.serializer(), s)
if (pointWrapper != decoded) return decoded.toString()
}
run {
val segmentWrapper = SegmentWrapper(dSegment)
val s = Json.encodeToString(SegmentWrapper.serializer(), segmentWrapper)
if (s != """{"value":{"p1":{"x":1.0,"y":2.0},"p2":{"x":3.0,"y":4.0}}}""") return s
val decoded = Json.decodeFromString(SegmentWrapper.serializer(), s)
if (segmentWrapper != decoded) return decoded.toString()
}
run {
val s = Json.encodeToString(I.serializer(), p1)
if (s != """{"type":"DPoint","x":1.0,"y":2.0}""") return s
val decoded = Json.decodeFromString(I.serializer(), s)
if (p1 != decoded) return decoded.toString()
}
run {
val s = Json.encodeToString(I.serializer(), dSegment)
if (s != """{"type":"DSegment","p1":{"x":1.0,"y":2.0},"p2":{"x":3.0,"y":4.0}}""") return s
val decoded = Json.decodeFromString(I.serializer(), s)
if (dSegment != decoded) return decoded.toString()
}
return "OK"
}
@@ -93,6 +93,12 @@ public class SerializationFirBlackBoxTestGenerated extends AbstractSerialization
runTest("plugins/kotlinx-serialization/testData/boxIr/metaSerializable.kt");
}
@Test
@TestMetadata("multiFieldValueClasses.kt")
public void testMultiFieldValueClasses() throws Exception {
runTest("plugins/kotlinx-serialization/testData/boxIr/multiFieldValueClasses.kt");
}
@Test
@TestMetadata("multimoduleInheritance.kt")
public void testMultimoduleInheritance() throws Exception {
@@ -91,6 +91,12 @@ public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxT
runTest("plugins/kotlinx-serialization/testData/boxIr/metaSerializable.kt");
}
@Test
@TestMetadata("multiFieldValueClasses.kt")
public void testMultiFieldValueClasses() throws Exception {
runTest("plugins/kotlinx-serialization/testData/boxIr/multiFieldValueClasses.kt");
}
@Test
@TestMetadata("multimoduleInheritance.kt")
public void testMultimoduleInheritance() throws Exception {