[Serialization] Implement serialization checker for K2 version of plugin

^KT-53178 Fixed
This commit is contained in:
Dmitriy Novozhilov
2022-08-24 10:59:11 +03:00
committed by teamcity
parent e048ffcf6d
commit 671083c701
69 changed files with 1767 additions and 453 deletions
@@ -8,6 +8,7 @@ plugins {
dependencies {
compileOnly(project(":compiler:util"))
compileOnly(project(":core:compiler.common"))
compileOnly(project(":core:deserialization.common.jvm"))
compileOnly(intellijCore())
}
@@ -0,0 +1,56 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization.compiler.diagnostic
import com.intellij.openapi.util.io.JarUtil
import org.jetbrains.kotlin.config.ApiVersion
import org.jetbrains.kotlin.config.KotlinCompilerVersion
import org.jetbrains.kotlin.descriptors.SourceElement
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
import java.io.File
import java.util.jar.Attributes
data class RuntimeVersions(val implementationVersion: ApiVersion?, val requireKotlinVersion: ApiVersion?) {
companion object {
val MINIMAL_SUPPORTED_VERSION = ApiVersion.parse("1.0-M1-SNAPSHOT")!!
val MINIMAL_VERSION_FOR_INLINE_CLASSES = ApiVersion.parse("1.1-M1-SNAPSHOT")!!
}
fun currentCompilerMatchRequired(): Boolean {
val current = requireNotNull(KotlinCompilerVersion.getVersion()?.let(ApiVersion.Companion::parse))
return requireKotlinVersion == null || requireKotlinVersion <= current
}
fun implementationVersionMatchSupported(): Boolean {
return implementationVersion != null && implementationVersion >= MINIMAL_SUPPORTED_VERSION
}
}
object CommonVersionReader {
private val REQUIRE_KOTLIN_VERSION = Attributes.Name("Require-Kotlin-Version")
private const val CLASS_SUFFIX = "!/kotlinx/serialization/KSerializer.class"
fun computeRuntimeVersions(sourceElement: SourceElement?): RuntimeVersions? {
val location = (sourceElement as? KotlinJvmBinarySourceElement)?.binaryClass?.location ?: return null
val jarFile = location.removeSuffix(CLASS_SUFFIX)
if (!jarFile.endsWith(".jar")) return null
val file = File(jarFile)
if (!file.exists()) return null
return getVersionsFromManifest(file)
}
fun getVersionsFromManifest(runtimeLibraryPath: File): RuntimeVersions {
val version = JarUtil.getJarAttribute(runtimeLibraryPath, Attributes.Name.IMPLEMENTATION_VERSION)?.let(ApiVersion.Companion::parse)
val kotlinVersion = JarUtil.getJarAttribute(runtimeLibraryPath, REQUIRE_KOTLIN_VERSION)?.let(ApiVersion.Companion::parse)
return RuntimeVersions(version, kotlinVersion)
}
fun canSupportInlineClasses(currentVersion: RuntimeVersions?): Boolean {
if (currentVersion == null) return true
val implVersion = currentVersion.implementationVersion ?: return false
return implVersion >= RuntimeVersions.MINIMAL_VERSION_FOR_INLINE_CLASSES
}
}
@@ -0,0 +1,44 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization.compiler.resolve
interface ISerializableProperties<S : ISerializableProperty> {
val serializableProperties: List<S>
val isExternallySerializable: Boolean
val serializableConstructorProperties: List<S>
val serializableStandaloneProperties: List<S>
}
val ISerializableProperties<*>.goldenMask: Int
get() {
var goldenMask = 0
var requiredBit = 1
for (property in serializableProperties) {
if (!property.optional) {
goldenMask = goldenMask or requiredBit
}
requiredBit = requiredBit shl 1
}
return goldenMask
}
val ISerializableProperties<*>.goldenMaskList: List<Int>
get() {
val maskSlotCount = serializableProperties.bitMaskSlotCount()
val goldenMaskList = MutableList(maskSlotCount) { 0 }
for (i in serializableProperties.indices) {
if (!serializableProperties[i].optional) {
val slotNumber = i / 32
val bitInSlot = i % 32
goldenMaskList[slotNumber] = goldenMaskList[slotNumber] or (1 shl bitInSlot)
}
}
return goldenMaskList
}
fun List<ISerializableProperty>.bitMaskSlotCount(): Int = size / 32 + 1
fun bitMaskSlotAt(propertyIndex: Int): Int = propertyIndex / 32
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlinx.serialization.compiler.resolve
import org.jetbrains.kotlin.name.Name
interface ISerializableProperty {
val isConstructorParameterWithDefault: Boolean
val name: String
val originalDescriptorName: Name
val optional: Boolean
val transient: Boolean
}
@@ -41,6 +41,22 @@ object SerializationAnnotations {
val contextualOnPropertyFqName = FqName("kotlinx.serialization.Contextual")
val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
val serializableAnnotationClassId = ClassId.topLevel(serializableAnnotationFqName)
val serializerAnnotationClassId = ClassId.topLevel(serializerAnnotationFqName)
val serialNameAnnotationClassId = ClassId.topLevel(serialNameAnnotationFqName)
val requiredAnnotationClassId = ClassId.topLevel(requiredAnnotationFqName)
val serialTransientClassId = ClassId.topLevel(serialTransientFqName)
val serialInfoClassId = ClassId.topLevel(serialInfoFqName)
val inheritableSerialInfoClassId = ClassId.topLevel(inheritableSerialInfoFqName)
val metaSerializableAnnotationClassId = ClassId.topLevel(metaSerializableAnnotationFqName)
val encodeDefaultClassId = ClassId.topLevel(encodeDefaultFqName)
val contextualClassId = ClassId.topLevel(contextualFqName)
val contextualOnFileClassId = ClassId.topLevel(contextualOnFileFqName)
val contextualOnPropertyClassId = ClassId.topLevel(contextualOnPropertyFqName)
val polymorphicClassId = ClassId.topLevel(polymorphicFqName)
val additionalSerializersClassId = ClassId.topLevel(additionalSerializersFqName)
}
object SerialEntityNames {
@@ -55,9 +71,12 @@ object SerialEntityNames {
// classes
val KCLASS_NAME_FQ = FqName("kotlin.reflect.KClass")
val KCLASS_NAME_CLASS_ID = ClassId.topLevel(KCLASS_NAME_FQ)
val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS)
val SERIAL_CTOR_MARKER_NAME = Name.identifier("SerializationConstructorMarker")
val KSERIALIZER_NAME_FQ = SerializationPackages.packageFqName.child(KSERIALIZER_NAME)
val KSERIALIZER_CLASS_ID = ClassId.topLevel(KSERIALIZER_NAME_FQ)
val SERIALIZER_CLASS_NAME = Name.identifier(SERIALIZER_CLASS)
val IMPL_NAME = Name.identifier("Impl")
@@ -123,8 +142,30 @@ object SpecialBuiltins {
const val sealedSerializer = "SealedClassSerializer"
const val contextSerializer = "ContextualSerializer"
const val nullableSerializer = "NullableSerializer"
object Names {
val referenceArraySerializer = Name.identifier(SpecialBuiltins.referenceArraySerializer)
val objectSerializer = Name.identifier(SpecialBuiltins.objectSerializer)
val enumSerializer = Name.identifier(SpecialBuiltins.enumSerializer)
val polymorphicSerializer = Name.identifier(SpecialBuiltins.polymorphicSerializer)
val sealedSerializer = Name.identifier(SpecialBuiltins.sealedSerializer)
val contextSerializer = Name.identifier(SpecialBuiltins.contextSerializer)
val nullableSerializer = Name.identifier(SpecialBuiltins.nullableSerializer)
}
}
object PrimitiveBuiltins {
const val booleanSerializer = "BooleanSerializer"
const val byteSerializer = "ByteSerializer"
const val shortSerializer = "ShortSerializer"
const val intSerializer = "IntSerializer"
const val longSerializer = "LongSerializer"
const val floatSerializer = "FloatSerializer"
const val doubleSerializer = "DoubleSerializer"
const val charSerializer = "CharSerializer"
}
object CallingConventions {
const val begin = "beginStructure"
const val end = "endStructure"
@@ -153,3 +194,71 @@ object SerializationDependencies {
val FUNCTION0_FQ = FqName("kotlin.Function0")
val LAZY_PUBLICATION_MODE_NAME = Name.identifier("PUBLICATION")
}
object SerializersClassIds {
val enumSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.enumSerializer))
val polymorphicSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.polymorphicSerializer))
val referenceArraySerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.referenceArraySerializer))
val objectSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.objectSerializer))
val sealedSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer))
val contextSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
}
fun findStandardKotlinTypeSerializerName(typeName: String?): String? {
return when (typeName) {
null -> null
"kotlin.Unit" -> "UnitSerializer"
"kotlin.Nothing" -> "NothingSerializer"
"kotlin.Boolean" -> "BooleanSerializer"
"kotlin.Byte" -> "ByteSerializer"
"kotlin.Short" -> "ShortSerializer"
"kotlin.Int" -> "IntSerializer"
"kotlin.Long" -> "LongSerializer"
"kotlin.Float" -> "FloatSerializer"
"kotlin.Double" -> "DoubleSerializer"
"kotlin.Char" -> "CharSerializer"
"kotlin.UInt" -> "UIntSerializer"
"kotlin.ULong" -> "ULongSerializer"
"kotlin.UByte" -> "UByteSerializer"
"kotlin.UShort" -> "UShortSerializer"
"kotlin.String" -> "StringSerializer"
"kotlin.Pair" -> "PairSerializer"
"kotlin.Triple" -> "TripleSerializer"
"kotlin.collections.Collection", "kotlin.collections.List",
"kotlin.collections.ArrayList", "kotlin.collections.MutableList" -> "ArrayListSerializer"
"kotlin.collections.Set", "kotlin.collections.LinkedHashSet", "kotlin.collections.MutableSet" -> "LinkedHashSetSerializer"
"kotlin.collections.HashSet" -> "HashSetSerializer"
"kotlin.collections.Map", "kotlin.collections.LinkedHashMap", "kotlin.collections.MutableMap" -> "LinkedHashMapSerializer"
"kotlin.collections.HashMap" -> "HashMapSerializer"
"kotlin.collections.Map.Entry" -> "MapEntrySerializer"
"kotlin.ByteArray" -> "ByteArraySerializer"
"kotlin.ShortArray" -> "ShortArraySerializer"
"kotlin.IntArray" -> "IntArraySerializer"
"kotlin.LongArray" -> "LongArraySerializer"
"kotlin.UByteArray" -> "UByteArraySerializer"
"kotlin.UShortArray" -> "UShortArraySerializer"
"kotlin.UIntArray" -> "UIntArraySerializer"
"kotlin.ULongArray" -> "ULongArraySerializer"
"kotlin.CharArray" -> "CharArraySerializer"
"kotlin.FloatArray" -> "FloatArraySerializer"
"kotlin.DoubleArray" -> "DoubleArraySerializer"
"kotlin.BooleanArray" -> "BooleanArraySerializer"
"kotlin.time.Duration" -> "DurationSerializer"
"java.lang.Boolean" -> "BooleanSerializer"
"java.lang.Byte" -> "ByteSerializer"
"java.lang.Short" -> "ShortSerializer"
"java.lang.Integer" -> "IntSerializer"
"java.lang.Long" -> "LongSerializer"
"java.lang.Float" -> "FloatSerializer"
"java.lang.Double" -> "DoubleSerializer"
"java.lang.Character" -> "CharSerializer"
"java.lang.String" -> "StringSerializer"
"java.util.Collection", "java.util.List", "java.util.ArrayList" -> "ArrayListSerializer"
"java.util.Set", "java.util.LinkedHashSet" -> "LinkedHashSetSerializer"
"java.util.HashSet" -> "HashSetSerializer"
"java.util.Map", "java.util.LinkedHashMap" -> "LinkedHashMapSerializer"
"java.util.HashMap" -> "HashMapSerializer"
"java.util.Map.Entry" -> "MapEntrySerializer"
else -> return null
}
}