[Serialization] Workaround registering static writeSelf method in metadata
`IrGeneratedDeclarationsRegistrar` assumes that all generated functions are correct from a Kotlin point of view. But `writeSelf` method on JVM is a static method outside any object/companion object So to properly calculate containing class for this method we should generate a dispatch receiver parameter, register the method in metadata, and then remove the parameter (to make function static)
This commit is contained in:
+15
-6
@@ -15,8 +15,10 @@ 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.ir.util.copyTo
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.annotations.JVM_STATIC_ANNOTATION_CLASS_ID
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount
|
||||
@@ -58,17 +60,17 @@ class IrPreGenerator(
|
||||
modality = Modality.FINAL
|
||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||
}
|
||||
method.apply {
|
||||
dispatchReceiverParameter = null // function is static
|
||||
excludeFromJsExport()
|
||||
}
|
||||
method.excludeFromJsExport()
|
||||
|
||||
val typeParams = irClass.typeParameters.map {
|
||||
val typeParamsMap = irClass.typeParameters.associateWith {
|
||||
method.addTypeParameter(
|
||||
it.name.asString(), compilerContext.irBuiltIns.anyNType
|
||||
)
|
||||
}
|
||||
val typeParamsAsArguments = typeParams.map { it.defaultType }
|
||||
// Despite this function should be static in bytecode, registerFunctionAsMetadataVisible needs a correct dispatch receiver
|
||||
// to handle function metadata correctly. This dispatchReceiverParameter will become `null` later.
|
||||
method.dispatchReceiverParameter = irClass.thisReceiver?.copyTo(method, remapTypeMap = typeParamsMap)
|
||||
val typeParamsAsArguments = typeParamsMap.values.map { it.defaultType }
|
||||
|
||||
// object
|
||||
method.addValueParameter(
|
||||
@@ -96,7 +98,14 @@ class IrPreGenerator(
|
||||
SERIALIZATION_PLUGIN_ORIGIN
|
||||
)
|
||||
}
|
||||
|
||||
compilerContext.referenceClass(JVM_STATIC_ANNOTATION_CLASS_ID)
|
||||
?.let { method.annotations += method.createAnnotationCallWithoutArgs(it) }
|
||||
|
||||
compilerContext.metadataDeclarationRegistrar.registerFunctionAsMetadataVisible(method)
|
||||
|
||||
// Make function static in bytecode (see JvmStaticAnnotationLowering.makeStatic).
|
||||
method.dispatchReceiverParameter = null
|
||||
}
|
||||
|
||||
private fun preGenerateDeserializationConstructorIfNeeded() {
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// TARGET_BACKEND: JVM_IR
|
||||
// WITH_REFLECT
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.reflect.jvm.javaConstructor
|
||||
import kotlin.reflect.jvm.kotlinFunction
|
||||
|
||||
@Serializable
|
||||
data class Tuple2<out A1, out A2>(
|
||||
val _1: A1,
|
||||
val _2: A2,
|
||||
)
|
||||
|
||||
fun box(): String {
|
||||
val cls = Tuple2::class
|
||||
val ctor = cls.constructors.single { it.parameters.size == 4 }
|
||||
val kf = ctor.javaConstructor?.kotlinFunction
|
||||
return if (kf.toString() == "fun `<init>`(kotlin.Int, A1?, A2?, kotlinx.serialization.internal.SerializationConstructorMarker?): Tuple2<A1, A2>") "OK" else "Fail: $kf"
|
||||
}
|
||||
+6
@@ -22,6 +22,12 @@ public class SerializationFirLightTreeBlackBoxTestGenerated extends AbstractSeri
|
||||
@TestMetadata("plugins/kotlinx-serialization/testData/boxIr")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class BoxIr {
|
||||
@Test
|
||||
@TestMetadata("allConstructorsAccessible.kt")
|
||||
public void testAllConstructorsAccessible() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/allConstructorsAccessible.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllFilesPresentInBoxIr() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
|
||||
+6
@@ -20,6 +20,12 @@ import java.util.regex.Pattern;
|
||||
@TestMetadata("plugins/kotlinx-serialization/testData/boxIr")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxTest {
|
||||
@Test
|
||||
@TestMetadata("allConstructorsAccessible.kt")
|
||||
public void testAllConstructorsAccessible() throws Exception {
|
||||
runTest("plugins/kotlinx-serialization/testData/boxIr/allConstructorsAccessible.kt");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAllFilesPresentInBoxIr() throws Exception {
|
||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
||||
|
||||
Reference in New Issue
Block a user