[Serialization] Reorganize module structure

This commit is contained in:
Dmitriy Novozhilov
2022-08-22 11:49:45 +03:00
parent 0a8cefc8a5
commit cc00dcc038
150 changed files with 493 additions and 322 deletions
@@ -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;
}
@@ -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>()
}
@@ -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
)
}
@@ -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()
@@ -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))
@@ -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);
}
};
}
@@ -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
@@ -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
)
}
}
@@ -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
}
}
@@ -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)
}
}
@@ -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)
}
@@ -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)
}
}
@@ -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
@@ -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)
}
}
@@ -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()
@@ -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) }
}
@@ -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
}