[Serialization] Reorganize module structure
This commit is contained in:
@@ -0,0 +1,28 @@
|
||||
description = "Kotlin Serialization Compiler Plugin (K1)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compileOnly(project(":core:compiler.common.jvm"))
|
||||
compileOnly(project(":compiler:frontend"))
|
||||
compileOnly(project(":js:js.frontend"))
|
||||
compileOnly(project(":compiler:cli-common"))
|
||||
compileOnly(project(":compiler:ir.backend.common")) // needed for CompilationException
|
||||
compileOnly(project(":core:deserialization.common.jvm")) // needed for CompilationException
|
||||
|
||||
implementation(project(":kotlinx-serialization-compiler-plugin.common"))
|
||||
|
||||
compileOnly(intellijCore())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
sourcesJar()
|
||||
javadocJar()
|
||||
@@ -0,0 +1,11 @@
|
||||
package org.jetbrains.kotlinx.serialization.compiler.extensions;
|
||||
|
||||
import "core/metadata/src/metadata.proto";
|
||||
import "core/metadata/src/ext_options.proto";
|
||||
|
||||
option java_outer_classname = "SerializationPluginMetadataExtensions";
|
||||
option optimize_for = LITE_RUNTIME;
|
||||
|
||||
extend org.jetbrains.kotlin.metadata.Class {
|
||||
repeated int32 properties_names_in_program_order = 18000;
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlin.js.translate.utils.AnnotationsUtils
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.firstArgument
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isKSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.toClassDescriptor
|
||||
|
||||
abstract class AbstractSerialGenerator(val bindingContext: BindingContext?, val currentDeclaration: ClassDescriptor) {
|
||||
private fun getKClassListFromFileAnnotation(annotationFqName: FqName, declarationInFile: DeclarationDescriptor): List<KotlinType> {
|
||||
if (bindingContext == null) return emptyList()
|
||||
val annotation = AnnotationsUtils
|
||||
.getContainingFileAnnotations(bindingContext, declarationInFile)
|
||||
.find { it.fqName == annotationFqName }
|
||||
?: return emptyList()
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
val typeList: List<KClassValue> = annotation.firstArgument()?.value as? List<KClassValue> ?: return emptyList()
|
||||
return typeList.map { it.getArgumentType(declarationInFile.module) }
|
||||
}
|
||||
|
||||
val contextualKClassListInCurrentFile: Set<KotlinType> by lazy {
|
||||
getKClassListFromFileAnnotation(
|
||||
SerializationAnnotations.contextualFqName,
|
||||
currentDeclaration
|
||||
).plus(
|
||||
getKClassListFromFileAnnotation(
|
||||
SerializationAnnotations.contextualOnFileFqName,
|
||||
currentDeclaration
|
||||
)
|
||||
).toSet()
|
||||
}
|
||||
|
||||
val additionalSerializersInScopeOfCurrentFile: Map<Pair<ClassDescriptor, Boolean>, ClassDescriptor> by lazy {
|
||||
getKClassListFromFileAnnotation(SerializationAnnotations.additionalSerializersFqName, currentDeclaration)
|
||||
.associateBy(
|
||||
{
|
||||
val kotlinType = it.supertypes().find(::isKSerializer)?.arguments?.firstOrNull()?.type
|
||||
val descriptor = kotlinType.toClassDescriptor
|
||||
?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type")
|
||||
descriptor to kotlinType!!.isMarkedNullable
|
||||
},
|
||||
{ it.toClassDescriptor!! }
|
||||
)
|
||||
}
|
||||
|
||||
protected fun ClassDescriptor.getFuncDesc(funcName: String): Sequence<FunctionDescriptor> =
|
||||
unsubstitutedMemberScope.getDescriptorsFiltered { it == Name.identifier(funcName) }.asSequence()
|
||||
.filterIsInstance<FunctionDescriptor>()
|
||||
}
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* 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.backend.common
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CodegenUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
object SerializationDescriptorUtils {
|
||||
fun getSyntheticLoadMember(serializerDescriptor: ClassDescriptor): FunctionDescriptor? = CodegenUtil.getMemberToGenerate(
|
||||
serializerDescriptor, SerialEntityNames.LOAD,
|
||||
serializerDescriptor::checkLoadMethodResult, serializerDescriptor::checkLoadMethodParameters
|
||||
)
|
||||
|
||||
fun getSyntheticSaveMember(serializerDescriptor: ClassDescriptor): FunctionDescriptor? = CodegenUtil.getMemberToGenerate(
|
||||
serializerDescriptor, SerialEntityNames.SAVE,
|
||||
serializerDescriptor::checkSaveMethodResult, serializerDescriptor::checkSaveMethodParameters
|
||||
)
|
||||
}
|
||||
+267
@@ -0,0 +1,267 @@
|
||||
/*
|
||||
* 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.backend.common
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.backend.common.CompilationException
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.js.descriptorUtils.getJetTypeFqName
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtAnonymousInitializer
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.psi.KtProperty
|
||||
import org.jetbrains.kotlin.psi.KtPureClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.enumSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.referenceArraySerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName
|
||||
|
||||
open class SerialTypeInfo(
|
||||
val property: SerializableProperty,
|
||||
val elementMethodPrefix: String,
|
||||
val serializer: ClassDescriptor? = null
|
||||
)
|
||||
|
||||
fun AbstractSerialGenerator.findAddOnSerializer(propertyType: KotlinType, module: ModuleDescriptor): ClassDescriptor? {
|
||||
additionalSerializersInScopeOfCurrentFile[propertyType.toClassDescriptor to propertyType.isMarkedNullable]?.let { return it }
|
||||
if (propertyType in contextualKClassListInCurrentFile)
|
||||
return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
|
||||
if (propertyType.toClassDescriptor?.annotations?.hasAnnotation(SerializationAnnotations.polymorphicFqName) == true)
|
||||
return module.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer)
|
||||
if (propertyType.isMarkedNullable) return findAddOnSerializer(propertyType.makeNotNullable(), module)
|
||||
return null
|
||||
}
|
||||
|
||||
fun KotlinType.isGeneratedSerializableObject() =
|
||||
toClassDescriptor?.run { kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs } == true
|
||||
|
||||
@Suppress("FunctionName", "LocalVariableName")
|
||||
fun AbstractSerialGenerator.getSerialTypeInfo(property: SerializableProperty): SerialTypeInfo {
|
||||
fun SerializableInfo(serializer: ClassDescriptor?) =
|
||||
SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", serializer)
|
||||
|
||||
val T = property.type
|
||||
property.serializableWith?.toClassDescriptor?.let { return SerializableInfo(it) }
|
||||
findAddOnSerializer(T, property.module)?.let { return SerializableInfo(it) }
|
||||
T.overridenSerializer?.toClassDescriptor?.let { return SerializableInfo(it) }
|
||||
return when {
|
||||
T.isTypeParameter() -> SerialTypeInfo(property, if (property.type.isMarkedNullable) "Nullable" else "", null)
|
||||
T.isPrimitiveNumberType() or T.isBoolean() -> SerialTypeInfo(
|
||||
property,
|
||||
T.getJetTypeFqName(false).removePrefix("kotlin.") // i don't feel so good about it...
|
||||
// alternative: KotlinBuiltIns.getPrimitiveType(T)!!.typeName.identifier
|
||||
)
|
||||
KotlinBuiltIns.isString(T) -> SerialTypeInfo(property, "String")
|
||||
KotlinBuiltIns.isNonPrimitiveArray(T.toClassDescriptor!!) -> {
|
||||
val serializer = property.serializableWith?.toClassDescriptor ?: property.module.findClassAcrossModuleDependencies(
|
||||
referenceArraySerializerId
|
||||
)
|
||||
SerializableInfo(serializer)
|
||||
}
|
||||
else -> {
|
||||
val serializer =
|
||||
findTypeSerializerOrContext(property.module, property.type, property.descriptor.findPsi())
|
||||
SerializableInfo(serializer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun AbstractSerialGenerator.allSealedSerializableSubclassesFor(
|
||||
klass: ClassDescriptor,
|
||||
module: ModuleDescriptor
|
||||
): Pair<List<KotlinType>, List<ClassDescriptor>> {
|
||||
assert(klass.modality == Modality.SEALED)
|
||||
fun recursiveSealed(klass: ClassDescriptor): Collection<ClassDescriptor> {
|
||||
return klass.sealedSubclasses.flatMap { if (it.modality == Modality.SEALED) recursiveSealed(it) else setOf(it) }
|
||||
}
|
||||
|
||||
val serializableSubtypes = recursiveSealed(klass).map { it.toSimpleType() }
|
||||
return serializableSubtypes.mapNotNull { subtype ->
|
||||
findTypeSerializerOrContextUnchecked(module, subtype)?.let { Pair(subtype, it) }
|
||||
}.unzip()
|
||||
}
|
||||
|
||||
fun KotlinType.serialName(): String {
|
||||
val serializableDescriptor = this.toClassDescriptor!!
|
||||
return serializableDescriptor.serialName()
|
||||
}
|
||||
|
||||
fun ClassDescriptor.serialName(): String {
|
||||
return annotations.serialNameValue ?: fqNameUnsafe.asString()
|
||||
}
|
||||
|
||||
val ClassDescriptor.isStaticSerializable: Boolean get() = this.declaredTypeParameters.isEmpty()
|
||||
|
||||
/**
|
||||
* Returns class descriptor for ContextSerializer or PolymorphicSerializer
|
||||
* if [annotations] contains @Contextual or @Polymorphic annotation
|
||||
*/
|
||||
fun analyzeSpecialSerializers(
|
||||
moduleDescriptor: ModuleDescriptor,
|
||||
annotations: Annotations
|
||||
): ClassDescriptor? = when {
|
||||
annotations.hasAnnotation(SerializationAnnotations.contextualFqName) || annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyFqName) ->
|
||||
moduleDescriptor.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
|
||||
// can be annotation on type usage, e.g. List<@Polymorphic Any>
|
||||
annotations.hasAnnotation(SerializationAnnotations.polymorphicFqName) ->
|
||||
moduleDescriptor.getClassFromSerializationPackage(SpecialBuiltins.polymorphicSerializer)
|
||||
else -> null
|
||||
}
|
||||
|
||||
fun AbstractSerialGenerator.findTypeSerializerOrContextUnchecked(
|
||||
module: ModuleDescriptor,
|
||||
kType: KotlinType
|
||||
): ClassDescriptor? {
|
||||
val annotations = kType.annotations
|
||||
if (kType.isTypeParameter()) return null
|
||||
annotations.serializableWith(module)?.let { return it.toClassDescriptor }
|
||||
additionalSerializersInScopeOfCurrentFile[kType.toClassDescriptor to kType.isMarkedNullable]?.let { return it }
|
||||
if (kType.isMarkedNullable) return findTypeSerializerOrContextUnchecked(module, kType.makeNotNullable())
|
||||
if (kType in contextualKClassListInCurrentFile) return module.getClassFromSerializationPackage(SpecialBuiltins.contextSerializer)
|
||||
return analyzeSpecialSerializers(module, annotations) ?: findTypeSerializer(module, kType)
|
||||
}
|
||||
|
||||
fun AbstractSerialGenerator.findTypeSerializerOrContext(
|
||||
module: ModuleDescriptor,
|
||||
kType: KotlinType,
|
||||
sourceElement: PsiElement? = null
|
||||
): ClassDescriptor? {
|
||||
if (kType.isTypeParameter()) return null
|
||||
return findTypeSerializerOrContextUnchecked(module, kType) ?: throw CompilationException(
|
||||
"Serializer for element of type $kType has not been found.\n" +
|
||||
"To use context serializer as fallback, explicitly annotate element with @Contextual",
|
||||
null,
|
||||
sourceElement
|
||||
)
|
||||
}
|
||||
|
||||
fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
|
||||
val userOverride = kType.overridenSerializer
|
||||
if (userOverride != null) return userOverride.toClassDescriptor
|
||||
if (kType.isTypeParameter()) return null
|
||||
if (KotlinBuiltIns.isArray(kType)) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.referenceArraySerializer)
|
||||
if (kType.isGeneratedSerializableObject()) return module.getClassFromInternalSerializationPackage(SpecialBuiltins.objectSerializer)
|
||||
val stdSer = findStandardKotlinTypeSerializer(module, kType) // see if there is a standard serializer
|
||||
?: findEnumTypeSerializer(module, kType)
|
||||
if (stdSer != null) return stdSer
|
||||
if (kType.isInterface() && kType.toClassDescriptor?.isSealedSerializableInterface == false) return module.getClassFromSerializationPackage(
|
||||
SpecialBuiltins.polymorphicSerializer
|
||||
)
|
||||
return kType.toClassDescriptor?.classSerializer // check for serializer defined on the type
|
||||
}
|
||||
|
||||
fun findStandardKotlinTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
|
||||
val typeName = kType.getJetTypeFqName(false)
|
||||
val name = when (typeName) {
|
||||
"Z" -> if (kType.isBoolean()) "BooleanSerializer" else null
|
||||
"B" -> if (kType.isByte()) "ByteSerializer" else null
|
||||
"S" -> if (kType.isShort()) "ShortSerializer" else null
|
||||
"I" -> if (kType.isInt()) "IntSerializer" else null
|
||||
"J" -> if (kType.isLong()) "LongSerializer" else null
|
||||
"F" -> if (kType.isFloat()) "FloatSerializer" else null
|
||||
"D" -> if (kType.isDouble()) "DoubleSerializer" else null
|
||||
"C" -> if (kType.isChar()) "CharSerializer" else null
|
||||
else -> findStandardKotlinTypeSerializer(typeName)
|
||||
} ?: return null
|
||||
val identifier = Name.identifier(name)
|
||||
return module.findClassAcrossModuleDependencies(ClassId(internalPackageFqName, identifier))
|
||||
?: module.findClassAcrossModuleDependencies(ClassId(SerializationPackages.packageFqName, identifier))
|
||||
}
|
||||
|
||||
fun findStandardKotlinTypeSerializer(typeName: String): String? {
|
||||
return when (typeName) {
|
||||
"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
|
||||
}
|
||||
}
|
||||
|
||||
fun findEnumTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescriptor? {
|
||||
val classDescriptor = kType.toClassDescriptor ?: return null
|
||||
return if (classDescriptor.kind == ClassKind.ENUM_CLASS && !classDescriptor.isEnumWithLegacyGeneratedSerializer())
|
||||
module.findClassAcrossModuleDependencies(enumSerializerId)
|
||||
else null
|
||||
}
|
||||
|
||||
fun KtPureClassOrObject.bodyPropertiesDescriptorsMap(
|
||||
bindingContext: BindingContext,
|
||||
filterUninitialized: Boolean = true
|
||||
): Map<PropertyDescriptor, KtProperty> = declarations
|
||||
.asSequence()
|
||||
.filterIsInstance<KtProperty>()
|
||||
// can filter here because it's impossible to create body property w/ backing field w/o explicit delegating or initializing
|
||||
.filter { if (filterUninitialized) it.delegateExpressionOrInitializer != null else true }
|
||||
.associateBy { (bindingContext[BindingContext.DECLARATION_TO_DESCRIPTOR, it] as? PropertyDescriptor)!! }
|
||||
|
||||
fun KtPureClassOrObject.primaryConstructorPropertiesDescriptorsMap(bindingContext: BindingContext): Map<PropertyDescriptor, KtParameter> =
|
||||
primaryConstructorParameters
|
||||
.asSequence()
|
||||
.filter { it.hasValOrVar() }
|
||||
.associateBy { bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, it]!! }
|
||||
|
||||
fun KtPureClassOrObject.anonymousInitializers() = declarations
|
||||
.asSequence()
|
||||
.filterIsInstance<KtAnonymousInitializer>()
|
||||
.mapNotNull { it.body }
|
||||
.toList()
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* 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.backend.jvm
|
||||
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
|
||||
|
||||
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))
|
||||
+56
@@ -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.psi.PsiElement;
|
||||
import org.jetbrains.kotlin.diagnostics.*;
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
|
||||
|
||||
public interface SerializationErrors {
|
||||
DiagnosticFactory2<PsiElement, String, String> INLINE_CLASSES_NOT_SUPPORTED = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PLUGIN_IS_NOT_ENABLED = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> ANONYMOUS_OBJECTS_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> INNER_CLASSES_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> EXPLICIT_SERIALIZABLE_IS_REQUIRED = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<KtAnnotationEntry> SERIALIZABLE_ANNOTATION_IGNORED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtAnnotationEntry> NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<KtAnnotationEntry> PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<KtAnnotationEntry, String> DUPLICATE_SERIAL_NAME = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, KotlinType, String, String> DUPLICATE_SERIAL_NAME_ENUM = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> SERIALIZER_NOT_FOUND = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> SERIALIZER_NULLABILITY_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory3<PsiElement, KotlinType, KotlinType, KotlinType> SERIALIZER_TYPE_INCOMPATIBLE = DiagnosticFactory3.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, KotlinType> LOCAL_SERIALIZER_USAGE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> TRANSIENT_MISSING_INITIALIZER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> TRANSIENT_IS_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> JSON_FORMAT_REDUNDANT_DEFAULT = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> JSON_FORMAT_REDUNDANT = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INCORRECT_TRANSIENT = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
DiagnosticFactory3<KtAnnotationEntry, String, String, String> REQUIRED_KOTLIN_TOO_HIGH = DiagnosticFactory3.create(ERROR);
|
||||
DiagnosticFactory3<KtAnnotationEntry, String, String, String> PROVIDED_RUNTIME_TOO_LOW = DiagnosticFactory3.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> INCONSISTENT_INHERITABLE_SERIALINFO = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_NOT_SERIALIZABLE = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> EXTERNAL_CLASS_IN_ANOTHER_MODULE = DiagnosticFactory2.create(ERROR);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
Object _initializer = new Object() {
|
||||
{
|
||||
Errors.Initializer
|
||||
.initializeFactoryNamesAndDefaultErrorMessages(SerializationErrors.class, SerializationPluginErrorsRendering.INSTANCE);
|
||||
}
|
||||
};
|
||||
}
|
||||
+480
@@ -0,0 +1,480 @@
|
||||
/*
|
||||
* 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.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.JvmNames.TRANSIENT_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.*
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
import org.jetbrains.kotlin.util.slicedMap.Slices
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.bodyPropertiesDescriptorsMap
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.primaryConstructorPropertiesDescriptorsMap
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
val SERIALIZABLE_PROPERTIES: WritableSlice<ClassDescriptor, SerializableProperties> = Slices.createSimpleSlice()
|
||||
|
||||
open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
private var useLegacyEnumSerializerCached: Boolean? = null
|
||||
|
||||
final override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
if (descriptor !is ClassDescriptor) return
|
||||
|
||||
checkEnum(descriptor, declaration, context.trace)
|
||||
checkExternalSerializer(descriptor, declaration, context.trace)
|
||||
|
||||
if (!canBeSerializedInternally(descriptor, declaration, context.trace)) return
|
||||
if (declaration !is KtPureClassOrObject) return
|
||||
if (!isIde) {
|
||||
// In IDE, BindingTrace is recreated each time code is modified, effectively resulting in JAR manifest read every time user types
|
||||
// something, which may be very slow. So we perform this check only during CLI/Gradle compilation.
|
||||
VersionReader.getVersionsForCurrentModuleFromTrace(descriptor.module, context.trace)?.let {
|
||||
checkMinKotlin(it, descriptor, context.trace)
|
||||
checkMinRuntime(it, descriptor, context.trace)
|
||||
}
|
||||
}
|
||||
val props = buildSerializableProperties(descriptor, context.trace) ?: return
|
||||
checkCorrectTransientAnnotationIsUsed(descriptor, props.serializableProperties, context.trace)
|
||||
checkTransients(declaration, context.trace)
|
||||
analyzePropertiesSerializers(context.trace, descriptor, props.serializableProperties)
|
||||
checkInheritedAnnotations(descriptor, declaration, context.trace)
|
||||
}
|
||||
|
||||
private fun checkExternalSerializer(classDescriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||
val serializableKType = classDescriptor.serializerForClass ?: return
|
||||
val serializableDescriptor = serializableKType.toClassDescriptor ?: return
|
||||
val props = SerializableProperties(serializableDescriptor, trace.bindingContext)
|
||||
|
||||
if (!props.isExternallySerializable) {
|
||||
val entry = classDescriptor.findAnnotationDeclaration(SerializationAnnotations.serializerAnnotationFqName)
|
||||
val inSameModule =
|
||||
trace.bindingContext[BindingContext.FQNAME_TO_CLASS_DESCRIPTOR, serializableDescriptor.fqNameUnsafe] != null
|
||||
val diagnostic = if (inSameModule) SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE else SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE
|
||||
|
||||
trace.report(diagnostic.on(entry ?: declaration, classDescriptor.defaultType, serializableKType))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkInheritedAnnotations(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||
val annotationsFilter: (Annotations) -> List<Pair<FqName, AnnotationDescriptor>> = { an ->
|
||||
an.map { it.annotationClass!!.fqNameSafe to it }
|
||||
.filter { it.second.annotationClass?.isInheritableSerialInfoAnnotation == true }
|
||||
}
|
||||
val annotationByFq: MutableMap<FqName, AnnotationDescriptor> = mutableMapOf()
|
||||
val reported: MutableSet<FqName> = mutableSetOf()
|
||||
// my annotations
|
||||
annotationByFq.putAll(annotationsFilter(descriptor.annotations))
|
||||
// inherited
|
||||
for (clazz in descriptor.getAllSuperClassifiers()) {
|
||||
val annotations = annotationsFilter(clazz.annotations)
|
||||
annotations.forEach { (fqname, call) ->
|
||||
if (fqname in annotationByFq) {
|
||||
val existing = annotationByFq.getValue(fqname)
|
||||
if (existing.allValueArguments != call.allValueArguments) {
|
||||
if (reported.add(fqname)) {
|
||||
val entry = (existing as? LazyAnnotationDescriptor)?.annotationEntry ?: declaration
|
||||
trace.report(
|
||||
SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO.on(
|
||||
entry,
|
||||
existing.type,
|
||||
clazz.defaultType
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMinRuntime(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
|
||||
// if RuntimeVersions are present, but implementation version is not,
|
||||
// it means that we are reading from jar which does not have this manifest parameter - a pre-1.0 serialization runtime.
|
||||
// For non-JAR distributions (klib, js) this method is not invoked, since getVersionsForCurrentModule
|
||||
// unable to read from them
|
||||
if (!versions.implementationVersionMatchSupported()) {
|
||||
descriptor.onSerializableOrMetaAnnotation {
|
||||
trace.report(
|
||||
SerializationErrors.PROVIDED_RUNTIME_TOO_LOW.on(
|
||||
it,
|
||||
versions.implementationVersion?.toString() ?: "too low",
|
||||
KotlinCompilerVersion.getVersion() ?: "unknown",
|
||||
VersionReader.MINIMAL_SUPPORTED_VERSION.toString(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMinKotlin(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
|
||||
if (versions.currentCompilerMatchRequired()) return
|
||||
descriptor.onSerializableOrMetaAnnotation {
|
||||
trace.report(
|
||||
SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH.on(
|
||||
it,
|
||||
KotlinCompilerVersion.getVersion() ?: "too low",
|
||||
versions.implementationVersion?.toString() ?: "unknown",
|
||||
versions.requireKotlinVersion?.toString() ?: "N/A",
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
protected open val isIde: Boolean get() = false
|
||||
|
||||
private fun checkCorrectTransientAnnotationIsUsed(
|
||||
descriptor: ClassDescriptor,
|
||||
properties: List<SerializableProperty>,
|
||||
trace: BindingTrace
|
||||
) {
|
||||
if (descriptor.getSuperInterfaces().any { it.fqNameSafe.asString() == "java.io.Serializable" }) return // do not check
|
||||
for (prop in properties) {
|
||||
if (prop.transient) continue // correct annotation is used
|
||||
val incorrectTransient = prop.descriptor.backingField?.annotations?.findAnnotation(TRANSIENT_ANNOTATION_FQ_NAME)
|
||||
if (incorrectTransient != null) {
|
||||
val elementToReport = incorrectTransient.source.getPsi() ?: prop.descriptor.findPsi() ?: continue
|
||||
trace.report(SerializationErrors.INCORRECT_TRANSIENT.on(elementToReport))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.useLegacyGeneratedEnumSerializer(): Boolean {
|
||||
return useLegacyEnumSerializerCached ?: useGeneratedEnumSerializer.also { useLegacyEnumSerializerCached = it }
|
||||
}
|
||||
|
||||
private fun canBeSerializedInternally(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace): Boolean {
|
||||
// if enum has meta or SerialInfo annotation on a class or entries and used plugin-generated serializer
|
||||
if (descriptor.useLegacyGeneratedEnumSerializer() && descriptor.isSerializableEnumWithMissingSerializer()) {
|
||||
val declarationToReport = declaration.modifierList ?: declaration
|
||||
trace.report(SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED.on(declarationToReport))
|
||||
return false
|
||||
}
|
||||
|
||||
if (!descriptor.hasSerializableOrMetaAnnotation) return false
|
||||
|
||||
if (!serializationPluginEnabledOn(descriptor)) {
|
||||
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.PLUGIN_IS_NOT_ENABLED)
|
||||
return false
|
||||
}
|
||||
|
||||
if (descriptor.isAnonymousObjectOrContained) {
|
||||
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
|
||||
return false
|
||||
}
|
||||
|
||||
if (descriptor.isInner) {
|
||||
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.INNER_CLASSES_NOT_SUPPORTED)
|
||||
return false
|
||||
}
|
||||
|
||||
if (descriptor.isInlineClass() && !canSupportInlineClasses(descriptor.module, trace)) {
|
||||
descriptor.onSerializableOrMetaAnnotation {
|
||||
trace.report(
|
||||
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
|
||||
it,
|
||||
VersionReader.minVersionForInlineClasses.toString(),
|
||||
VersionReader.getVersionsForCurrentModuleFromTrace(descriptor.module, trace)?.implementationVersion.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
if (!descriptor.hasSerializableOrMetaAnnotationWithoutArgs) {
|
||||
// defined custom serializer
|
||||
checkClassWithCustomSerializer(descriptor, declaration, trace)
|
||||
return false
|
||||
}
|
||||
|
||||
if (descriptor.serializableAnnotationIsUseless) {
|
||||
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
|
||||
return false
|
||||
}
|
||||
|
||||
// check that we can instantiate supertype
|
||||
if (descriptor.kind != ClassKind.ENUM_CLASS) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
|
||||
val superClass = descriptor.getSuperClassOrAny()
|
||||
if (!superClass.isInternalSerializable && superClass.constructors.singleOrNull { it.valueParameters.size == 0 } == null) {
|
||||
trace.reportOnSerializableOrMetaAnnotation(descriptor, SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun checkClassWithCustomSerializer(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||
val annotationPsi = descriptor.findSerializableOrMetaAnnotationDeclaration()
|
||||
checkCustomSerializerMatch(descriptor.module, descriptor.defaultType, descriptor, annotationPsi, trace, declaration)
|
||||
checkCustomSerializerIsNotLocal(descriptor.module, descriptor, trace, declaration)
|
||||
}
|
||||
|
||||
private val ClassDescriptor.isAnonymousObjectOrContained: Boolean
|
||||
get() {
|
||||
var current: DeclarationDescriptor? = this
|
||||
while (current != null) {
|
||||
if (DescriptorUtils.isAnonymousObject(current)) {
|
||||
return true
|
||||
}
|
||||
current = current.containingDeclaration
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun checkEnum(descriptor: ClassDescriptor, declaration: KtDeclaration, trace: BindingTrace) {
|
||||
if (descriptor.kind != ClassKind.ENUM_CLASS) return
|
||||
|
||||
val entryBySerialName = mutableMapOf<String, ClassDescriptor?>()
|
||||
descriptor.enumEntries().forEach { entryDescriptor ->
|
||||
val serialNameAnnotation = entryDescriptor.annotations.serialNameAnnotation
|
||||
val serialName = entryDescriptor.annotations.serialNameValue ?: entryDescriptor.name.asString()
|
||||
val firstEntry = entryBySerialName[serialName]
|
||||
if (firstEntry != null) {
|
||||
trace.report(
|
||||
SerializationErrors.DUPLICATE_SERIAL_NAME_ENUM.on(
|
||||
serialNameAnnotation?.findAnnotationEntry() ?: firstEntry.annotations.serialNameAnnotation?.findAnnotationEntry()
|
||||
?: declaration,
|
||||
descriptor.defaultType,
|
||||
serialName,
|
||||
entryDescriptor.name.asString()
|
||||
)
|
||||
)
|
||||
} else {
|
||||
entryBySerialName[serialName] = entryDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
private fun ClassDescriptor.isSerializableEnumWithMissingSerializer(): Boolean {
|
||||
if (kind != ClassKind.ENUM_CLASS) return false
|
||||
if (hasSerializableOrMetaAnnotation) return false
|
||||
if (annotations.hasAnySerialAnnotation) return true
|
||||
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
|
||||
}
|
||||
|
||||
open fun serializationPluginEnabledOn(descriptor: ClassDescriptor): Boolean {
|
||||
// In the CLI/Gradle compiler, this diagnostic is located in the plugin itself.
|
||||
// Therefore, if we are here, plugin is in the compile classpath and enabled.
|
||||
// For the IDE case, see SerializationPluginIDEDeclarationChecker
|
||||
return true
|
||||
}
|
||||
|
||||
private fun buildSerializableProperties(descriptor: ClassDescriptor, trace: BindingTrace): SerializableProperties? {
|
||||
if (!descriptor.hasSerializableOrMetaAnnotation) return null
|
||||
if (!descriptor.isInternalSerializable) return null
|
||||
if (descriptor.hasCompanionObjectAsSerializer) return null // customized by user
|
||||
|
||||
val props = SerializableProperties(descriptor, trace.bindingContext)
|
||||
if (!props.isExternallySerializable) trace.reportOnSerializableOrMetaAnnotation(
|
||||
descriptor,
|
||||
SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY
|
||||
)
|
||||
|
||||
// check that all names are unique
|
||||
val namesSet = mutableSetOf<String>()
|
||||
props.serializableProperties.forEach {
|
||||
if (!namesSet.add(it.name)) {
|
||||
descriptor.onSerializableOrMetaAnnotation { a ->
|
||||
trace.report(SerializationErrors.DUPLICATE_SERIAL_NAME.on(a, it.name))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trace.record(SERIALIZABLE_PROPERTIES, descriptor, props)
|
||||
return props
|
||||
}
|
||||
|
||||
private fun checkTransients(declaration: KtPureClassOrObject, trace: BindingTrace) {
|
||||
val propertiesMap: Map<PropertyDescriptor, KtDeclaration> =
|
||||
declaration.bodyPropertiesDescriptorsMap(
|
||||
trace.bindingContext,
|
||||
filterUninitialized = false
|
||||
) + declaration.primaryConstructorPropertiesDescriptorsMap(trace.bindingContext)
|
||||
propertiesMap.forEach { (descriptor, declaration) ->
|
||||
val isInitialized = declarationHasInitializer(declaration) || descriptor.isLateInit
|
||||
val isMarkedTransient = descriptor.annotations.serialTransient
|
||||
val hasBackingField = descriptor.hasBackingField(trace.bindingContext)
|
||||
if (!hasBackingField && isMarkedTransient) {
|
||||
val transientPsi =
|
||||
(descriptor.annotations.findAnnotation(SerializationAnnotations.serialTransientFqName) as? LazyAnnotationDescriptor)?.annotationEntry
|
||||
trace.report(SerializationErrors.TRANSIENT_IS_REDUNDANT.on(transientPsi ?: declaration))
|
||||
}
|
||||
|
||||
if (isMarkedTransient && !isInitialized && hasBackingField) {
|
||||
trace.report(SerializationErrors.TRANSIENT_MISSING_INITIALIZER.on(declaration))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun declarationHasInitializer(declaration: KtDeclaration): Boolean = when (declaration) {
|
||||
is KtParameter -> declaration.hasDefaultValue()
|
||||
is KtProperty -> declaration.hasDelegateExpressionOrInitializer()
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun analyzePropertiesSerializers(trace: BindingTrace, serializableClass: ClassDescriptor, props: List<SerializableProperty>) {
|
||||
val generatorContextForAnalysis = object : AbstractSerialGenerator(trace.bindingContext, serializableClass) {}
|
||||
props.forEach {
|
||||
val serializer = it.serializableWith?.toClassDescriptor
|
||||
val propertyPsi = it.descriptor.findPsi() ?: return@forEach
|
||||
val ktType = (propertyPsi as? KtCallableDeclaration)?.typeReference
|
||||
if (serializer != null) {
|
||||
val element = ktType?.typeElement
|
||||
checkCustomSerializerMatch(it.module, it.type, it.descriptor, element, trace, propertyPsi)
|
||||
checkCustomSerializerIsNotLocal(it.module, it.descriptor, trace, propertyPsi)
|
||||
checkSerializerNullability(it.type, serializer.defaultType, element, trace, propertyPsi)
|
||||
generatorContextForAnalysis.checkTypeArguments(it.module, it.type, element, trace, propertyPsi)
|
||||
} else {
|
||||
generatorContextForAnalysis.checkType(it.module, it.type, ktType, trace, propertyPsi)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun AbstractSerialGenerator.checkTypeArguments(
|
||||
module: ModuleDescriptor,
|
||||
type: KotlinType,
|
||||
element: KtTypeElement?,
|
||||
trace: BindingTrace,
|
||||
fallbackElement: PsiElement
|
||||
) {
|
||||
type.arguments.forEachIndexed { i, it ->
|
||||
checkType(
|
||||
module,
|
||||
it.type,
|
||||
element?.typeArgumentsAsTypes?.getOrNull(i),
|
||||
trace,
|
||||
fallbackElement
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.isUnsupportedInlineType() = isInlineClassType() && !KotlinBuiltIns.isPrimitiveTypeOrNullablePrimitiveType(this)
|
||||
|
||||
private fun canSupportInlineClasses(module: ModuleDescriptor, trace: BindingTrace): Boolean {
|
||||
if (isIde) return true // do not get version from jar manifest in ide
|
||||
return VersionReader.canSupportInlineClasses(module, trace)
|
||||
}
|
||||
|
||||
private fun AbstractSerialGenerator.checkType(
|
||||
module: ModuleDescriptor,
|
||||
type: KotlinType,
|
||||
ktType: KtTypeReference?,
|
||||
trace: BindingTrace,
|
||||
fallbackElement: PsiElement
|
||||
) {
|
||||
if (type.genericIndex != null) return // type arguments always have serializer stored in class' field
|
||||
val element = ktType?.typeElement
|
||||
if (type.isUnsupportedInlineType() && !canSupportInlineClasses(module, trace)) {
|
||||
trace.report(
|
||||
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
|
||||
element ?: fallbackElement,
|
||||
VersionReader.minVersionForInlineClasses.toString(),
|
||||
VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString()
|
||||
)
|
||||
)
|
||||
}
|
||||
val serializer = findTypeSerializerOrContextUnchecked(module, type)
|
||||
if (serializer != null) {
|
||||
checkCustomSerializerMatch(module, type, type, element, trace, fallbackElement)
|
||||
checkCustomSerializerIsNotLocal(module, type, trace, fallbackElement)
|
||||
checkSerializerNullability(type, serializer.defaultType, element, trace, fallbackElement)
|
||||
checkTypeArguments(module, type, element, trace, fallbackElement)
|
||||
} else {
|
||||
trace.report(SerializationErrors.SERIALIZER_NOT_FOUND.on(element ?: fallbackElement, type))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkCustomSerializerMatch(
|
||||
module: ModuleDescriptor,
|
||||
classType: KotlinType,
|
||||
descriptor: Annotated,
|
||||
element: KtElement?,
|
||||
trace: BindingTrace,
|
||||
fallbackElement: PsiElement
|
||||
) {
|
||||
val serializerType = descriptor.annotations.serializableWith(module) ?: return
|
||||
val serializerForType = serializerType.supertypes().find { isKSerializer(it) }?.arguments?.first()?.type ?: return
|
||||
// Compare constructors because we do not care about generic arguments and nullability
|
||||
if (classType.constructor != serializerForType.constructor)
|
||||
trace.report(
|
||||
SerializationErrors.SERIALIZER_TYPE_INCOMPATIBLE.on(
|
||||
element ?: fallbackElement,
|
||||
classType,
|
||||
serializerType,
|
||||
serializerForType
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private fun checkCustomSerializerIsNotLocal(
|
||||
module: ModuleDescriptor,
|
||||
declaration: Annotated,
|
||||
trace: BindingTrace,
|
||||
declarationElement: PsiElement
|
||||
) {
|
||||
val serializerType = declaration.annotations.serializableWith(module) ?: return
|
||||
val serializerDescriptor = serializerType.toClassDescriptor ?: return
|
||||
|
||||
if (DescriptorUtils.isLocal(serializerDescriptor)) {
|
||||
val element = declaration.findSerializableOrMetaAnnotationDeclaration() ?: declarationElement
|
||||
|
||||
trace.report(
|
||||
SerializationErrors.LOCAL_SERIALIZER_USAGE.on(
|
||||
element,
|
||||
serializerType
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkSerializerNullability(
|
||||
classType: KotlinType,
|
||||
serializerType: KotlinType,
|
||||
element: KtTypeElement?,
|
||||
trace: BindingTrace,
|
||||
fallbackElement: PsiElement
|
||||
) {
|
||||
// @Serializable annotation has proper signature so this error would be caught in type checker
|
||||
val castedToKSerial = serializerType.supertypes().find { isKSerializer(it) } ?: return
|
||||
|
||||
val serializerForType = castedToKSerial.arguments.first().type
|
||||
if (!classType.isMarkedNullable && serializerForType.isMarkedNullable)
|
||||
trace.report(
|
||||
SerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE.on(element ?: fallbackElement, serializerType, classType),
|
||||
)
|
||||
}
|
||||
|
||||
private inline fun ClassDescriptor.onSerializableOrMetaAnnotation(report: (KtAnnotationEntry) -> Unit) {
|
||||
findSerializableOrMetaAnnotationDeclaration()?.let(report)
|
||||
}
|
||||
|
||||
private fun BindingTrace.reportOnSerializableOrMetaAnnotation(
|
||||
descriptor: ClassDescriptor,
|
||||
error: DiagnosticFactory0<in KtAnnotationEntry>
|
||||
) {
|
||||
descriptor.onSerializableOrMetaAnnotation { e ->
|
||||
report(error.on(e))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val ClassDescriptor.serializableAnnotationIsUseless: Boolean
|
||||
get() = hasSerializableOrMetaAnnotationWithoutArgs && !isInternalSerializable && !hasCompanionObjectAsSerializer && kind != ClassKind.ENUM_CLASS && !isSealedSerializableInterface
|
||||
+149
@@ -0,0 +1,149 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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 org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers
|
||||
|
||||
object SerializationPluginErrorsRendering : DefaultErrorMessages.Extension {
|
||||
private val MAP = DiagnosticFactoryToRendererMap("SerializationPlugin")
|
||||
override fun getMap() = MAP
|
||||
|
||||
init {
|
||||
MAP.put(
|
||||
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED,
|
||||
"Inline classes require runtime serialization library version at least {0}, while your classpath has {1}.",
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING,
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.PLUGIN_IS_NOT_ENABLED,
|
||||
"kotlinx.serialization compiler plugin is not applied to the module, so this annotation would not be processed. " +
|
||||
"Make sure that you've setup your buildscript correctly and re-import project."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED,
|
||||
"Anonymous objects or contained in it classes can not be serializable."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.INNER_CLASSES_NOT_SUPPORTED,
|
||||
"Inner (with reference to outer this) serializable classes are not supported. Remove @Serializable annotation or 'inner' keyword."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED,
|
||||
"Explicit @Serializable annotation on enum class is required when @SerialName or @SerialInfo annotations are used on its members."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED,
|
||||
"@Serializable annotation without arguments can be used only on sealed interfaces." +
|
||||
"Non-sealed interfaces are polymorphically serializable by default."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR,
|
||||
"Impossible to make this class serializable because its parent is not serializable and does not have exactly one constructor without parameters"
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY,
|
||||
"This class is not serializable automatically because it has primary constructor parameters that are not properties"
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.DUPLICATE_SERIAL_NAME,
|
||||
"Serializable class has duplicate serial name of property ''{0}'', either in the class itself or its supertypes",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.DUPLICATE_SERIAL_NAME_ENUM,
|
||||
"Enum class ''{0}'' has duplicate serial name ''{1}'' in entry ''{2}''",
|
||||
Renderers.RENDER_TYPE,
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.SERIALIZER_NOT_FOUND,
|
||||
"Serializer has not been found for type ''{0}''. " +
|
||||
"To use context serializer as fallback, explicitly annotate type or property with @Contextual",
|
||||
Renderers.RENDER_TYPE_WITH_ANNOTATIONS
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE,
|
||||
"Type ''{1}'' is non-nullable and therefore can not be serialized with serializer for nullable type ''{0}''",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.SERIALIZER_TYPE_INCOMPATIBLE,
|
||||
"Class ''{1}'', which is serializer for type ''{2}'', is applied here to type ''{0}''. This may lead to errors or incorrect behavior.",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.LOCAL_SERIALIZER_USAGE,
|
||||
"Class ''{0}'' can't be used as a serializer since it is local",
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.TRANSIENT_MISSING_INITIALIZER,
|
||||
"This property is marked as @Transient and therefore must have an initializing expression"
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.TRANSIENT_IS_REDUNDANT,
|
||||
"Property does not have backing field which makes it non-serializable and therefore @Transient is redundant"
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.JSON_FORMAT_REDUNDANT_DEFAULT,
|
||||
"Redundant creation of Json default format. Creating instances for each usage can be slow."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.JSON_FORMAT_REDUNDANT,
|
||||
"Redundant creation of Json format. Creating instances for each usage can be slow."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.INCORRECT_TRANSIENT,
|
||||
"@kotlin.jvm.Transient does not affect @Serializable classes. Please use @kotlinx.serialization.Transient instead."
|
||||
)
|
||||
MAP.put(
|
||||
SerializationErrors.REQUIRED_KOTLIN_TOO_HIGH,
|
||||
"Your current Kotlin version is {0}, while kotlinx.serialization core runtime {1} requires at least Kotlin {2}. " +
|
||||
"Please update your Kotlin compiler and IDE plugin.",
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
SerializationErrors.PROVIDED_RUNTIME_TOO_LOW,
|
||||
"Your current kotlinx.serialization core version is {0}, while current Kotlin compiler plugin {1} requires at least {2}. " +
|
||||
"Please update your kotlinx.serialization runtime dependency.",
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
SerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO,
|
||||
"Argument values for inheritable serial info annotation ''{0}'' must be the same as the values in parent type ''{1}''",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
SerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE,
|
||||
"Cannot generate external serializer ''{0}'': class ''{1}'' have constructor parameters which are not properties and therefore it is not serializable automatically",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
SerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE,
|
||||
"Cannot generate external serializer ''{0}'': class ''{1}'' is defined in another module",
|
||||
Renderers.RENDER_TYPE,
|
||||
Renderers.RENDER_TYPE
|
||||
)
|
||||
}
|
||||
}
|
||||
+77
@@ -0,0 +1,77 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.ModuleDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.BindingTrace
|
||||
import org.jetbrains.kotlin.util.slicedMap.Slices
|
||||
import org.jetbrains.kotlin.util.slicedMap.WritableSlice
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.getClassFromSerializationPackage
|
||||
import java.io.File
|
||||
import java.util.jar.Attributes
|
||||
|
||||
object VersionReader {
|
||||
data class RuntimeVersions(val implementationVersion: ApiVersion?, val requireKotlinVersion: ApiVersion?) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
val MINIMAL_SUPPORTED_VERSION = ApiVersion.parse("1.0-M1-SNAPSHOT")!!
|
||||
|
||||
private val REQUIRE_KOTLIN_VERSION = Attributes.Name("Require-Kotlin-Version")
|
||||
private const val CLASS_SUFFIX = "!/kotlinx/serialization/KSerializer.class"
|
||||
|
||||
private val VERSIONS_SLICE: WritableSlice<ModuleDescriptor, RuntimeVersions> = Slices.createSimpleSlice()
|
||||
|
||||
fun getVersionsForCurrentModuleFromTrace(module: ModuleDescriptor, trace: BindingTrace): RuntimeVersions? {
|
||||
trace.get(VERSIONS_SLICE, module)?.let { return it }
|
||||
val versions = getVersionsForCurrentModule(module) ?: return null
|
||||
trace.record(VERSIONS_SLICE, module, versions)
|
||||
return versions
|
||||
}
|
||||
|
||||
fun getVersionsForCurrentModuleFromContext(module: ModuleDescriptor, context: BindingContext?): RuntimeVersions? {
|
||||
context?.get(VERSIONS_SLICE, module)?.let { return it }
|
||||
return getVersionsForCurrentModule(module)
|
||||
}
|
||||
|
||||
fun getVersionsForCurrentModule(module: ModuleDescriptor): RuntimeVersions? {
|
||||
val markerClass = module.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
val location = (markerClass.source 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)
|
||||
}
|
||||
|
||||
internal val minVersionForInlineClasses = ApiVersion.parse("1.1-M1-SNAPSHOT")!!
|
||||
|
||||
fun canSupportInlineClasses(module: ModuleDescriptor, trace: BindingTrace): Boolean {
|
||||
// Klibs do not have manifest file, unfortunately, so we hope for the better
|
||||
val currentVersion = getVersionsForCurrentModuleFromTrace(module, trace) ?: return true
|
||||
val implVersion = currentVersion.implementationVersion ?: return false
|
||||
return implVersion >= minVersionForInlineClasses
|
||||
}
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.extensions
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.metadata.ProtoBuf
|
||||
import org.jetbrains.kotlin.metadata.serialization.MutableVersionRequirementTable
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializer
|
||||
import org.jetbrains.kotlin.serialization.DescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlin.serialization.SerializerExtension
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializableProperties
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.isInternalSerializable
|
||||
|
||||
class SerializationDescriptorSerializerPlugin : DescriptorSerializerPlugin {
|
||||
private val descriptorMetadataMap: MutableMap<ClassDescriptor, SerializableProperties> = hashMapOf()
|
||||
|
||||
private val ClassDescriptor.needSaveProgramOrder: Boolean
|
||||
get() = isInternalSerializable && (modality == Modality.OPEN || modality == Modality.ABSTRACT)
|
||||
|
||||
internal fun putIfNeeded(descriptor: ClassDescriptor, properties: SerializableProperties) {
|
||||
if (!descriptor.needSaveProgramOrder) return
|
||||
descriptorMetadataMap[descriptor] = properties
|
||||
}
|
||||
|
||||
override fun afterClass(
|
||||
descriptor: ClassDescriptor,
|
||||
proto: ProtoBuf.Class.Builder,
|
||||
versionRequirementTable: MutableVersionRequirementTable,
|
||||
childSerializer: DescriptorSerializer,
|
||||
extension: SerializerExtension
|
||||
) {
|
||||
fun Name.toIndex() = extension.stringTable.getStringIndex(asString())
|
||||
|
||||
if (!descriptor.needSaveProgramOrder) return
|
||||
|
||||
val propertiesCorrectOrder = (descriptorMetadataMap[descriptor] ?: return).serializableProperties
|
||||
proto.setExtension(
|
||||
SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder,
|
||||
propertiesCorrectOrder.map { it.descriptor.name.toIndex() }
|
||||
)
|
||||
descriptorMetadataMap.remove(descriptor)
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// Generated by the protocol buffer compiler. DO NOT EDIT!
|
||||
// source: plugins/kotlin-serialization/kotlin-serialization-compiler/src/class_extensions.proto
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.extensions;
|
||||
|
||||
public final class SerializationPluginMetadataExtensions {
|
||||
private SerializationPluginMetadataExtensions() {}
|
||||
public static void registerAllExtensions(
|
||||
org.jetbrains.kotlin.protobuf.ExtensionRegistryLite registry) {
|
||||
registry.add(org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder);
|
||||
}
|
||||
public static final int PROPERTIES_NAMES_IN_PROGRAM_ORDER_FIELD_NUMBER = 18000;
|
||||
/**
|
||||
* <code>extend .org.jetbrains.kotlin.metadata.Class { ... }</code>
|
||||
*/
|
||||
public static final
|
||||
org.jetbrains.kotlin.protobuf.GeneratedMessageLite.GeneratedExtension<
|
||||
org.jetbrains.kotlin.metadata.ProtoBuf.Class,
|
||||
java.util.List<java.lang.Integer>> propertiesNamesInProgramOrder = org.jetbrains.kotlin.protobuf.GeneratedMessageLite
|
||||
.newRepeatedGeneratedExtension(
|
||||
org.jetbrains.kotlin.metadata.ProtoBuf.Class.getDefaultInstance(),
|
||||
null,
|
||||
null,
|
||||
18000,
|
||||
org.jetbrains.kotlin.protobuf.WireFormat.FieldType.INT32,
|
||||
false,
|
||||
java.lang.Integer.class);
|
||||
|
||||
static {
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(outer_class_scope)
|
||||
}
|
||||
+131
@@ -0,0 +1,131 @@
|
||||
/*
|
||||
* 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.extensions
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.SpecialNames
|
||||
import org.jetbrains.kotlin.platform.jvm.isJvm
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.isInlineClass
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.SerializationDescriptorUtils
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
open class SerializationResolveExtension @JvmOverloads constructor(val metadataPlugin: SerializationDescriptorSerializerPlugin? = null) : SyntheticResolveExtension {
|
||||
override fun getSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name> = when {
|
||||
thisDescriptor.isSerialInfoAnnotation && thisDescriptor.platform?.isJvm() == true -> listOf(SerialEntityNames.IMPL_NAME)
|
||||
(thisDescriptor.shouldHaveGeneratedSerializer) && !thisDescriptor.hasCompanionObjectAsSerializer ->
|
||||
listOf(SerialEntityNames.SERIALIZER_CLASS_NAME)
|
||||
else -> listOf()
|
||||
}
|
||||
|
||||
override fun getPossibleSyntheticNestedClassNames(thisDescriptor: ClassDescriptor): List<Name>? {
|
||||
return listOf(SerialEntityNames.IMPL_NAME, SerialEntityNames.SERIALIZER_CLASS_NAME)
|
||||
}
|
||||
|
||||
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> = when {
|
||||
thisDescriptor.isSerializableObject || thisDescriptor.isCompanionObject && getSerializableClassDescriptorByCompanion(thisDescriptor) != null ->
|
||||
listOf(SerialEntityNames.SERIALIZER_PROVIDER_NAME)
|
||||
thisDescriptor.isInternalSerializable && !thisDescriptor.isInlineClass() && thisDescriptor.platform?.isJvm() == true && !hasCustomizedSerializeMethod(thisDescriptor) -> {
|
||||
// add write$Self, but only if .serialize was not customized in companion.
|
||||
// It works not only on JVM, but I see no reason to enable it on other platforms —
|
||||
// private fields there have no access control, and additional function
|
||||
// only increases compiled code size.
|
||||
listOf(SerialEntityNames.WRITE_SELF_NAME)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
|
||||
override fun getSyntheticPropertiesNames(thisDescriptor: ClassDescriptor): List<Name> {
|
||||
// typeSerial0, typeSerial1, ... for serializers of parameterized classes
|
||||
val count = thisDescriptor.declaredTypeParameters.size
|
||||
if (count < 1) return emptyList()
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return emptyList()
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return emptyList()
|
||||
val propNames = (0 until count).map { "${SerialEntityNames.typeArgPrefix}$it" }.map { Name.identifier(it) }
|
||||
return propNames
|
||||
}
|
||||
|
||||
private fun hasCustomizedSerializeMethod(serializableClass: ClassDescriptor): Boolean {
|
||||
// We cannot check whether companion has @Serializer(MyClass::class) annotation due to recursive resolve problems
|
||||
// (apparently, resolve MyClass type asks for all function names, which leads us to this function again)
|
||||
// so we rely on less strict check that companion just has non-empty @Serializer annotation.
|
||||
// Anyway, I doubt that serializable class companion would ever be serializer for _another_ class.
|
||||
val companion = serializableClass.companionObjectDescriptor ?: return false
|
||||
return companion.annotations.hasAnnotation(SerializationAnnotations.serializerAnnotationFqName)
|
||||
}
|
||||
|
||||
override fun generateSyntheticClasses(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
ctx: LazyClassContext,
|
||||
declarationProvider: ClassMemberDeclarationProvider,
|
||||
result: MutableSet<ClassDescriptor>
|
||||
) {
|
||||
if (thisDescriptor.isSerialInfoAnnotation && name == SerialEntityNames.IMPL_NAME)
|
||||
result.add(KSerializerDescriptorResolver.addSerialInfoImplClass(thisDescriptor, declarationProvider, ctx))
|
||||
else if (thisDescriptor.shouldHaveGeneratedSerializer && name == SerialEntityNames.SERIALIZER_CLASS_NAME &&
|
||||
result.none { it.name == SerialEntityNames.SERIALIZER_CLASS_NAME }
|
||||
)
|
||||
result.add(KSerializerDescriptorResolver.addSerializerImplClass(thisDescriptor, declarationProvider, ctx))
|
||||
return
|
||||
}
|
||||
|
||||
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? =
|
||||
if (thisDescriptor.shouldHaveGeneratedMethodsInCompanion && !thisDescriptor.isSerializableObject)
|
||||
SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT
|
||||
else null
|
||||
|
||||
override fun addSyntheticSupertypes(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
|
||||
KSerializerDescriptorResolver.addSerialInfoSuperType(thisDescriptor, supertypes)
|
||||
KSerializerDescriptorResolver.addSerializerSupertypes(thisDescriptor, supertypes)
|
||||
KSerializerDescriptorResolver.addSerializerFactorySuperType(thisDescriptor, supertypes)
|
||||
}
|
||||
|
||||
override fun generateSyntheticSecondaryConstructors(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
result: MutableCollection<ClassConstructorDescriptor>
|
||||
) {
|
||||
if (thisDescriptor.isInternalSerializable) {
|
||||
// do not add synthetic deserialization constructor if .deserialize method is customized
|
||||
if (thisDescriptor.hasCompanionObjectAsSerializer && SerializationDescriptorUtils.getSyntheticLoadMember(thisDescriptor.companionObjectDescriptor!!) == null) return
|
||||
if (thisDescriptor.isInlineClass()) return
|
||||
result.add(KSerializerDescriptorResolver.createLoadConstructorDescriptor(thisDescriptor, bindingContext, metadataPlugin))
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateSyntheticMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
bindingContext: BindingContext,
|
||||
fromSupertypes: List<SimpleFunctionDescriptor>,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
KSerializerDescriptorResolver.generateSerializerMethods(thisDescriptor, fromSupertypes, name, result)
|
||||
KSerializerDescriptorResolver.generateCompanionObjectMethods(thisDescriptor, name, result)
|
||||
KSerializerDescriptorResolver.generateSerializableClassMethods(thisDescriptor, name, result)
|
||||
}
|
||||
|
||||
override fun generateSyntheticProperties(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
bindingContext: BindingContext,
|
||||
fromSupertypes: ArrayList<PropertyDescriptor>,
|
||||
result: MutableSet<PropertyDescriptor>
|
||||
) {
|
||||
KSerializerDescriptorResolver.generateDescriptorsForAnnotationImpl(thisDescriptor, fromSupertypes, result)
|
||||
KSerializerDescriptorResolver.generateSerializerProperties(thisDescriptor, fromSupertypes, name, result)
|
||||
}
|
||||
}
|
||||
+306
@@ -0,0 +1,306 @@
|
||||
/*
|
||||
* 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.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.platform.js.isJs
|
||||
import org.jetbrains.kotlin.platform.konan.isNative
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.annotationClass
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.platform
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serializableAnnotationFqName
|
||||
|
||||
fun isAllowedToHaveAutoGeneratedSerializerMethods(
|
||||
classDescriptor: ClassDescriptor,
|
||||
serializableClassDescriptor: ClassDescriptor
|
||||
): Boolean {
|
||||
if (serializableClassDescriptor.isSerializableEnum()) return true
|
||||
// don't generate automatically anything for enums or interfaces or other strange things
|
||||
if (serializableClassDescriptor.kind != ClassKind.CLASS) return false
|
||||
// it is either GeneratedSerializer implementation
|
||||
// or user implementation which does not have type parameters (to be able to correctly initialize descriptor)
|
||||
return classDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) ||
|
||||
(classDescriptor.typeConstructor.supertypes.any(::isKSerializer) && classDescriptor.declaredTypeParameters.isEmpty())
|
||||
}
|
||||
|
||||
fun isKSerializer(type: KotlinType?): Boolean =
|
||||
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.KSERIALIZER_NAME_FQ)
|
||||
|
||||
fun isGeneratedKSerializer(type: KotlinType?): Boolean =
|
||||
type != null && KotlinBuiltIns.isConstructedFromGivenClass(type, SerialEntityNames.GENERATED_SERIALIZER_FQ)
|
||||
|
||||
fun ClassDescriptor.getGeneratedSerializerDescriptor(): ClassDescriptor =
|
||||
module.getClassFromInternalSerializationPackage(SerialEntityNames.GENERATED_SERIALIZER_CLASS.identifier)
|
||||
|
||||
fun ClassDescriptor.createSerializerTypeFor(argument: SimpleType, baseSerializerInterface: FqName): SimpleType {
|
||||
val projectionType = Variance.INVARIANT
|
||||
val types = listOf(TypeProjectionImpl(projectionType, argument))
|
||||
val descriptor = module.findClassAcrossModuleDependencies(ClassId.topLevel(baseSerializerInterface))
|
||||
?: throw IllegalArgumentException("Can't locate $baseSerializerInterface. Is kotlinx-serialization library present in compile classpath?")
|
||||
return KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, descriptor, types)
|
||||
}
|
||||
|
||||
fun extractKSerializerArgumentFromImplementation(implementationClass: ClassDescriptor): KotlinType? {
|
||||
val supertypes = implementationClass.typeConstructor.supertypes
|
||||
val kSerializerSupertype = supertypes.find { isGeneratedKSerializer(it) }
|
||||
?: supertypes.find { isKSerializer(it) }
|
||||
?: return null
|
||||
return kSerializerSupertype.arguments.first().type
|
||||
}
|
||||
|
||||
val DeclarationDescriptor.serializableWith: KotlinType?
|
||||
get() = annotations.serializableWith(module)
|
||||
|
||||
fun Annotations.serializableWith(module: ModuleDescriptor): KotlinType? =
|
||||
this.findAnnotationKotlinTypeValue(serializableAnnotationFqName, module, "with")
|
||||
|
||||
val DeclarationDescriptor.serializerForClass: KotlinType?
|
||||
get() = annotations.findAnnotationKotlinTypeValue(SerializationAnnotations.serializerAnnotationFqName, module, "forClass")
|
||||
|
||||
val ClassDescriptor.isSerialInfoAnnotation: Boolean
|
||||
get() = annotations.hasAnnotation(serialInfoFqName)
|
||||
|| annotations.hasAnnotation(inheritableSerialInfoFqName)
|
||||
|| annotations.hasAnnotation(metaSerializableAnnotationFqName)
|
||||
|
||||
val ClassDescriptor.isInheritableSerialInfoAnnotation: Boolean
|
||||
get() = annotations.hasAnnotation(inheritableSerialInfoFqName)
|
||||
|
||||
val Annotations.serialNameValue: String?
|
||||
get() = findAnnotationConstantValue(SerializationAnnotations.serialNameAnnotationFqName, "value")
|
||||
|
||||
val Annotations.serialNameAnnotation: AnnotationDescriptor?
|
||||
get() = findAnnotation(SerializationAnnotations.serialNameAnnotationFqName)
|
||||
|
||||
val Annotations.serialRequired: Boolean
|
||||
get() = hasAnnotation(SerializationAnnotations.requiredAnnotationFqName)
|
||||
|
||||
val Annotations.serialTransient: Boolean
|
||||
get() = hasAnnotation(SerializationAnnotations.serialTransientFqName)
|
||||
|
||||
// ----------------------------------------
|
||||
|
||||
val KotlinType?.toClassDescriptor: ClassDescriptor?
|
||||
@JvmName("toClassDescriptor")
|
||||
get() = this?.constructor?.declarationDescriptor?.let { descriptor ->
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> descriptor
|
||||
is TypeParameterDescriptor -> descriptor.representativeUpperBound.toClassDescriptor
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
val ClassDescriptor.shouldHaveGeneratedMethodsInCompanion: Boolean
|
||||
get() = this.isSerializableObject || this.isSerializableEnum() || (this.kind == ClassKind.CLASS && hasSerializableOrMetaAnnotation) || this.isSealedSerializableInterface
|
||||
|
||||
val ClassDescriptor.isSerializableObject: Boolean
|
||||
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotation
|
||||
|
||||
val ClassDescriptor.isInternallySerializableObject: Boolean
|
||||
get() = kind == ClassKind.OBJECT && hasSerializableOrMetaAnnotationWithoutArgs
|
||||
|
||||
val ClassDescriptor.isSealedSerializableInterface: Boolean
|
||||
get() = kind == ClassKind.INTERFACE && modality == Modality.SEALED && hasSerializableOrMetaAnnotation
|
||||
|
||||
val ClassDescriptor.isInternalSerializable: Boolean //todo normal checking
|
||||
get() {
|
||||
if (kind != ClassKind.CLASS) return false
|
||||
return hasSerializableOrMetaAnnotationWithoutArgs
|
||||
}
|
||||
|
||||
fun ClassDescriptor.isSerializableEnum(): Boolean = kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotation
|
||||
|
||||
fun ClassDescriptor.isEnumWithLegacyGeneratedSerializer(): Boolean = isInternallySerializableEnum() && useGeneratedEnumSerializer
|
||||
|
||||
fun ClassDescriptor.isInternallySerializableEnum(): Boolean =
|
||||
kind == ClassKind.ENUM_CLASS && hasSerializableOrMetaAnnotationWithoutArgs
|
||||
|
||||
val ClassDescriptor.shouldHaveGeneratedSerializer: Boolean
|
||||
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN))
|
||||
|| isEnumWithLegacyGeneratedSerializer()
|
||||
|
||||
val ClassDescriptor.useGeneratedEnumSerializer: Boolean
|
||||
get() {
|
||||
val functions = module.getPackage(SerializationPackages.internalPackageFqName).memberScope.getFunctionNames()
|
||||
return !functions.contains(ENUM_SERIALIZER_FACTORY_FUNC_NAME) || !functions.contains(MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)
|
||||
}
|
||||
|
||||
fun ClassDescriptor.enumEntries(): List<ClassDescriptor> {
|
||||
check(this.kind == ClassKind.ENUM_CLASS)
|
||||
return unsubstitutedMemberScope.getContributedDescriptors().asSequence()
|
||||
.filterIsInstance<ClassDescriptor>()
|
||||
.filter { it.kind == ClassKind.ENUM_ENTRY }
|
||||
.toList()
|
||||
}
|
||||
|
||||
// check enum or its elements has any SerialInfo annotation
|
||||
fun ClassDescriptor.isEnumWithSerialInfoAnnotation(): Boolean {
|
||||
if (kind != ClassKind.ENUM_CLASS) return false
|
||||
if (annotations.hasAnySerialAnnotation) return true
|
||||
return enumEntries().any { (it.annotations.hasAnySerialAnnotation) }
|
||||
}
|
||||
|
||||
val Annotations.hasAnySerialAnnotation: Boolean
|
||||
get() = serialNameValue != null || any { it.annotationClass?.isSerialInfoAnnotation == true }
|
||||
|
||||
val ClassDescriptor.hasSerializableOrMetaAnnotation
|
||||
get() = hasSerializableAnnotation || hasMetaSerializableAnnotation
|
||||
|
||||
private val ClassDescriptor.hasSerializableAnnotation
|
||||
get() = annotations.hasSerializableAnnotation
|
||||
|
||||
private val Annotations.hasSerializableAnnotation
|
||||
get() = hasAnnotation(serializableAnnotationFqName)
|
||||
|
||||
val ClassDescriptor.hasMetaSerializableAnnotation: Boolean
|
||||
get() = annotations.any { it.isMetaSerializableAnnotation }
|
||||
|
||||
val AnnotationDescriptor.isMetaSerializableAnnotation: Boolean
|
||||
get() = annotationClass?.annotations?.hasAnnotation(metaSerializableAnnotationFqName) ?: false
|
||||
|
||||
val ClassDescriptor.hasSerializableOrMetaAnnotationWithoutArgs: Boolean
|
||||
get() = hasSerializableAnnotationWithoutArgs
|
||||
|| (!annotations.hasSerializableAnnotation && hasMetaSerializableAnnotation)
|
||||
|
||||
private val ClassDescriptor.hasSerializableAnnotationWithoutArgs: Boolean
|
||||
get() {
|
||||
if (!hasSerializableAnnotation) return false
|
||||
// If provided descriptor is lazy, carefully look at psi in order not to trigger full resolve which may be recursive.
|
||||
// Otherwise, this descriptor is deserialized from another module, and it is OK to check value right away.
|
||||
val psi = findSerializableAnnotationDeclaration() ?: return (serializableWith == null)
|
||||
return psi.valueArguments.isEmpty()
|
||||
}
|
||||
|
||||
private fun Annotated.findSerializableAnnotationDeclaration(): KtAnnotationEntry? {
|
||||
val lazyDesc = annotations.findAnnotation(serializableAnnotationFqName) as? LazyAnnotationDescriptor
|
||||
return lazyDesc?.annotationEntry
|
||||
}
|
||||
|
||||
fun Annotated.findSerializableOrMetaAnnotationDeclaration(): KtAnnotationEntry? {
|
||||
val lazyDesc = (annotations.findAnnotation(serializableAnnotationFqName)
|
||||
?: annotations.firstOrNull { it.isMetaSerializableAnnotation }) as? LazyAnnotationDescriptor
|
||||
return lazyDesc?.annotationEntry
|
||||
}
|
||||
|
||||
fun Annotated.findAnnotationDeclaration(fqName: FqName): KtAnnotationEntry? {
|
||||
val lazyDesc = annotations.findAnnotation(fqName) as? LazyAnnotationDescriptor
|
||||
return lazyDesc?.annotationEntry
|
||||
}
|
||||
|
||||
// For abstract classes marked with @Serializable,
|
||||
// methods are generated anyway, although they shouldn't have
|
||||
// generated $serializer and use Polymorphic one.
|
||||
fun ClassDescriptor.isAbstractOrSealedSerializableClass(): Boolean =
|
||||
isInternalSerializable && (modality == Modality.ABSTRACT || modality == Modality.SEALED)
|
||||
|
||||
fun ClassDescriptor.polymorphicSerializerIfApplicableAutomatically(): ClassDescriptor? {
|
||||
val serializer = when {
|
||||
kind == ClassKind.INTERFACE && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
|
||||
kind == ClassKind.INTERFACE -> SpecialBuiltins.polymorphicSerializer
|
||||
isInternalSerializable && modality == Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
|
||||
isInternalSerializable && modality == Modality.SEALED -> SpecialBuiltins.sealedSerializer
|
||||
else -> null
|
||||
}
|
||||
return serializer?.let { module.getClassFromSerializationPackage(it) }
|
||||
}
|
||||
|
||||
// serializer that was declared for this type
|
||||
val ClassDescriptor?.classSerializer: ClassDescriptor?
|
||||
get() = this?.let {
|
||||
// serializer annotation on class?
|
||||
serializableWith?.let { return it.toClassDescriptor }
|
||||
// companion object serializer?
|
||||
if (hasCompanionObjectAsSerializer) return companionObjectDescriptor
|
||||
// can infer @Poly?
|
||||
polymorphicSerializerIfApplicableAutomatically()?.let { return it }
|
||||
// default serializable?
|
||||
if (shouldHaveGeneratedSerializer) {
|
||||
// $serializer nested class
|
||||
return this.unsubstitutedMemberScope
|
||||
.getDescriptorsFiltered(nameFilter = { it == SerialEntityNames.SERIALIZER_CLASS_NAME })
|
||||
.filterIsInstance<ClassDescriptor>().singleOrNull()
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
val ClassDescriptor.hasCompanionObjectAsSerializer: Boolean
|
||||
get() = isInternallySerializableObject || companionObjectDescriptor?.serializerForClass == this.defaultType
|
||||
|
||||
// returns only user-overriden Serializer
|
||||
val KotlinType.overridenSerializer: KotlinType?
|
||||
get() {
|
||||
val desc = this.toClassDescriptor ?: return null
|
||||
desc.serializableWith?.let { return it }
|
||||
return null
|
||||
}
|
||||
|
||||
val KotlinType.genericIndex: Int?
|
||||
get() = (this.constructor.declarationDescriptor as? TypeParameterDescriptor)?.index
|
||||
|
||||
fun getSerializableClassDescriptorByCompanion(thisDescriptor: ClassDescriptor): ClassDescriptor? {
|
||||
if (thisDescriptor.isSerializableObject) return thisDescriptor
|
||||
if (!thisDescriptor.isCompanionObject) return null
|
||||
val classDescriptor = (thisDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
|
||||
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
|
||||
return classDescriptor
|
||||
}
|
||||
|
||||
fun ClassDescriptor.needSerializerFactory(): Boolean {
|
||||
if (!(this.platform?.isNative() == true || this.platform.isJs())) return false
|
||||
val serializableClass = getSerializableClassDescriptorByCompanion(this) ?: return false
|
||||
if (serializableClass.isSerializableObject) return true
|
||||
if (serializableClass.isSerializableEnum()) return true
|
||||
if (serializableClass.isAbstractOrSealedSerializableClass()) return true
|
||||
if (serializableClass.isSealedSerializableInterface) return true
|
||||
if (serializableClass.declaredTypeParameters.isEmpty()) return false
|
||||
return true
|
||||
}
|
||||
|
||||
fun getSerializableClassDescriptorBySerializer(serializerDescriptor: ClassDescriptor): ClassDescriptor? {
|
||||
val serializerForClass = serializerDescriptor.serializerForClass
|
||||
if (serializerForClass != null) return serializerForClass.toClassDescriptor
|
||||
if (serializerDescriptor.name !in setOf(
|
||||
SerialEntityNames.SERIALIZER_CLASS_NAME,
|
||||
SerialEntityNames.GENERATED_SERIALIZER_CLASS
|
||||
)
|
||||
) return null
|
||||
val classDescriptor = (serializerDescriptor.containingDeclaration as? ClassDescriptor) ?: return null
|
||||
if (!classDescriptor.shouldHaveGeneratedSerializer) return null
|
||||
return classDescriptor
|
||||
}
|
||||
|
||||
fun ClassDescriptor.checkSerializableClassPropertyResult(prop: PropertyDescriptor): Boolean =
|
||||
prop.returnType!!.isSubtypeOf(getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false)) // todo: cache lookup
|
||||
|
||||
// todo: serialization: do an actual check better that just number of parameters
|
||||
fun ClassDescriptor.checkSaveMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
|
||||
parameters.size == 2
|
||||
|
||||
fun ClassDescriptor.checkSaveMethodResult(type: KotlinType): Boolean =
|
||||
KotlinBuiltIns.isUnit(type)
|
||||
|
||||
// todo: serialization: do an actual check better that just number of parameters
|
||||
fun ClassDescriptor.checkLoadMethodParameters(parameters: List<ValueParameterDescriptor>): Boolean =
|
||||
parameters.size == 1
|
||||
|
||||
fun ClassDescriptor.checkLoadMethodResult(type: KotlinType): Boolean =
|
||||
getSerializableClassDescriptorBySerializer(this)?.defaultType == type
|
||||
+667
@@ -0,0 +1,667 @@
|
||||
/*
|
||||
* Copyright 2010-2021 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.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.annotations.createDeprecatedAnnotation
|
||||
import org.jetbrains.kotlin.descriptors.impl.*
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlin.psi.synthetics.SyntheticClassOrObjectDescriptor
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.components.isVararg
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.lazy.LazyClassContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.declarations.ClassMemberDeclarationProvider
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyClassDescriptor
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.*
|
||||
import org.jetbrains.kotlin.types.typeUtil.createProjection
|
||||
import org.jetbrains.kotlin.types.typeUtil.makeNullable
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.IMPL_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.SERIALIZER_CLASS_NAME
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||
|
||||
object KSerializerDescriptorResolver {
|
||||
|
||||
fun createDeprecatedHiddenAnnotation(module: ModuleDescriptor): AnnotationDescriptor {
|
||||
return module.builtIns.createDeprecatedAnnotation(
|
||||
"This synthesized declaration should not be used directly",
|
||||
level = "HIDDEN"
|
||||
)
|
||||
}
|
||||
|
||||
fun isSerialInfoImpl(thisDescriptor: ClassDescriptor): Boolean {
|
||||
return thisDescriptor.name == IMPL_NAME
|
||||
&& thisDescriptor.containingDeclaration is LazyClassDescriptor
|
||||
&& (thisDescriptor.containingDeclaration as ClassDescriptor).isSerialInfoAnnotation
|
||||
}
|
||||
|
||||
fun addSerialInfoSuperType(thisDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
|
||||
if (isSerialInfoImpl(thisDescriptor)) {
|
||||
supertypes.add((thisDescriptor.containingDeclaration as LazyClassDescriptor).toSimpleType(false))
|
||||
}
|
||||
}
|
||||
|
||||
fun addSerializerFactorySuperType(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
|
||||
if (!classDescriptor.needSerializerFactory()) return
|
||||
val serializerFactoryClass =
|
||||
classDescriptor.module.getClassFromInternalSerializationPackage("SerializerFactory")
|
||||
supertypes.add(KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializerFactoryClass, listOf()))
|
||||
}
|
||||
|
||||
fun addSerializerSupertypes(classDescriptor: ClassDescriptor, supertypes: MutableList<KotlinType>) {
|
||||
val serializableClassDescriptor = getSerializableClassDescriptorBySerializer(classDescriptor) ?: return
|
||||
if (supertypes.any(::isKSerializer)) return
|
||||
|
||||
// Add GeneratedSerializer as superinterface for generated $serializer class, and KSerializer to all others
|
||||
val fqName = if (classDescriptor.name == SerialEntityNames.SERIALIZER_CLASS_NAME)
|
||||
SerialEntityNames.GENERATED_SERIALIZER_FQ
|
||||
else
|
||||
SerialEntityNames.KSERIALIZER_NAME_FQ
|
||||
supertypes.add(classDescriptor.createSerializerTypeFor(serializableClassDescriptor.defaultType, fqName))
|
||||
}
|
||||
|
||||
fun addSerialInfoImplClass(
|
||||
interfaceDesc: ClassDescriptor,
|
||||
declarationProvider: ClassMemberDeclarationProvider,
|
||||
ctx: LazyClassContext
|
||||
): ClassDescriptor {
|
||||
val interfaceDecl = declarationProvider.correspondingClassOrObject!!
|
||||
val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor)
|
||||
|
||||
val props = interfaceDecl.primaryConstructorParameters
|
||||
// if there is some properties, there will be a public synthetic constructor at the codegen phase
|
||||
val primaryCtorVisibility = if (props.isEmpty()) DescriptorVisibilities.PUBLIC else DescriptorVisibilities.PRIVATE
|
||||
|
||||
val descriptor = SyntheticClassOrObjectDescriptor(
|
||||
ctx,
|
||||
interfaceDecl,
|
||||
interfaceDesc,
|
||||
IMPL_NAME,
|
||||
interfaceDesc.source,
|
||||
scope,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC,
|
||||
Annotations.create(listOf(createDeprecatedHiddenAnnotation(interfaceDesc.module))),
|
||||
primaryCtorVisibility,
|
||||
ClassKind.CLASS,
|
||||
false
|
||||
)
|
||||
descriptor.initialize()
|
||||
return descriptor
|
||||
}
|
||||
|
||||
fun addSerializerImplClass(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
declarationProvider: ClassMemberDeclarationProvider,
|
||||
ctx: LazyClassContext
|
||||
): ClassDescriptor {
|
||||
val thisDeclaration = declarationProvider.correspondingClassOrObject!!
|
||||
val scope = ctx.declarationScopeProvider.getResolutionScopeForDeclaration(declarationProvider.ownerInfo!!.scopeAnchor)
|
||||
val hasTypeParams = thisDescriptor.declaredTypeParameters.isNotEmpty()
|
||||
val serializerKind = if (hasTypeParams) ClassKind.CLASS else ClassKind.OBJECT
|
||||
val serializerDescriptor = SyntheticClassOrObjectDescriptor(
|
||||
ctx,
|
||||
thisDeclaration,
|
||||
thisDescriptor, SERIALIZER_CLASS_NAME, thisDescriptor.source,
|
||||
scope,
|
||||
Modality.FINAL, DescriptorVisibilities.PUBLIC,
|
||||
Annotations.create(listOf(createDeprecatedHiddenAnnotation(thisDescriptor.module))),
|
||||
DescriptorVisibilities.PRIVATE,
|
||||
serializerKind, false
|
||||
)
|
||||
val typeParameters: List<TypeParameterDescriptor> =
|
||||
thisDescriptor.declaredTypeParameters.mapIndexed { index, param ->
|
||||
TypeParameterDescriptorImpl.createWithDefaultBound(
|
||||
serializerDescriptor, Annotations.EMPTY, false, Variance.INVARIANT,
|
||||
param.name, index, LockBasedStorageManager.NO_LOCKS
|
||||
)
|
||||
}
|
||||
serializerDescriptor.initialize(typeParameters)
|
||||
val secondaryCtors =
|
||||
if (!hasTypeParams)
|
||||
emptyList()
|
||||
else
|
||||
listOf(createTypedSerializerConstructorDescriptor(serializerDescriptor, thisDescriptor, typeParameters))
|
||||
serializerDescriptor.secondaryConstructors = secondaryCtors
|
||||
return serializerDescriptor
|
||||
}
|
||||
|
||||
fun generateSerializerProperties(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
fromSupertypes: ArrayList<PropertyDescriptor>,
|
||||
name: Name,
|
||||
result: MutableSet<PropertyDescriptor>
|
||||
) {
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
|
||||
|
||||
// Do not auto-generate anything for user serializers
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return
|
||||
|
||||
if (name == SerialEntityNames.SERIAL_DESC_FIELD_NAME && result.none(thisDescriptor::checkSerializableClassPropertyResult) &&
|
||||
fromSupertypes.none { thisDescriptor.checkSerializableClassPropertyResult(it) && it.modality == Modality.FINAL }
|
||||
) {
|
||||
result.add(createSerializableClassPropertyDescriptor(thisDescriptor, classDescriptor))
|
||||
}
|
||||
// don't add local serializer fields if typed constructor is not synthetic
|
||||
if (classDescriptor.declaredTypeParameters.isNotEmpty() &&
|
||||
findSerializerConstructorForTypeArgumentsSerializers(thisDescriptor, onlyIfSynthetic = true) != null
|
||||
) {
|
||||
result.addAll(createLocalSerializersFieldsDescriptor(name, classDescriptor, thisDescriptor))
|
||||
}
|
||||
}
|
||||
|
||||
fun generateCompanionObjectMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
val classDescriptor = getSerializableClassDescriptorByCompanion(thisDescriptor) ?: return
|
||||
|
||||
if (name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == classDescriptor.declaredTypeParameters.size }) {
|
||||
result.add(createSerializerGetterDescriptor(thisDescriptor, classDescriptor))
|
||||
}
|
||||
|
||||
if (thisDescriptor.needSerializerFactory() && name == SerialEntityNames.SERIALIZER_PROVIDER_NAME && result.none { it.valueParameters.size == 1 && it.valueParameters.first().isVararg }) {
|
||||
result.add(createSerializerFactoryVarargDescriptor(thisDescriptor))
|
||||
}
|
||||
}
|
||||
|
||||
fun generateSerializerMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
fromSupertypes: List<SimpleFunctionDescriptor>,
|
||||
name: Name,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
val classDescriptor = getSerializableClassDescriptorBySerializer(thisDescriptor) ?: return
|
||||
|
||||
// Do not auto-generate anything for user serializers
|
||||
if (!isAllowedToHaveAutoGeneratedSerializerMethods(thisDescriptor, classDescriptor)) return
|
||||
|
||||
fun shouldAddSerializerFunction(checkParameters: (FunctionDescriptor) -> Boolean): Boolean {
|
||||
// Add 'save' / 'load' iff there is no such declared member AND there is no such final member in supertypes
|
||||
return result.none(checkParameters) &&
|
||||
fromSupertypes.none { checkParameters(it) && it.modality == Modality.FINAL }
|
||||
}
|
||||
|
||||
val isSave = name == SerialEntityNames.SAVE_NAME &&
|
||||
shouldAddSerializerFunction { classDescriptor.checkSaveMethodParameters(it.valueParameters) }
|
||||
val isLoad = name == SerialEntityNames.LOAD_NAME &&
|
||||
shouldAddSerializerFunction { classDescriptor.checkLoadMethodParameters(it.valueParameters) }
|
||||
val isDescriptorGetter = name == SerialEntityNames.CHILD_SERIALIZERS_GETTER &&
|
||||
thisDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) &&
|
||||
shouldAddSerializerFunction { true /* TODO? */ }
|
||||
|
||||
val isTypeParamsSerializersGetter = name == SerialEntityNames.TYPE_PARAMS_SERIALIZERS_GETTER &&
|
||||
thisDescriptor.typeConstructor.supertypes.any(::isGeneratedKSerializer) &&
|
||||
classDescriptor.declaredTypeParameters.isNotEmpty() &&
|
||||
shouldAddSerializerFunction { true /* TODO? */ }
|
||||
|
||||
if (isSave || isLoad || isDescriptorGetter || isTypeParamsSerializersGetter) {
|
||||
result.add(doCreateSerializerFunction(thisDescriptor, name))
|
||||
}
|
||||
}
|
||||
|
||||
fun generateSerializableClassMethods(thisDescriptor: ClassDescriptor, name: Name, result: MutableCollection<SimpleFunctionDescriptor>) {
|
||||
if (thisDescriptor.isInternalSerializable && name == SerialEntityNames.WRITE_SELF_NAME)
|
||||
result.add(createWriteSelfFunctionDescriptor(thisDescriptor))
|
||||
}
|
||||
|
||||
private fun createSerializableClassPropertyDescriptor(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
serializableClassDescriptor: ClassDescriptor
|
||||
): PropertyDescriptor {
|
||||
val typeParam = listOf(createProjection(serializableClassDescriptor.defaultType, Variance.INVARIANT, null))
|
||||
val propertyFromSerializer = thisDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam)
|
||||
.getContributedVariables(SerialEntityNames.SERIAL_DESC_FIELD_NAME, NoLookupLocation.FROM_BUILTINS).single()
|
||||
val result = doCreateSerializerProperty(
|
||||
thisDescriptor,
|
||||
SerialEntityNames.SERIAL_DESC_FIELD_NAME,
|
||||
propertyFromSerializer.type,
|
||||
propertyFromSerializer.typeParameters,
|
||||
DescriptorVisibilities.PUBLIC,
|
||||
Modality.OPEN // TODO: it was historically OPEN, but I do not see the reasons not to change to FINAL
|
||||
)
|
||||
result.overriddenDescriptors = listOf(propertyFromSerializer)
|
||||
return result
|
||||
}
|
||||
|
||||
private fun doCreateSerializerProperty(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
type: KotlinType,
|
||||
typeParameters: List<TypeParameterDescriptor> = emptyList(),
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE,
|
||||
modality: Modality = Modality.FINAL,
|
||||
needBackingField: Boolean = false
|
||||
): PropertyDescriptor {
|
||||
val propertyDescriptor = PropertyDescriptorImpl.create(
|
||||
thisDescriptor, Annotations.EMPTY, modality, visibility, false, name,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED, thisDescriptor.source, false, false, false, false, false, false
|
||||
)
|
||||
|
||||
val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call
|
||||
propertyDescriptor.setType(
|
||||
type,
|
||||
typeParameters,
|
||||
thisDescriptor.thisAsReceiverParameter,
|
||||
extensionReceiverParameter,
|
||||
emptyList()
|
||||
)
|
||||
|
||||
val propertyGetter = PropertyGetterDescriptorImpl(
|
||||
propertyDescriptor, Annotations.EMPTY, modality, visibility, false, false, false,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED, null, thisDescriptor.source
|
||||
)
|
||||
propertyGetter.initialize(type)
|
||||
|
||||
val backingField = if (needBackingField) FieldDescriptorImpl(Annotations.EMPTY, propertyDescriptor) else null
|
||||
propertyDescriptor.initialize(propertyGetter, null, backingField, null)
|
||||
return propertyDescriptor
|
||||
}
|
||||
|
||||
private fun doCreateSerializerFunction(
|
||||
companionDescriptor: ClassDescriptor,
|
||||
name: Name
|
||||
): SimpleFunctionDescriptor {
|
||||
val functionDescriptor = SimpleFunctionDescriptorImpl.create(
|
||||
companionDescriptor, Annotations.EMPTY, name, CallableMemberDescriptor.Kind.SYNTHESIZED, companionDescriptor.source
|
||||
)
|
||||
|
||||
val serializableClassOnImplSite = extractKSerializerArgumentFromImplementation(companionDescriptor)
|
||||
?: throw AssertionError("Serializer does not implement ${SerialEntityNames.KSERIALIZER_CLASS}??")
|
||||
|
||||
val typeParam = listOf(createProjection(serializableClassOnImplSite, Variance.INVARIANT, null))
|
||||
val functionFromSerializer = companionDescriptor.getGeneratedSerializerDescriptor().getMemberScope(typeParam)
|
||||
.getContributedFunctions(name, NoLookupLocation.FROM_BUILTINS).single()
|
||||
|
||||
functionDescriptor.initialize(
|
||||
null,
|
||||
companionDescriptor.thisAsReceiverParameter,
|
||||
emptyList(),
|
||||
functionFromSerializer.typeParameters,
|
||||
functionFromSerializer.valueParameters.map { it.copy(functionDescriptor, it.name, it.index) },
|
||||
functionFromSerializer.returnType,
|
||||
Modality.OPEN,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
return functionDescriptor
|
||||
}
|
||||
|
||||
fun createValPropertyDescriptor(
|
||||
name: Name,
|
||||
containingClassDescriptor: ClassDescriptor,
|
||||
type: KotlinType,
|
||||
visibility: DescriptorVisibility = DescriptorVisibilities.PRIVATE,
|
||||
createGetter: Boolean = false
|
||||
): PropertyDescriptor {
|
||||
val propertyDescriptor = PropertyDescriptorImpl.create(
|
||||
containingClassDescriptor,
|
||||
Annotations.EMPTY, Modality.FINAL, visibility, false, name,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED, containingClassDescriptor.source, false, false, false, false, false, false
|
||||
)
|
||||
val extensionReceiverParameter: ReceiverParameterDescriptor? = null // kludge to disambiguate call
|
||||
propertyDescriptor.setType(
|
||||
type,
|
||||
emptyList(), // no need type parameters?
|
||||
containingClassDescriptor.thisAsReceiverParameter,
|
||||
extensionReceiverParameter,
|
||||
emptyList()
|
||||
)
|
||||
|
||||
val propertyGetter: PropertyGetterDescriptorImpl? = if (createGetter) {
|
||||
PropertyGetterDescriptorImpl(
|
||||
propertyDescriptor, Annotations.EMPTY, Modality.FINAL, visibility, false, false, false,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED, null, containingClassDescriptor.source
|
||||
).apply { initialize(type) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
propertyDescriptor.initialize(propertyGetter, null)
|
||||
|
||||
return propertyDescriptor
|
||||
}
|
||||
|
||||
fun createLoadConstructorDescriptor(
|
||||
classDescriptor: ClassDescriptor,
|
||||
bindingContext: BindingContext,
|
||||
metadataPlugin: SerializationDescriptorSerializerPlugin?
|
||||
): ClassConstructorDescriptor {
|
||||
if (!classDescriptor.isInternalSerializable) throw IllegalArgumentException()
|
||||
|
||||
val functionDescriptor = ClassConstructorDescriptorImpl.createSynthesized(
|
||||
classDescriptor,
|
||||
Annotations.create(listOf(createDeprecatedHiddenAnnotation(classDescriptor.module))),
|
||||
false,
|
||||
SourceElement.NO_SOURCE
|
||||
)
|
||||
|
||||
val markerDesc = classDescriptor.getKSerializerConstructorMarker()
|
||||
val markerType = markerDesc.toSimpleType(nullable = true)
|
||||
|
||||
val serializableProperties = bindingContext.serializablePropertiesFor(classDescriptor, metadataPlugin).serializableProperties
|
||||
val parameterDescsAsProps = serializableProperties.map { it.descriptor }
|
||||
val bitMaskSlotsCount = serializableProperties.bitMaskSlotCount()
|
||||
var i = 0
|
||||
val consParams = mutableListOf<ValueParameterDescriptor>()
|
||||
repeat(bitMaskSlotsCount) {
|
||||
consParams.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
functionDescriptor, null, i++, Annotations.EMPTY, Name.identifier("seen$i"), functionDescriptor.builtIns.intType, false,
|
||||
false, false, null, functionDescriptor.source
|
||||
)
|
||||
)
|
||||
}
|
||||
for (prop in parameterDescsAsProps) {
|
||||
consParams.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
functionDescriptor, null, i++, prop.annotations, prop.name, prop.type.makeNullableIfNotPrimitive(), false, false,
|
||||
false, null, functionDescriptor.source
|
||||
)
|
||||
)
|
||||
}
|
||||
consParams.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
functionDescriptor, null, i, Annotations.EMPTY, SerialEntityNames.dummyParamName, markerType, false,
|
||||
false, false, null, functionDescriptor.source
|
||||
)
|
||||
)
|
||||
|
||||
functionDescriptor.initialize(
|
||||
consParams,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
functionDescriptor.returnType = classDescriptor.defaultType
|
||||
return functionDescriptor
|
||||
}
|
||||
|
||||
private fun createTypedSerializerConstructorDescriptor(
|
||||
classDescriptor: ClassDescriptor,
|
||||
serializableDescriptor: ClassDescriptor,
|
||||
typeParameters: List<TypeParameterDescriptor>
|
||||
): ClassConstructorDescriptor {
|
||||
val constrDesc = ClassConstructorDescriptorImpl.createSynthesized(
|
||||
classDescriptor,
|
||||
Annotations.create(listOf(createDeprecatedHiddenAnnotation(classDescriptor.module))),
|
||||
false,
|
||||
classDescriptor.source
|
||||
)
|
||||
val serializerClass = classDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
assert(serializableDescriptor.declaredTypeParameters.size == typeParameters.size)
|
||||
val args = List(serializableDescriptor.declaredTypeParameters.size) { index ->
|
||||
val pType = KotlinTypeFactory.simpleNotNullType(
|
||||
TypeAttributes.Empty,
|
||||
serializerClass,
|
||||
listOf(TypeProjectionImpl(typeParameters[index].defaultType))
|
||||
)
|
||||
|
||||
ValueParameterDescriptorImpl(
|
||||
constrDesc, null, index, Annotations.EMPTY, Name.identifier("$typeArgPrefix$index"), pType,
|
||||
false, false, false, null, constrDesc.source
|
||||
)
|
||||
}
|
||||
|
||||
constrDesc.initialize(args, DescriptorVisibilities.PUBLIC, typeParameters)
|
||||
constrDesc.returnType = classDescriptor.defaultType
|
||||
return constrDesc
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates free type parameters T0, T1, ... for given serializable class
|
||||
* Returns [T0, T1, ...] and [KSerializer<T0>, KSerializer<T1>,...]
|
||||
*/
|
||||
private fun createKSerializerParamsForEachGenericArgument(
|
||||
parentFunction: FunctionDescriptor,
|
||||
serializableClass: ClassDescriptor,
|
||||
actualArgsOffset: Int = 0
|
||||
): Pair<List<TypeParameterDescriptor>, List<ValueParameterDescriptor>> {
|
||||
val serializerClass = serializableClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
val args = mutableListOf<ValueParameterDescriptor>()
|
||||
val typeArgs = mutableListOf<TypeParameterDescriptor>()
|
||||
var i = 0
|
||||
|
||||
serializableClass.declaredTypeParameters.forEach { _ ->
|
||||
val targ = TypeParameterDescriptorImpl.createWithDefaultBound(
|
||||
parentFunction, Annotations.EMPTY, false, Variance.INVARIANT,
|
||||
Name.identifier("T$i"), i, LockBasedStorageManager.NO_LOCKS
|
||||
)
|
||||
|
||||
val pType =
|
||||
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializerClass, listOf(TypeProjectionImpl(targ.defaultType)))
|
||||
|
||||
args.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
containingDeclaration = parentFunction,
|
||||
original = null,
|
||||
index = actualArgsOffset + i,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier("$typeArgPrefix$i"),
|
||||
outType = pType,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = parentFunction.source
|
||||
)
|
||||
)
|
||||
|
||||
typeArgs.add(targ)
|
||||
i++
|
||||
}
|
||||
|
||||
return typeArgs to args
|
||||
}
|
||||
|
||||
private fun createSerializerFactoryVarargDescriptor(thisClass: ClassDescriptor): SimpleFunctionDescriptor {
|
||||
val f = SimpleFunctionDescriptorImpl.create(
|
||||
thisClass,
|
||||
Annotations.EMPTY,
|
||||
SerialEntityNames.SERIALIZER_PROVIDER_NAME,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
thisClass.source
|
||||
)
|
||||
val serializerClass = thisClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
|
||||
val kSerializerStarType =
|
||||
KotlinTypeFactory.simpleNotNullType(
|
||||
TypeAttributes.Empty,
|
||||
serializerClass,
|
||||
listOf(StarProjectionImpl(serializerClass.typeConstructor.parameters.first()))
|
||||
)
|
||||
|
||||
val varargType = thisClass.builtIns.getArrayType(Variance.OUT_VARIANCE, kSerializerStarType)
|
||||
|
||||
val vararg = ValueParameterDescriptorImpl(
|
||||
containingDeclaration = f,
|
||||
original = null,
|
||||
index = 0,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier("typeParamsSerializers"),
|
||||
outType = varargType,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = kSerializerStarType,
|
||||
source = f.source
|
||||
)
|
||||
|
||||
f.initialize(
|
||||
null,
|
||||
thisClass.thisAsReceiverParameter,
|
||||
emptyList(),
|
||||
listOf(),
|
||||
listOf(vararg),
|
||||
kSerializerStarType,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
return f
|
||||
}
|
||||
|
||||
private fun createSerializerGetterDescriptor(
|
||||
thisClass: ClassDescriptor,
|
||||
serializableClass: ClassDescriptor
|
||||
): SimpleFunctionDescriptor {
|
||||
val f = SimpleFunctionDescriptorImpl.create(
|
||||
thisClass,
|
||||
Annotations.EMPTY,
|
||||
SerialEntityNames.SERIALIZER_PROVIDER_NAME,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
thisClass.source
|
||||
)
|
||||
val serializerClass = thisClass.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
|
||||
val (typeArgs, args) = createKSerializerParamsForEachGenericArgument(f, serializableClass)
|
||||
|
||||
val newSerializableType =
|
||||
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializableClass, typeArgs.map { TypeProjectionImpl(it.defaultType) })
|
||||
val serialReturnType =
|
||||
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, serializerClass, listOf(TypeProjectionImpl(newSerializableType)))
|
||||
|
||||
f.initialize(null, thisClass.thisAsReceiverParameter, emptyList(), typeArgs, args, serialReturnType, Modality.FINAL, DescriptorVisibilities.PUBLIC)
|
||||
return f
|
||||
}
|
||||
|
||||
|
||||
private fun KotlinType.makeNullableIfNotPrimitive() =
|
||||
if (KotlinBuiltIns.isPrimitiveType(this)) this
|
||||
else this.makeNullable()
|
||||
|
||||
fun createWriteSelfFunctionDescriptor(thisClass: ClassDescriptor): SimpleFunctionDescriptor {
|
||||
val jvmStaticClass = thisClass.module.findClassAcrossModuleDependencies(StandardClassIds.Annotations.JvmStatic)!!
|
||||
val jvmStaticAnnotation = AnnotationDescriptorImpl(jvmStaticClass.defaultType, mapOf(), jvmStaticClass.source)
|
||||
val annotations = Annotations.create(listOf(jvmStaticAnnotation))
|
||||
|
||||
val f = SimpleFunctionDescriptorImpl.create(
|
||||
thisClass,
|
||||
annotations,
|
||||
SerialEntityNames.WRITE_SELF_NAME,
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
thisClass.source
|
||||
)
|
||||
val returnType = f.builtIns.unitType
|
||||
|
||||
val (typeArgs, argsKSer) = createKSerializerParamsForEachGenericArgument(f, thisClass, actualArgsOffset = 3)
|
||||
|
||||
val args = mutableListOf<ValueParameterDescriptor>()
|
||||
|
||||
// object
|
||||
val objectType =
|
||||
KotlinTypeFactory.simpleNotNullType(TypeAttributes.Empty, thisClass, typeArgs.map { TypeProjectionImpl(it.defaultType) })
|
||||
args.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
containingDeclaration = f,
|
||||
original = null,
|
||||
index = 0,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier("self"),
|
||||
outType = objectType,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = f.source
|
||||
)
|
||||
)
|
||||
|
||||
// encoder
|
||||
args.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
containingDeclaration = f,
|
||||
original = null,
|
||||
index = 1,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier("output"),
|
||||
outType = thisClass.getClassFromSerializationPackage(SerialEntityNames.STRUCTURE_ENCODER_CLASS).toSimpleType(false),
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = f.source
|
||||
)
|
||||
)
|
||||
|
||||
//descriptor
|
||||
args.add(
|
||||
ValueParameterDescriptorImpl(
|
||||
containingDeclaration = f,
|
||||
original = null,
|
||||
index = 2,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier("serialDesc"),
|
||||
outType = thisClass.getClassFromSerializationPackage(SerialEntityNames.SERIAL_DESCRIPTOR_CLASS).toSimpleType(false),
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = f.source
|
||||
)
|
||||
)
|
||||
|
||||
args.addAll(argsKSer)
|
||||
|
||||
f.initialize(
|
||||
null,
|
||||
null,
|
||||
emptyList(),
|
||||
typeArgs,
|
||||
args,
|
||||
returnType,
|
||||
Modality.FINAL,
|
||||
DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
fun generateDescriptorsForAnnotationImpl(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
fromSupertypes: List<PropertyDescriptor>,
|
||||
result: MutableCollection<PropertyDescriptor>
|
||||
) {
|
||||
if (isSerialInfoImpl(thisDescriptor)) {
|
||||
result.add(
|
||||
fromSupertypes.first().newCopyBuilder().apply {
|
||||
setOwner(thisDescriptor)
|
||||
setModality(Modality.FINAL)
|
||||
setKind(CallableMemberDescriptor.Kind.SYNTHESIZED)
|
||||
setDispatchReceiverParameter(thisDescriptor.thisAsReceiverParameter)
|
||||
}.build()!!
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// create properties typeSerial0, typeSerial1, etc... for storing generic arguments' serializers
|
||||
private fun createLocalSerializersFieldsDescriptor(
|
||||
name: Name,
|
||||
serializableDescriptor: ClassDescriptor,
|
||||
serializerDescriptor: ClassDescriptor
|
||||
): List<PropertyDescriptor> {
|
||||
if (serializableDescriptor.declaredTypeParameters.isEmpty()) return emptyList()
|
||||
val serializerClass = serializableDescriptor.getClassFromSerializationPackage(SerialEntityNames.KSERIALIZER_CLASS)
|
||||
val index = name.identifier.removePrefix(typeArgPrefix).toIntOrNull() ?: return emptyList()
|
||||
val param = serializerDescriptor.declaredTypeParameters[index]
|
||||
val pType =
|
||||
KotlinTypeFactory.simpleNotNullType(
|
||||
TypeAttributes.Empty,
|
||||
serializerClass,
|
||||
listOf(TypeProjectionImpl(param.defaultType))
|
||||
)
|
||||
val desc = doCreateSerializerProperty(serializerDescriptor, Name.identifier("$typeArgPrefix$index"), pType, needBackingField = true)
|
||||
return listOf(desc)
|
||||
}
|
||||
}
|
||||
+137
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
* 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.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.resolve.constants.KClassValue
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.classId
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.lazy.descriptors.LazyAnnotationDescriptor
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.KotlinTypeFactory
|
||||
import org.jetbrains.kotlin.types.TypeAttributes
|
||||
|
||||
fun ClassConstructorDescriptor.isSerializationCtor(): Boolean {
|
||||
/*kind == CallableMemberDescriptor.Kind.SYNTHESIZED does not work because DeserializedClassConstructorDescriptor loses its kind*/
|
||||
return valueParameters.lastOrNull()?.run {
|
||||
name == SerialEntityNames.dummyParamName && type.constructor.declarationDescriptor?.classId == ClassId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
SerialEntityNames.SERIAL_CTOR_MARKER_NAME
|
||||
)
|
||||
} == true
|
||||
}
|
||||
|
||||
// finds constructor (KSerializer<T0>, KSerializer<T1>...) on a KSerializer<T<T0, T1...>>
|
||||
fun findSerializerConstructorForTypeArgumentsSerializers(
|
||||
serializerDescriptor: ClassDescriptor,
|
||||
onlyIfSynthetic: Boolean = false
|
||||
): ClassConstructorDescriptor? {
|
||||
val serializableImplementationTypeArguments = extractKSerializerArgumentFromImplementation(serializerDescriptor)?.arguments
|
||||
?: throw AssertionError("Serializer does not implement KSerializer??")
|
||||
|
||||
val typeParamsCount = serializableImplementationTypeArguments.size
|
||||
if (typeParamsCount == 0) return null //don't need it
|
||||
val ctor = serializerDescriptor.constructors.find { ctor ->
|
||||
ctor.valueParameters.size == typeParamsCount && ctor.valueParameters.all { isKSerializer(it.type) }
|
||||
}
|
||||
return if (!onlyIfSynthetic) ctor else ctor?.takeIf { it.kind == CallableMemberDescriptor.Kind.SYNTHESIZED }
|
||||
}
|
||||
|
||||
fun AnnotationDescriptor.findAnnotationEntry(): KtAnnotationEntry? = (this as? LazyAnnotationDescriptor)?.annotationEntry
|
||||
|
||||
inline fun <reified R> Annotations.findAnnotationConstantValue(annotationFqName: FqName, property: String): R? =
|
||||
findAnnotation(annotationFqName)?.findConstantValue(property)
|
||||
|
||||
inline fun <reified R> AnnotationDescriptor.findConstantValue(property: String): R? =
|
||||
allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value?.value as? R
|
||||
|
||||
fun Annotations.findAnnotationKotlinTypeValue(
|
||||
annotationFqName: FqName,
|
||||
moduleForResolve: ModuleDescriptor,
|
||||
property: String
|
||||
): KotlinType? =
|
||||
findAnnotation(annotationFqName)?.let { annotation ->
|
||||
val maybeKClass = annotation.allValueArguments.entries.singleOrNull { it.key.asString() == property }?.value as? KClassValue
|
||||
maybeKClass?.getArgumentType(moduleForResolve)
|
||||
}
|
||||
|
||||
fun ClassDescriptor.getKSerializerConstructorMarker(): ClassDescriptor =
|
||||
module.findClassAcrossModuleDependencies(
|
||||
ClassId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
SerialEntityNames.SERIAL_CTOR_MARKER_NAME
|
||||
)
|
||||
)!!
|
||||
|
||||
fun ClassDescriptor.getKSerializer(): ClassDescriptor =
|
||||
module.findClassAcrossModuleDependencies(
|
||||
ClassId(
|
||||
SerializationPackages.packageFqName,
|
||||
SerialEntityNames.KSERIALIZER_NAME
|
||||
)
|
||||
)!!
|
||||
|
||||
fun getInternalPackageFqn(classSimpleName: String): FqName =
|
||||
SerializationPackages.internalPackageFqName.child(Name.identifier(classSimpleName))
|
||||
|
||||
fun ModuleDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
|
||||
requireNotNull(
|
||||
findClassAcrossModuleDependencies(
|
||||
ClassId(
|
||||
SerializationPackages.internalPackageFqName,
|
||||
Name.identifier(classSimpleName)
|
||||
)
|
||||
)
|
||||
) { "Can't locate class $classSimpleName from package ${SerializationPackages.internalPackageFqName}" }
|
||||
|
||||
fun ModuleDescriptor.getClassFromSerializationDescriptorsPackage(classSimpleName: String) =
|
||||
requireNotNull(
|
||||
findClassAcrossModuleDependencies(
|
||||
ClassId(
|
||||
SerializationPackages.descriptorsPackageFqName,
|
||||
Name.identifier(classSimpleName)
|
||||
)
|
||||
)
|
||||
) { "Can't locate class $classSimpleName from package ${SerializationPackages.descriptorsPackageFqName}" }
|
||||
|
||||
fun getSerializationPackageFqn(classSimpleName: String): FqName =
|
||||
SerializationPackages.packageFqName.child(Name.identifier(classSimpleName))
|
||||
|
||||
fun ModuleDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
|
||||
SerializationPackages.allPublicPackages.firstNotNullOfOrNull { pkg ->
|
||||
module.findClassAcrossModuleDependencies(ClassId(
|
||||
pkg,
|
||||
Name.identifier(classSimpleName)
|
||||
))
|
||||
} ?: throw IllegalArgumentException("Can't locate class $classSimpleName")
|
||||
|
||||
fun ClassDescriptor.getClassFromSerializationPackage(classSimpleName: String) =
|
||||
module.getClassFromSerializationPackage(classSimpleName)
|
||||
|
||||
fun ClassDescriptor.getClassFromInternalSerializationPackage(classSimpleName: String) =
|
||||
module.getClassFromInternalSerializationPackage(classSimpleName)
|
||||
|
||||
fun ClassDescriptor.toSimpleType(nullable: Boolean = false) =
|
||||
KotlinTypeFactory.simpleType(TypeAttributes.Empty, this.typeConstructor, emptyList(), nullable)
|
||||
|
||||
fun Annotated.annotationsWithArguments(): List<Triple<ClassDescriptor, List<ValueArgument>, List<ValueParameterDescriptor>>> =
|
||||
annotations.asSequence()
|
||||
.filter { it.type.toClassDescriptor?.isSerialInfoAnnotation == true }
|
||||
.filterIsInstance<LazyAnnotationDescriptor>()
|
||||
.mapNotNull { annDesc ->
|
||||
annDesc.type.toClassDescriptor?.let {
|
||||
Triple(it, annDesc.annotationEntry.valueArguments, it.unsubstitutedPrimaryConstructor?.valueParameters.orEmpty())
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
+169
@@ -0,0 +1,169 @@
|
||||
/*
|
||||
* 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.descriptors.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.KtDeclarationWithInitializer
|
||||
import org.jetbrains.kotlin.psi.KtParameter
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||
import org.jetbrains.kotlin.resolve.hasBackingField
|
||||
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedPropertyDescriptor
|
||||
import org.jetbrains.kotlin.serialization.deserialization.getName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROPERTIES
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationDescriptorSerializerPlugin
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginMetadataExtensions
|
||||
|
||||
interface ISerializableProperties<S : ISerializableProperty> {
|
||||
val serializableProperties: List<S>
|
||||
val isExternallySerializable: Boolean
|
||||
val serializableConstructorProperties: List<S>
|
||||
val serializableStandaloneProperties: List<S>
|
||||
}
|
||||
|
||||
class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) :
|
||||
ISerializableProperties<SerializableProperty> {
|
||||
private val primaryConstructorParameters: List<ValueParameterDescriptor> =
|
||||
serializableClass.unsubstitutedPrimaryConstructor?.valueParameters ?: emptyList()
|
||||
|
||||
override val serializableProperties: List<SerializableProperty>
|
||||
override val isExternallySerializable: Boolean
|
||||
private val primaryConstructorProperties: Map<PropertyDescriptor, Boolean>
|
||||
|
||||
init {
|
||||
val descriptorsSequence = serializableClass.unsubstitutedMemberScope.getContributedDescriptors(DescriptorKindFilter.VARIABLES)
|
||||
.asSequence()
|
||||
// call to any BindingContext.get should be only AFTER MemberScope.getContributedDescriptors
|
||||
primaryConstructorProperties =
|
||||
primaryConstructorParameters.asSequence()
|
||||
.map { parameter -> bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, parameter] to parameter.declaresDefaultValue() }
|
||||
.mapNotNull { (a, b) -> if (a == null) null else a to b }
|
||||
.toMap()
|
||||
|
||||
fun isPropSerializable(it: PropertyDescriptor) =
|
||||
if (serializableClass.isInternalSerializable) !it.annotations.serialTransient
|
||||
else !DescriptorVisibilities.isPrivate(it.visibility) && ((it.isVar && !it.annotations.serialTransient) || primaryConstructorProperties.contains(
|
||||
it
|
||||
))
|
||||
|
||||
serializableProperties = descriptorsSequence.filterIsInstance<PropertyDescriptor>()
|
||||
.filter { it.kind == CallableMemberDescriptor.Kind.DECLARATION }
|
||||
.filter(::isPropSerializable)
|
||||
.map { prop ->
|
||||
val declaresDefaultValue = prop.declaresDefaultValue()
|
||||
SerializableProperty(
|
||||
prop,
|
||||
primaryConstructorProperties[prop] ?: false,
|
||||
prop.hasBackingField(bindingContext) || (prop is DeserializedPropertyDescriptor && prop.backingField != null) // workaround for TODO in .hasBackingField
|
||||
// workaround for overridden getter (val) and getter+setter (var) - in this case hasBackingField returning false
|
||||
// but initializer presents only for property with backing field
|
||||
|| declaresDefaultValue,
|
||||
declaresDefaultValue
|
||||
)
|
||||
}
|
||||
.filterNot { it.transient }
|
||||
.partition { primaryConstructorProperties.contains(it.descriptor) }
|
||||
.run {
|
||||
val supers = serializableClass.getSuperClassNotAny()
|
||||
if (supers == null || !supers.isInternalSerializable)
|
||||
first + second
|
||||
else
|
||||
SerializableProperties(supers, bindingContext).serializableProperties + first + second
|
||||
}
|
||||
.let { restoreCorrectOrderFromClassProtoExtension(serializableClass, it) }
|
||||
|
||||
isExternallySerializable =
|
||||
serializableClass.isInternallySerializableEnum() || primaryConstructorParameters.size == primaryConstructorProperties.size
|
||||
|
||||
}
|
||||
|
||||
override val serializableConstructorProperties: List<SerializableProperty> =
|
||||
serializableProperties.asSequence()
|
||||
.filter { primaryConstructorProperties.contains(it.descriptor) }
|
||||
.toList()
|
||||
|
||||
override val serializableStandaloneProperties: List<SerializableProperty> =
|
||||
serializableProperties.minus(serializableConstructorProperties)
|
||||
|
||||
val size = serializableProperties.size
|
||||
operator fun get(index: Int) = serializableProperties[index]
|
||||
operator fun iterator() = serializableProperties.iterator()
|
||||
|
||||
val primaryConstructorWithDefaults = serializableClass.unsubstitutedPrimaryConstructor
|
||||
?.original?.valueParameters?.any { it.declaresDefaultValue() } ?: false
|
||||
}
|
||||
|
||||
fun PropertyDescriptor.declaresDefaultValue(): Boolean {
|
||||
when (val declaration = this.source.getPsi()) {
|
||||
is KtDeclarationWithInitializer -> return declaration.initializer != null
|
||||
is KtParameter -> return declaration.defaultValue != null
|
||||
is Any -> return false // Not-null check
|
||||
}
|
||||
// PSI is null, property is from another module
|
||||
if (this !is DeserializedPropertyDescriptor) return false
|
||||
val myClassCtor = (this.containingDeclaration as? ClassDescriptor)?.unsubstitutedPrimaryConstructor ?: return false
|
||||
// If property is a constructor parameter, check parameter default value
|
||||
// (serializable classes always have parameters-as-properties, so no name clash here)
|
||||
if (myClassCtor.valueParameters.find { it.name == this.name }?.declaresDefaultValue() == true) return true
|
||||
// If it is a body property, then it is likely to have initializer when getter is not specified
|
||||
// note this approach is not working well if we have smth like `get() = field`, but such cases on cross-module boundaries
|
||||
// should be very marginal. If we want to solve them, we need to add protobuf metadata extension.
|
||||
if (getter?.isDefault == true) return true
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
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() = size / 32 + 1
|
||||
fun bitMaskSlotAt(propertyIndex: Int) = propertyIndex / 32
|
||||
|
||||
fun BindingContext.serializablePropertiesFor(
|
||||
classDescriptor: ClassDescriptor,
|
||||
serializationDescriptorSerializer: SerializationDescriptorSerializerPlugin? = null
|
||||
): SerializableProperties {
|
||||
val props = this.get(SERIALIZABLE_PROPERTIES, classDescriptor) ?: SerializableProperties(classDescriptor, this)
|
||||
serializationDescriptorSerializer?.putIfNeeded(classDescriptor, props)
|
||||
return props
|
||||
}
|
||||
|
||||
fun <P: ISerializableProperty> restoreCorrectOrderFromClassProtoExtension(descriptor: ClassDescriptor, props: List<P>): List<P> {
|
||||
if (descriptor !is DeserializedClassDescriptor) return props
|
||||
val correctOrder: List<Name> = descriptor.classProto.getExtension(SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder)
|
||||
.map { descriptor.c.nameResolver.getName(it) }
|
||||
val propsMap = props.associateBy { it.originalDescriptorName }
|
||||
return correctOrder.map { propsMap.getValue(it) }
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.resolve
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.analyzeSpecialSerializers
|
||||
|
||||
interface ISerializableProperty {
|
||||
val isConstructorParameterWithDefault: Boolean
|
||||
val name: String
|
||||
val originalDescriptorName: Name
|
||||
val optional: Boolean
|
||||
val transient: Boolean
|
||||
}
|
||||
|
||||
class SerializableProperty(
|
||||
val descriptor: PropertyDescriptor,
|
||||
override val isConstructorParameterWithDefault: Boolean,
|
||||
hasBackingField: Boolean,
|
||||
declaresDefaultValue: Boolean
|
||||
) : ISerializableProperty {
|
||||
override val name = descriptor.annotations.serialNameValue ?: descriptor.name.asString()
|
||||
override val originalDescriptorName: Name = descriptor.name
|
||||
val type = descriptor.type
|
||||
val genericIndex = type.genericIndex
|
||||
val module = descriptor.module
|
||||
val serializableWith = descriptor.serializableWith ?: analyzeSpecialSerializers(module, descriptor.annotations)?.defaultType
|
||||
override val optional = !descriptor.annotations.serialRequired && declaresDefaultValue
|
||||
override val transient = descriptor.annotations.serialTransient || !hasBackingField
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user