[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.declarations.isSingleFieldValueClass
|
||||||
import org.jetbrains.kotlin.ir.types.*
|
import org.jetbrains.kotlin.ir.types.*
|
||||||
import org.jetbrains.kotlin.ir.util.companionObject
|
import org.jetbrains.kotlin.ir.util.companionObject
|
||||||
|
import org.jetbrains.kotlin.ir.util.copyTo
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
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.extensions.SerializationPluginContext
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount
|
import org.jetbrains.kotlinx.serialization.compiler.resolve.bitMaskSlotCount
|
||||||
@@ -58,17 +60,17 @@ class IrPreGenerator(
|
|||||||
modality = Modality.FINAL
|
modality = Modality.FINAL
|
||||||
origin = SERIALIZATION_PLUGIN_ORIGIN
|
origin = SERIALIZATION_PLUGIN_ORIGIN
|
||||||
}
|
}
|
||||||
method.apply {
|
method.excludeFromJsExport()
|
||||||
dispatchReceiverParameter = null // function is static
|
|
||||||
excludeFromJsExport()
|
|
||||||
}
|
|
||||||
|
|
||||||
val typeParams = irClass.typeParameters.map {
|
val typeParamsMap = irClass.typeParameters.associateWith {
|
||||||
method.addTypeParameter(
|
method.addTypeParameter(
|
||||||
it.name.asString(), compilerContext.irBuiltIns.anyNType
|
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
|
// object
|
||||||
method.addValueParameter(
|
method.addValueParameter(
|
||||||
@@ -96,7 +98,14 @@ class IrPreGenerator(
|
|||||||
SERIALIZATION_PLUGIN_ORIGIN
|
SERIALIZATION_PLUGIN_ORIGIN
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
compilerContext.referenceClass(JVM_STATIC_ANNOTATION_CLASS_ID)
|
||||||
|
?.let { method.annotations += method.createAnnotationCallWithoutArgs(it) }
|
||||||
|
|
||||||
compilerContext.metadataDeclarationRegistrar.registerFunctionAsMetadataVisible(method)
|
compilerContext.metadataDeclarationRegistrar.registerFunctionAsMetadataVisible(method)
|
||||||
|
|
||||||
|
// Make function static in bytecode (see JvmStaticAnnotationLowering.makeStatic).
|
||||||
|
method.dispatchReceiverParameter = null
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun preGenerateDeserializationConstructorIfNeeded() {
|
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")
|
@TestMetadata("plugins/kotlinx-serialization/testData/boxIr")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
public class BoxIr {
|
public class BoxIr {
|
||||||
|
@Test
|
||||||
|
@TestMetadata("allConstructorsAccessible.kt")
|
||||||
|
public void testAllConstructorsAccessible() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/allConstructorsAccessible.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllFilesPresentInBoxIr() throws Exception {
|
public void testAllFilesPresentInBoxIr() throws Exception {
|
||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
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")
|
@TestMetadata("plugins/kotlinx-serialization/testData/boxIr")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxTest {
|
public class SerializationIrBoxTestGenerated extends AbstractSerializationIrBoxTest {
|
||||||
|
@Test
|
||||||
|
@TestMetadata("allConstructorsAccessible.kt")
|
||||||
|
public void testAllConstructorsAccessible() throws Exception {
|
||||||
|
runTest("plugins/kotlinx-serialization/testData/boxIr/allConstructorsAccessible.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testAllFilesPresentInBoxIr() throws Exception {
|
public void testAllFilesPresentInBoxIr() throws Exception {
|
||||||
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/kotlinx-serialization/testData/boxIr"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
|
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