[Serialization] Implement serialization checker for K2 version of plugin
^KT-53178 Fixed
This commit is contained in:
committed by
teamcity
parent
e048ffcf6d
commit
671083c701
@@ -372,6 +372,7 @@ val projectsWithEnabledContextReceivers by extra {
|
||||
":compiler:frontend.common",
|
||||
":compiler:fir:fir2ir",
|
||||
":kotlin-lombok-compiler-plugin.k1",
|
||||
":kotlinx-serialization-compiler-plugin.k2",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -114,7 +114,7 @@ fun FirTypeRef.toRegularClassSymbol(session: FirSession): FirRegularClassSymbol?
|
||||
* or null if no proper declaration has been found.
|
||||
*/
|
||||
fun FirDeclaration.getContainingClassSymbol(session: FirSession): FirClassLikeSymbol<*>? =
|
||||
this.safeAs<FirCallableDeclaration>()?.containingClass()?.toSymbol(session)
|
||||
(this as? FirCallableDeclaration)?.containingClass()?.toSymbol(session)
|
||||
|
||||
@OptIn(SymbolInternals::class)
|
||||
fun FirBasedSymbol<*>.getContainingClassSymbol(session: FirSession): FirClassLikeSymbol<*>? = fir.getContainingClassSymbol(session)
|
||||
|
||||
-11
@@ -23,7 +23,6 @@ import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isLocal
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.resolve.toSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
@@ -223,14 +222,4 @@ object FirAnnotationClassDeclarationChecker : FirRegularClassChecker() {
|
||||
return annotationHasCycle(referencedAnnotation)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun FirRegularClassSymbol.primaryConstructorSymbol(): FirConstructorSymbol? {
|
||||
for (declarationSymbol in this.declarationSymbols) {
|
||||
if (declarationSymbol is FirConstructorSymbol && declarationSymbol.isPrimary) {
|
||||
return declarationSymbol
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -101,3 +101,12 @@ internal val FirCallableDeclaration.isExtensionMember: Boolean
|
||||
|
||||
internal val FirCallableSymbol<*>.isExtensionMember: Boolean
|
||||
get() = resolvedReceiverTypeRef != null && dispatchReceiverType != null
|
||||
|
||||
fun FirClassSymbol<*>.primaryConstructorSymbol(): FirConstructorSymbol? {
|
||||
for (declarationSymbol in this.declarationSymbols) {
|
||||
if (declarationSymbol is FirConstructorSymbol && declarationSymbol.isPrimary) {
|
||||
return declarationSymbol
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
+3
@@ -101,6 +101,9 @@ object FirDiagnosticRenderers {
|
||||
t.renderReadableWithFqNames()
|
||||
}
|
||||
|
||||
// TODO: properly implement
|
||||
val RENDER_TYPE_WITH_ANNOTATIONS = RENDER_TYPE
|
||||
|
||||
val FQ_NAMES_IN_TYPES = Renderer { symbol: FirBasedSymbol<*> ->
|
||||
@OptIn(SymbolInternals::class)
|
||||
FirRenderer(
|
||||
|
||||
@@ -8,18 +8,28 @@ package org.jetbrains.kotlin.fir.types
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
|
||||
val ConeKotlinType.isByte: Boolean get() = isBuiltinType(StandardClassIds.Byte, false)
|
||||
val ConeKotlinType.isShort: Boolean get() = isBuiltinType(StandardClassIds.Short, false)
|
||||
val ConeKotlinType.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false)
|
||||
val ConeKotlinType.isLong: Boolean get() = isBuiltinType(StandardClassIds.Long, false)
|
||||
val ConeKotlinType.isFloat: Boolean get() = isBuiltinType(StandardClassIds.Float, false)
|
||||
val ConeKotlinType.isDouble: Boolean get() = isBuiltinType(StandardClassIds.Double, false)
|
||||
|
||||
val ConeKotlinType.isAny: Boolean get() = isBuiltinType(StandardClassIds.Any, false)
|
||||
val ConeKotlinType.isNullableAny: Boolean get() = isBuiltinType(StandardClassIds.Any, true)
|
||||
val ConeKotlinType.isNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, false)
|
||||
val ConeKotlinType.isNullableNothing: Boolean get() = isBuiltinType(StandardClassIds.Nothing, true)
|
||||
|
||||
val ConeKotlinType.isUnit: Boolean get() = isBuiltinType(StandardClassIds.Unit, false)
|
||||
val ConeKotlinType.isBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, false)
|
||||
val ConeKotlinType.isNullableBoolean: Boolean get() = isBuiltinType(StandardClassIds.Boolean, true)
|
||||
val ConeKotlinType.isBooleanOrNullableBoolean: Boolean get() = isAnyOfBuiltinType(setOf(StandardClassIds.Boolean))
|
||||
val ConeKotlinType.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false)
|
||||
|
||||
val ConeKotlinType.isChar: Boolean get() = isBuiltinType(StandardClassIds.Char, false)
|
||||
val ConeKotlinType.isString: Boolean get() = isBuiltinType(StandardClassIds.String, false)
|
||||
val ConeKotlinType.isInt: Boolean get() = isBuiltinType(StandardClassIds.Int, false)
|
||||
val ConeKotlinType.isLong: Boolean get() = isBuiltinType(StandardClassIds.Long, false)
|
||||
|
||||
val ConeKotlinType.isEnum: Boolean get() = isBuiltinType(StandardClassIds.Enum, false)
|
||||
|
||||
val ConeKotlinType.isUInt: Boolean get() = isBuiltinType(StandardClassIds.UInt, false)
|
||||
val ConeKotlinType.isULong: Boolean get() = isBuiltinType(StandardClassIds.ULong, false)
|
||||
val ConeKotlinType.isPrimitiveOrNullablePrimitive: Boolean get() = isAnyOfBuiltinType(StandardClassIds.primitiveTypes)
|
||||
|
||||
+21
-7
@@ -16,10 +16,7 @@ import org.jetbrains.kotlin.fir.resolvedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.ConeClassLikeLookupTag
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirCallableSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeClassLikeType
|
||||
import org.jetbrains.kotlin.fir.types.FirResolvedTypeRef
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.fir.types.coneTypeSafe
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
@@ -99,11 +96,15 @@ private val DEFAULT_USE_SITE_TARGETS: Set<AnnotationUseSiteTarget> =
|
||||
USE_SITE_TARGET_NAME_MAP.values.fold(setOf<AnnotationUseSiteTarget>()) { a, b -> a + b } - setOf(AnnotationUseSiteTarget.FILE)
|
||||
|
||||
fun FirDeclaration.hasAnnotation(classId: ClassId): Boolean {
|
||||
return annotations.any { it.toAnnotationClassId() == classId }
|
||||
return annotations.hasAnnotation(classId)
|
||||
}
|
||||
|
||||
fun FirBasedSymbol<*>.hasAnnotation(classId: ClassId): Boolean {
|
||||
return resolvedAnnotationsWithClassIds.any { it.toAnnotationClassId() == classId }
|
||||
return resolvedAnnotationsWithClassIds.hasAnnotation(classId)
|
||||
}
|
||||
|
||||
fun List<FirAnnotation>.hasAnnotation(classId: ClassId): Boolean {
|
||||
return this.any { it.toAnnotationClassId() == classId }
|
||||
}
|
||||
|
||||
fun <D> FirBasedSymbol<out D>.getAnnotationByClassId(classId: ClassId): FirAnnotation? where D : FirAnnotationContainer, D : FirDeclaration {
|
||||
@@ -111,7 +112,11 @@ fun <D> FirBasedSymbol<out D>.getAnnotationByClassId(classId: ClassId): FirAnnot
|
||||
}
|
||||
|
||||
fun FirAnnotationContainer.getAnnotationByClassId(classId: ClassId): FirAnnotation? {
|
||||
return annotations.find {
|
||||
return annotations.getAnnotationByClassId(classId)
|
||||
}
|
||||
|
||||
fun List<FirAnnotation>.getAnnotationByClassId(classId: ClassId): FirAnnotation? {
|
||||
return find {
|
||||
it.annotationTypeRef.coneTypeSafe<ConeClassLikeType>()?.lookupTag?.classId == classId
|
||||
}
|
||||
}
|
||||
@@ -173,6 +178,15 @@ private inline fun <reified T> FirAnnotation.getPrimitiveArgumentValue(name: Nam
|
||||
}
|
||||
}
|
||||
|
||||
fun FirAnnotation.getKClassArgument(name: Name): ConeKotlinType? {
|
||||
val argument = findArgumentByName(name) as? FirGetClassCall ?: return null
|
||||
return argument.getTargetType()
|
||||
}
|
||||
|
||||
fun FirGetClassCall.getTargetType(): ConeKotlinType? {
|
||||
return typeRef.coneType.typeArguments.getOrNull(0)?.type
|
||||
}
|
||||
|
||||
fun FirAnnotationContainer.getJvmNameFromAnnotation(target: AnnotationUseSiteTarget? = null): String? {
|
||||
val annotationCalls = getAnnotationsByClassId(StandardClassIds.Annotations.JvmName)
|
||||
return annotationCalls.firstNotNullOfOrNull { call ->
|
||||
|
||||
+3
-2
@@ -32,12 +32,13 @@ fun FirClass.primaryConstructorIfAny(session: FirSession): FirConstructorSymbol?
|
||||
return constructors(session).find(FirConstructorSymbol::isPrimary)
|
||||
}
|
||||
|
||||
fun FirRegularClass.collectEnumEntries(): Collection<FirEnumEntry> {
|
||||
// TODO: dog shit, rewrite with scopes
|
||||
fun FirClass.collectEnumEntries(): Collection<FirEnumEntry> {
|
||||
assert(classKind == ClassKind.ENUM_CLASS)
|
||||
return declarations.filterIsInstance<FirEnumEntry>()
|
||||
}
|
||||
|
||||
fun FirRegularClassSymbol.collectEnumEntries(): Collection<FirEnumEntrySymbol> {
|
||||
fun FirClassSymbol<*>.collectEnumEntries(): Collection<FirEnumEntrySymbol> {
|
||||
return fir.collectEnumEntries().map { it.symbol }
|
||||
}
|
||||
|
||||
|
||||
+4
@@ -118,6 +118,10 @@ fun FirSession.declaredMemberScope(klass: FirClassSymbol<*>): FirContainingNames
|
||||
return declaredMemberScope(klass.fir)
|
||||
}
|
||||
|
||||
fun FirClassSymbol<*>.declaredMemberScope(session: FirSession): FirContainingNamesAwareScope {
|
||||
return session.declaredMemberScope(fir)
|
||||
}
|
||||
|
||||
fun FirSession.declaredMemberScopeWithLazyNestedScope(
|
||||
klass: FirClass,
|
||||
existingNames: List<Name>,
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
package org.jetbrains.kotlin.fir.caches
|
||||
|
||||
import kotlin.reflect.KProperty
|
||||
|
||||
abstract class FirCache<in K : Any, out V, in CONTEXT> {
|
||||
abstract fun getValue(key: K, context: CONTEXT): V
|
||||
abstract fun getValueIfComputed(key: K): V?
|
||||
@@ -24,6 +26,10 @@ class FirLazyValue<out V, in CONTEXT>(private val cache: FirCache<Unit, V, CONTE
|
||||
}
|
||||
}
|
||||
|
||||
operator fun <V> FirLazyValue<V, Nothing?>.getValue(thisRef: Any?, property: KProperty<*>): V {
|
||||
return getValue(context = null)
|
||||
}
|
||||
|
||||
@Suppress("NOTHING_TO_INLINE")
|
||||
inline fun <V> FirLazyValue<V, Nothing?>.getValue(): V {
|
||||
return getValue(null)
|
||||
|
||||
+25
@@ -10,7 +10,12 @@ import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyBackingField
|
||||
import org.jetbrains.kotlin.fir.declarations.impl.FirDefaultPropertyGetter
|
||||
import org.jetbrains.kotlin.fir.expressions.FirPropertyAccessExpression
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirPropertyFromParameterResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassLikeSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.fir.types.coneType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
@@ -27,6 +32,12 @@ var FirProperty.fromPrimaryConstructor: Boolean? by FirDeclarationDataRegistry.d
|
||||
var FirClassLikeDeclaration.sourceElement: SourceElement? by FirDeclarationDataRegistry.data(SourceElementKey)
|
||||
var FirRegularClass.moduleName: String? by FirDeclarationDataRegistry.data(ModuleNameKey)
|
||||
|
||||
val FirClassLikeSymbol<*>.sourceElement: SourceElement?
|
||||
get() = fir.sourceElement
|
||||
|
||||
val FirPropertySymbol.fromPrimaryConstructor: Boolean
|
||||
get() = fir.fromPrimaryConstructor ?: false
|
||||
|
||||
/**
|
||||
* Constraint without corresponding type argument
|
||||
*/
|
||||
@@ -93,6 +104,12 @@ val FirProperty.hasBackingField: Boolean
|
||||
}
|
||||
}
|
||||
|
||||
val FirPropertySymbol.hasBackingField: Boolean
|
||||
get() {
|
||||
lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
|
||||
return fir.hasBackingField
|
||||
}
|
||||
|
||||
fun FirDeclaration.getDanglingTypeConstraintsOrEmpty(): List<DanglingTypeConstraint> {
|
||||
return when (this) {
|
||||
is FirRegularClass -> danglingTypeConstraints
|
||||
@@ -101,3 +118,11 @@ fun FirDeclaration.getDanglingTypeConstraintsOrEmpty(): List<DanglingTypeConstra
|
||||
else -> null
|
||||
} ?: emptyList()
|
||||
}
|
||||
|
||||
val FirPropertySymbol.correspondingValueParameterFromPrimaryConstructor: FirValueParameterSymbol?
|
||||
get() {
|
||||
if (!fromPrimaryConstructor) return null
|
||||
val initializer = resolvedInitializer as? FirPropertyAccessExpression ?: return null
|
||||
val reference = initializer.calleeReference as? FirPropertyFromParameterResolvedNamedReference ?: return null
|
||||
return reference.resolvedSymbol as? FirValueParameterSymbol
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.fir.symbols.impl
|
||||
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.diagnostics.ConeDiagnostic
|
||||
import org.jetbrains.kotlin.fir.expressions.FirExpression
|
||||
import org.jetbrains.kotlin.fir.references.FirControlFlowGraphReference
|
||||
import org.jetbrains.kotlin.fir.symbols.lazyResolveToPhase
|
||||
import org.jetbrains.kotlin.name.CallableId
|
||||
@@ -39,6 +40,12 @@ open class FirPropertySymbol(
|
||||
val hasInitializer: Boolean
|
||||
get() = fir.initializer != null
|
||||
|
||||
val resolvedInitializer: FirExpression?
|
||||
get() {
|
||||
lazyResolveToPhase(FirResolvePhase.BODY_RESOLVE)
|
||||
return fir.initializer
|
||||
}
|
||||
|
||||
val hasDelegate: Boolean
|
||||
get() = fir.delegate != null
|
||||
|
||||
|
||||
+4
@@ -12,6 +12,7 @@ import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.deserialization.PLATFORM_DEPENDENT_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi.JVM_FIELD_ANNOTATION_FQ_NAME
|
||||
import org.jetbrains.kotlin.metadata.jvm.deserialization.JvmProtoBufUtil
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.serialization.deserialization.descriptors.DeserializedClassDescriptor
|
||||
@@ -38,6 +39,9 @@ val VOLATILE_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Volatile")
|
||||
@JvmField
|
||||
val TRANSIENT_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.Transient")
|
||||
|
||||
@JvmField
|
||||
val TRANSIENT_ANNOTATION_CLASS_ID = ClassId.topLevel(TRANSIENT_ANNOTATION_FQ_NAME)
|
||||
|
||||
@JvmField
|
||||
val JVM_SERIALIZABLE_LAMBDA_ANNOTATION_FQ_NAME = FqName("kotlin.jvm.JvmSerializableLambda")
|
||||
|
||||
|
||||
@@ -27,11 +27,17 @@ enum class ClassKind(val codeRepresentation: String?) {
|
||||
get() = this == OBJECT || this == ENUM_ENTRY
|
||||
}
|
||||
|
||||
inline val ClassKind.isClass: Boolean
|
||||
get() = this == ClassKind.CLASS
|
||||
|
||||
inline val ClassKind.isInterface: Boolean
|
||||
get() = this == ClassKind.INTERFACE
|
||||
|
||||
inline val ClassKind.isEnumClass: Boolean
|
||||
get() = this == ClassKind.ENUM_CLASS
|
||||
|
||||
inline val ClassKind.isClass: Boolean
|
||||
get() = this == ClassKind.CLASS
|
||||
inline val ClassKind.isAnnotationClass: Boolean
|
||||
get() = this == ClassKind.ANNOTATION_CLASS
|
||||
|
||||
inline val ClassKind.isObject: Boolean
|
||||
get() = this == ClassKind.OBJECT
|
||||
|
||||
@@ -86,6 +86,7 @@
|
||||
<arg>-Xjvm-default=enable</arg>
|
||||
<arg>-opt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI</arg>
|
||||
<arg>-opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi</arg>
|
||||
<arg>-Xcontext-receivers</arg>
|
||||
</args>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
+7
-2
@@ -34,9 +34,14 @@ import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.contextSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.enumSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.objectSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.polymorphicSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.referenceArraySerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.sealedSerializerId
|
||||
|
||||
abstract class BaseIrGenerator(private val currentClass: IrClass, final override val compilerContext: SerializationPluginContext) :
|
||||
IrBuilderWithPluginContext {
|
||||
@@ -581,4 +586,4 @@ abstract class BaseIrGenerator(private val currentClass: IrClass, final override
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+7
-7
@@ -16,13 +16,13 @@ import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.findStandardKotlinTypeSerializer
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.extensions.SerializationPluginContext
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.contextSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.enumSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.objectSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.polymorphicSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.referenceArraySerializerId
|
||||
|
||||
class IrSerialTypeInfo(
|
||||
val property: IrSerializableProperty,
|
||||
@@ -182,7 +182,7 @@ fun findStandardKotlinTypeSerializer(context: SerializationPluginContext, type:
|
||||
"D" -> if (type.isDouble()) "DoubleSerializer" else null
|
||||
"C" -> if (type.isChar()) "CharSerializer" else null
|
||||
null -> null
|
||||
else -> findStandardKotlinTypeSerializer(typeName)
|
||||
else -> findStandardKotlinTypeSerializerName(typeName)
|
||||
} ?: return null
|
||||
return context.getClassFromRuntimeOrNull(name, SerializationPackages.internalPackageFqName, SerializationPackages.packageFqName)
|
||||
}
|
||||
|
||||
+6
-1
@@ -24,8 +24,13 @@ import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isTypeParameter
|
||||
import org.jetbrains.kotlin.types.typeUtil.representativeUpperBound
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.common.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.backend.jvm.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.contextSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.enumSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.objectSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.polymorphicSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.referenceArraySerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.sealedSerializerId
|
||||
|
||||
internal class JsBlockBuilder {
|
||||
val block: JsBlock = JsBlock()
|
||||
|
||||
+6
-3
@@ -17,7 +17,6 @@ import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.load.java.JvmAbi
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.load.kotlin.internalName
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.psi.ValueArgument
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
@@ -57,8 +56,12 @@ import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.ST
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.STRUCTURE_ENCODER_CLASS
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.UNKNOWN_FIELD_EXC
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames.typeArgPrefix
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.packageFqName
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.contextSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.enumSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.objectSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.polymorphicSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.referenceArraySerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.sealedSerializerId
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
@@ -8,6 +8,7 @@ plugins {
|
||||
dependencies {
|
||||
compileOnly(project(":compiler:util"))
|
||||
compileOnly(project(":core:compiler.common"))
|
||||
compileOnly(project(":core:deserialization.common.jvm"))
|
||||
compileOnly(intellijCore())
|
||||
}
|
||||
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.diagnostic
|
||||
|
||||
import com.intellij.openapi.util.io.JarUtil
|
||||
import org.jetbrains.kotlin.config.ApiVersion
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
||||
import org.jetbrains.kotlin.load.kotlin.KotlinJvmBinarySourceElement
|
||||
import java.io.File
|
||||
import java.util.jar.Attributes
|
||||
|
||||
data class RuntimeVersions(val implementationVersion: ApiVersion?, val requireKotlinVersion: ApiVersion?) {
|
||||
companion object {
|
||||
val MINIMAL_SUPPORTED_VERSION = ApiVersion.parse("1.0-M1-SNAPSHOT")!!
|
||||
val MINIMAL_VERSION_FOR_INLINE_CLASSES = ApiVersion.parse("1.1-M1-SNAPSHOT")!!
|
||||
}
|
||||
|
||||
fun currentCompilerMatchRequired(): Boolean {
|
||||
val current = requireNotNull(KotlinCompilerVersion.getVersion()?.let(ApiVersion.Companion::parse))
|
||||
return requireKotlinVersion == null || requireKotlinVersion <= current
|
||||
}
|
||||
|
||||
fun implementationVersionMatchSupported(): Boolean {
|
||||
return implementationVersion != null && implementationVersion >= MINIMAL_SUPPORTED_VERSION
|
||||
}
|
||||
}
|
||||
|
||||
object CommonVersionReader {
|
||||
private val REQUIRE_KOTLIN_VERSION = Attributes.Name("Require-Kotlin-Version")
|
||||
private const val CLASS_SUFFIX = "!/kotlinx/serialization/KSerializer.class"
|
||||
|
||||
fun computeRuntimeVersions(sourceElement: SourceElement?): RuntimeVersions? {
|
||||
val location = (sourceElement as? KotlinJvmBinarySourceElement)?.binaryClass?.location ?: return null
|
||||
val jarFile = location.removeSuffix(CLASS_SUFFIX)
|
||||
if (!jarFile.endsWith(".jar")) return null
|
||||
val file = File(jarFile)
|
||||
if (!file.exists()) return null
|
||||
return getVersionsFromManifest(file)
|
||||
}
|
||||
|
||||
fun getVersionsFromManifest(runtimeLibraryPath: File): RuntimeVersions {
|
||||
val version = JarUtil.getJarAttribute(runtimeLibraryPath, Attributes.Name.IMPLEMENTATION_VERSION)?.let(ApiVersion.Companion::parse)
|
||||
val kotlinVersion = JarUtil.getJarAttribute(runtimeLibraryPath, REQUIRE_KOTLIN_VERSION)?.let(ApiVersion.Companion::parse)
|
||||
return RuntimeVersions(version, kotlinVersion)
|
||||
}
|
||||
|
||||
fun canSupportInlineClasses(currentVersion: RuntimeVersions?): Boolean {
|
||||
if (currentVersion == null) return true
|
||||
val implVersion = currentVersion.implementationVersion ?: return false
|
||||
return implVersion >= RuntimeVersions.MINIMAL_VERSION_FOR_INLINE_CLASSES
|
||||
}
|
||||
}
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.resolve
|
||||
|
||||
interface ISerializableProperties<S : ISerializableProperty> {
|
||||
val serializableProperties: List<S>
|
||||
val isExternallySerializable: Boolean
|
||||
val serializableConstructorProperties: List<S>
|
||||
val serializableStandaloneProperties: List<S>
|
||||
}
|
||||
|
||||
val ISerializableProperties<*>.goldenMask: Int
|
||||
get() {
|
||||
var goldenMask = 0
|
||||
var requiredBit = 1
|
||||
for (property in serializableProperties) {
|
||||
if (!property.optional) {
|
||||
goldenMask = goldenMask or requiredBit
|
||||
}
|
||||
requiredBit = requiredBit shl 1
|
||||
}
|
||||
return goldenMask
|
||||
}
|
||||
|
||||
val ISerializableProperties<*>.goldenMaskList: List<Int>
|
||||
get() {
|
||||
val maskSlotCount = serializableProperties.bitMaskSlotCount()
|
||||
val goldenMaskList = MutableList(maskSlotCount) { 0 }
|
||||
|
||||
for (i in serializableProperties.indices) {
|
||||
if (!serializableProperties[i].optional) {
|
||||
val slotNumber = i / 32
|
||||
val bitInSlot = i % 32
|
||||
goldenMaskList[slotNumber] = goldenMaskList[slotNumber] or (1 shl bitInSlot)
|
||||
}
|
||||
}
|
||||
return goldenMaskList
|
||||
}
|
||||
|
||||
fun List<ISerializableProperty>.bitMaskSlotCount(): Int = size / 32 + 1
|
||||
fun bitMaskSlotAt(propertyIndex: Int): Int = propertyIndex / 32
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.resolve
|
||||
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
|
||||
interface ISerializableProperty {
|
||||
val isConstructorParameterWithDefault: Boolean
|
||||
val name: String
|
||||
val originalDescriptorName: Name
|
||||
val optional: Boolean
|
||||
val transient: Boolean
|
||||
}
|
||||
+109
@@ -41,6 +41,22 @@ object SerializationAnnotations {
|
||||
val contextualOnPropertyFqName = FqName("kotlinx.serialization.Contextual")
|
||||
val polymorphicFqName = FqName("kotlinx.serialization.Polymorphic")
|
||||
val additionalSerializersFqName = FqName("kotlinx.serialization.UseSerializers")
|
||||
|
||||
val serializableAnnotationClassId = ClassId.topLevel(serializableAnnotationFqName)
|
||||
val serializerAnnotationClassId = ClassId.topLevel(serializerAnnotationFqName)
|
||||
val serialNameAnnotationClassId = ClassId.topLevel(serialNameAnnotationFqName)
|
||||
val requiredAnnotationClassId = ClassId.topLevel(requiredAnnotationFqName)
|
||||
val serialTransientClassId = ClassId.topLevel(serialTransientFqName)
|
||||
val serialInfoClassId = ClassId.topLevel(serialInfoFqName)
|
||||
val inheritableSerialInfoClassId = ClassId.topLevel(inheritableSerialInfoFqName)
|
||||
val metaSerializableAnnotationClassId = ClassId.topLevel(metaSerializableAnnotationFqName)
|
||||
val encodeDefaultClassId = ClassId.topLevel(encodeDefaultFqName)
|
||||
|
||||
val contextualClassId = ClassId.topLevel(contextualFqName)
|
||||
val contextualOnFileClassId = ClassId.topLevel(contextualOnFileFqName)
|
||||
val contextualOnPropertyClassId = ClassId.topLevel(contextualOnPropertyFqName)
|
||||
val polymorphicClassId = ClassId.topLevel(polymorphicFqName)
|
||||
val additionalSerializersClassId = ClassId.topLevel(additionalSerializersFqName)
|
||||
}
|
||||
|
||||
object SerialEntityNames {
|
||||
@@ -55,9 +71,12 @@ object SerialEntityNames {
|
||||
|
||||
// classes
|
||||
val KCLASS_NAME_FQ = FqName("kotlin.reflect.KClass")
|
||||
val KCLASS_NAME_CLASS_ID = ClassId.topLevel(KCLASS_NAME_FQ)
|
||||
val KSERIALIZER_NAME = Name.identifier(KSERIALIZER_CLASS)
|
||||
val SERIAL_CTOR_MARKER_NAME = Name.identifier("SerializationConstructorMarker")
|
||||
val KSERIALIZER_NAME_FQ = SerializationPackages.packageFqName.child(KSERIALIZER_NAME)
|
||||
val KSERIALIZER_CLASS_ID = ClassId.topLevel(KSERIALIZER_NAME_FQ)
|
||||
|
||||
val SERIALIZER_CLASS_NAME = Name.identifier(SERIALIZER_CLASS)
|
||||
val IMPL_NAME = Name.identifier("Impl")
|
||||
|
||||
@@ -123,8 +142,30 @@ object SpecialBuiltins {
|
||||
const val sealedSerializer = "SealedClassSerializer"
|
||||
const val contextSerializer = "ContextualSerializer"
|
||||
const val nullableSerializer = "NullableSerializer"
|
||||
|
||||
object Names {
|
||||
val referenceArraySerializer = Name.identifier(SpecialBuiltins.referenceArraySerializer)
|
||||
val objectSerializer = Name.identifier(SpecialBuiltins.objectSerializer)
|
||||
val enumSerializer = Name.identifier(SpecialBuiltins.enumSerializer)
|
||||
val polymorphicSerializer = Name.identifier(SpecialBuiltins.polymorphicSerializer)
|
||||
val sealedSerializer = Name.identifier(SpecialBuiltins.sealedSerializer)
|
||||
val contextSerializer = Name.identifier(SpecialBuiltins.contextSerializer)
|
||||
val nullableSerializer = Name.identifier(SpecialBuiltins.nullableSerializer)
|
||||
}
|
||||
}
|
||||
|
||||
object PrimitiveBuiltins {
|
||||
const val booleanSerializer = "BooleanSerializer"
|
||||
const val byteSerializer = "ByteSerializer"
|
||||
const val shortSerializer = "ShortSerializer"
|
||||
const val intSerializer = "IntSerializer"
|
||||
const val longSerializer = "LongSerializer"
|
||||
const val floatSerializer = "FloatSerializer"
|
||||
const val doubleSerializer = "DoubleSerializer"
|
||||
const val charSerializer = "CharSerializer"
|
||||
}
|
||||
|
||||
|
||||
object CallingConventions {
|
||||
const val begin = "beginStructure"
|
||||
const val end = "endStructure"
|
||||
@@ -153,3 +194,71 @@ object SerializationDependencies {
|
||||
val FUNCTION0_FQ = FqName("kotlin.Function0")
|
||||
val LAZY_PUBLICATION_MODE_NAME = Name.identifier("PUBLICATION")
|
||||
}
|
||||
|
||||
object SerializersClassIds {
|
||||
val enumSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.enumSerializer))
|
||||
val polymorphicSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.polymorphicSerializer))
|
||||
val referenceArraySerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.referenceArraySerializer))
|
||||
val objectSerializerId = ClassId(SerializationPackages.internalPackageFqName, Name.identifier(SpecialBuiltins.objectSerializer))
|
||||
val sealedSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.sealedSerializer))
|
||||
val contextSerializerId = ClassId(SerializationPackages.packageFqName, Name.identifier(SpecialBuiltins.contextSerializer))
|
||||
}
|
||||
|
||||
fun findStandardKotlinTypeSerializerName(typeName: String?): String? {
|
||||
return when (typeName) {
|
||||
null -> null
|
||||
"kotlin.Unit" -> "UnitSerializer"
|
||||
"kotlin.Nothing" -> "NothingSerializer"
|
||||
"kotlin.Boolean" -> "BooleanSerializer"
|
||||
"kotlin.Byte" -> "ByteSerializer"
|
||||
"kotlin.Short" -> "ShortSerializer"
|
||||
"kotlin.Int" -> "IntSerializer"
|
||||
"kotlin.Long" -> "LongSerializer"
|
||||
"kotlin.Float" -> "FloatSerializer"
|
||||
"kotlin.Double" -> "DoubleSerializer"
|
||||
"kotlin.Char" -> "CharSerializer"
|
||||
"kotlin.UInt" -> "UIntSerializer"
|
||||
"kotlin.ULong" -> "ULongSerializer"
|
||||
"kotlin.UByte" -> "UByteSerializer"
|
||||
"kotlin.UShort" -> "UShortSerializer"
|
||||
"kotlin.String" -> "StringSerializer"
|
||||
"kotlin.Pair" -> "PairSerializer"
|
||||
"kotlin.Triple" -> "TripleSerializer"
|
||||
"kotlin.collections.Collection", "kotlin.collections.List",
|
||||
"kotlin.collections.ArrayList", "kotlin.collections.MutableList" -> "ArrayListSerializer"
|
||||
"kotlin.collections.Set", "kotlin.collections.LinkedHashSet", "kotlin.collections.MutableSet" -> "LinkedHashSetSerializer"
|
||||
"kotlin.collections.HashSet" -> "HashSetSerializer"
|
||||
"kotlin.collections.Map", "kotlin.collections.LinkedHashMap", "kotlin.collections.MutableMap" -> "LinkedHashMapSerializer"
|
||||
"kotlin.collections.HashMap" -> "HashMapSerializer"
|
||||
"kotlin.collections.Map.Entry" -> "MapEntrySerializer"
|
||||
"kotlin.ByteArray" -> "ByteArraySerializer"
|
||||
"kotlin.ShortArray" -> "ShortArraySerializer"
|
||||
"kotlin.IntArray" -> "IntArraySerializer"
|
||||
"kotlin.LongArray" -> "LongArraySerializer"
|
||||
"kotlin.UByteArray" -> "UByteArraySerializer"
|
||||
"kotlin.UShortArray" -> "UShortArraySerializer"
|
||||
"kotlin.UIntArray" -> "UIntArraySerializer"
|
||||
"kotlin.ULongArray" -> "ULongArraySerializer"
|
||||
"kotlin.CharArray" -> "CharArraySerializer"
|
||||
"kotlin.FloatArray" -> "FloatArraySerializer"
|
||||
"kotlin.DoubleArray" -> "DoubleArraySerializer"
|
||||
"kotlin.BooleanArray" -> "BooleanArraySerializer"
|
||||
"kotlin.time.Duration" -> "DurationSerializer"
|
||||
"java.lang.Boolean" -> "BooleanSerializer"
|
||||
"java.lang.Byte" -> "ByteSerializer"
|
||||
"java.lang.Short" -> "ShortSerializer"
|
||||
"java.lang.Integer" -> "IntSerializer"
|
||||
"java.lang.Long" -> "LongSerializer"
|
||||
"java.lang.Float" -> "FloatSerializer"
|
||||
"java.lang.Double" -> "DoubleSerializer"
|
||||
"java.lang.Character" -> "CharSerializer"
|
||||
"java.lang.String" -> "StringSerializer"
|
||||
"java.util.Collection", "java.util.List", "java.util.ArrayList" -> "ArrayListSerializer"
|
||||
"java.util.Set", "java.util.LinkedHashSet" -> "LinkedHashSetSerializer"
|
||||
"java.util.HashSet" -> "HashSetSerializer"
|
||||
"java.util.Map", "java.util.LinkedHashMap" -> "LinkedHashMapSerializer"
|
||||
"java.util.HashMap" -> "HashMapSerializer"
|
||||
"java.util.Map.Entry" -> "MapEntrySerializer"
|
||||
else -> return null
|
||||
}
|
||||
}
|
||||
|
||||
+11
-69
@@ -22,9 +22,9 @@ 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.SerializersClassIds.enumSerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializersClassIds.referenceArraySerializerId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationPackages.internalPackageFqName
|
||||
|
||||
open class SerialTypeInfo(
|
||||
@@ -164,79 +164,21 @@ fun findTypeSerializer(module: ModuleDescriptor, kType: KotlinType): ClassDescri
|
||||
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)
|
||||
"Z" -> if (kType.isBoolean()) PrimitiveBuiltins.booleanSerializer else null
|
||||
"B" -> if (kType.isByte()) PrimitiveBuiltins.byteSerializer else null
|
||||
"S" -> if (kType.isShort()) PrimitiveBuiltins.shortSerializer else null
|
||||
"I" -> if (kType.isInt()) PrimitiveBuiltins.intSerializer else null
|
||||
"J" -> if (kType.isLong()) PrimitiveBuiltins.longSerializer else null
|
||||
"F" -> if (kType.isFloat()) PrimitiveBuiltins.floatSerializer else null
|
||||
"D" -> if (kType.isDouble()) PrimitiveBuiltins.doubleSerializer else null
|
||||
"C" -> if (kType.isChar()) PrimitiveBuiltins.charSerializer else null
|
||||
else -> findStandardKotlinTypeSerializerName(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())
|
||||
|
||||
-18
@@ -1,18 +0,0 @@
|
||||
/*
|
||||
* 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))
|
||||
+5
-5
@@ -107,7 +107,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMinRuntime(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
|
||||
private fun checkMinRuntime(versions: 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
|
||||
@@ -119,14 +119,14 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
it,
|
||||
versions.implementationVersion?.toString() ?: "too low",
|
||||
KotlinCompilerVersion.getVersion() ?: "unknown",
|
||||
VersionReader.MINIMAL_SUPPORTED_VERSION.toString(),
|
||||
RuntimeVersions.MINIMAL_SUPPORTED_VERSION.toString(),
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMinKotlin(versions: VersionReader.RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
|
||||
private fun checkMinKotlin(versions: RuntimeVersions, descriptor: ClassDescriptor, trace: BindingTrace) {
|
||||
if (versions.currentCompilerMatchRequired()) return
|
||||
descriptor.onSerializableOrMetaAnnotation {
|
||||
trace.report(
|
||||
@@ -192,7 +192,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
trace.report(
|
||||
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
|
||||
it,
|
||||
VersionReader.minVersionForInlineClasses.toString(),
|
||||
RuntimeVersions.MINIMAL_VERSION_FOR_INLINE_CLASSES.toString(),
|
||||
VersionReader.getVersionsForCurrentModuleFromTrace(descriptor.module, trace)?.implementationVersion.toString()
|
||||
)
|
||||
)
|
||||
@@ -386,7 +386,7 @@ open class SerializationPluginDeclarationChecker : DeclarationChecker {
|
||||
trace.report(
|
||||
SerializationErrors.INLINE_CLASSES_NOT_SUPPORTED.on(
|
||||
element ?: fallbackElement,
|
||||
VersionReader.minVersionForInlineClasses.toString(),
|
||||
RuntimeVersions.MINIMAL_VERSION_FOR_INLINE_CLASSES.toString(),
|
||||
VersionReader.getVersionsForCurrentModuleFromTrace(module, trace)?.implementationVersion.toString()
|
||||
)
|
||||
)
|
||||
|
||||
+7
-38
@@ -5,11 +5,7 @@
|
||||
|
||||
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
|
||||
@@ -17,31 +13,8 @@ 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? {
|
||||
@@ -58,20 +31,16 @@ object VersionReader {
|
||||
|
||||
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)
|
||||
return CommonVersionReader.computeRuntimeVersions(markerClass.source)
|
||||
}
|
||||
|
||||
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
|
||||
return CommonVersionReader.canSupportInlineClasses(getVersionsForCurrentModuleFromTrace(module, trace))
|
||||
}
|
||||
|
||||
// This method is needed to keep compatibility with IDE plugin
|
||||
fun getVersionsFromManifest(runtimeLibraryPath: File): RuntimeVersions {
|
||||
return CommonVersionReader.getVersionsFromManifest(runtimeLibraryPath)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-40
@@ -21,13 +21,6 @@ import org.jetbrains.kotlinx.serialization.compiler.diagnostic.SERIALIZABLE_PROP
|
||||
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> =
|
||||
@@ -119,38 +112,6 @@ fun PropertyDescriptor.declaresDefaultValue(): Boolean {
|
||||
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
|
||||
@@ -160,7 +121,7 @@ fun BindingContext.serializablePropertiesFor(
|
||||
return props
|
||||
}
|
||||
|
||||
fun <P: ISerializableProperty> restoreCorrectOrderFromClassProtoExtension(descriptor: ClassDescriptor, props: List<P>): List<P> {
|
||||
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) }
|
||||
|
||||
-8
@@ -21,14 +21,6 @@ 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,
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* 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.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.hasBackingField
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.analyzeSpecialSerializers
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.ISerializableProperties
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.ISerializableProperty
|
||||
|
||||
class FirSerializableProperty(
|
||||
session: FirSession,
|
||||
val propertySymbol: FirPropertySymbol,
|
||||
override val isConstructorParameterWithDefault: Boolean,
|
||||
declaresDefaultValue: Boolean
|
||||
) : ISerializableProperty {
|
||||
override val name: String = propertySymbol.serialNameValue ?: propertySymbol.name.asString()
|
||||
|
||||
override val originalDescriptorName: Name
|
||||
get() = propertySymbol.name
|
||||
|
||||
override val optional: Boolean = !propertySymbol.serialRequired && declaresDefaultValue
|
||||
|
||||
override val transient: Boolean = propertySymbol.hasSerialTransient || !propertySymbol.hasBackingField
|
||||
|
||||
val serializableWith: ConeKotlinType? = propertySymbol.serializableWith
|
||||
?: analyzeSpecialSerializers(session, propertySymbol.resolvedAnnotationsWithArguments)?.defaultType()
|
||||
}
|
||||
|
||||
class FirSerializableProperties(
|
||||
override val serializableProperties: List<FirSerializableProperty>,
|
||||
override val isExternallySerializable: Boolean,
|
||||
override val serializableConstructorProperties: List<FirSerializableProperty>,
|
||||
override val serializableStandaloneProperties: List<FirSerializableProperty>,
|
||||
) : ISerializableProperties<FirSerializableProperty>
|
||||
+12
@@ -6,10 +6,22 @@
|
||||
package org.jetbrains.kotlinx.serialization.compiler.fir
|
||||
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionRegistrar
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.checkers.FirSerializationCheckersComponent
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.ContextualSerializersProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.DependencySerializationInfoProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.FirSerializablePropertiesProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.FirVersionReader
|
||||
|
||||
class FirSerializationExtensionRegistrar : FirExtensionRegistrar() {
|
||||
override fun ExtensionRegistrarContext.configurePlugin() {
|
||||
+::SerializationFirResolveExtension
|
||||
+::SerializationFirSupertypesExtension
|
||||
+::FirSerializationCheckersComponent
|
||||
|
||||
// services
|
||||
+::DependencySerializationInfoProvider
|
||||
+::FirSerializablePropertiesProvider
|
||||
+::FirVersionReader
|
||||
+::ContextualSerializersProvider
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -135,7 +135,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene
|
||||
override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List<FirNamedFunctionSymbol> {
|
||||
val owner = context?.owner ?: return emptyList()
|
||||
if (callableId.callableName == SerialEntityNames.SERIALIZER_PROVIDER_NAME) {
|
||||
val serializableClass = session.getSerializableClassDescriptorByCompanion(owner) ?: return emptyList()
|
||||
val serializableClass = owner.getSerializableClassSymbolIfCompanion(session) ?: return emptyList()
|
||||
return listOf(generateSerializerGetterInCompanion(owner, serializableClass, callableId))
|
||||
}
|
||||
if (owner.name != SerialEntityNames.SERIALIZER_CLASS_NAME) return emptyList()
|
||||
|
||||
+233
-28
@@ -5,50 +5,163 @@
|
||||
|
||||
package org.jetbrains.kotlinx.serialization.compiler.fir
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.getContainingDeclarationSymbol
|
||||
import org.jetbrains.kotlin.fir.declarations.findArgumentByName
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isCompanion
|
||||
import org.jetbrains.kotlin.fir.expressions.classId
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.unsubstitutedScope
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotation
|
||||
import org.jetbrains.kotlin.fir.expressions.FirAnnotationCall
|
||||
import org.jetbrains.kotlin.fir.expressions.arguments
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.scopes.getSingleClassifier
|
||||
import org.jetbrains.kotlin.fir.symbols.FirBasedSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.name.StandardClassIds
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.inheritableSerialInfoClassId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.metaSerializableAnnotationClassId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialInfoClassId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerializationAnnotations.serialNameAnnotationClassId
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SpecialBuiltins
|
||||
|
||||
internal val FirClassSymbol<*>.hasSerializableAnnotation
|
||||
get() = this.annotations.any {
|
||||
it.classId?.asSingleFqName()?.asString() == SerializationAnnotations.serializableAnnotationFqName.asString()
|
||||
}
|
||||
object AnnotationParameterNames {
|
||||
val VALUE = Name.identifier("value")
|
||||
val WITH = Name.identifier("with")
|
||||
val FOR_CLASS = Name.identifier("forClass")
|
||||
}
|
||||
|
||||
internal val FirClassSymbol<*>.hasSerializableAnnotationWithoutArgs: Boolean
|
||||
get() {
|
||||
if (!hasSerializableAnnotation) return false
|
||||
val target = this.resolvedAnnotationsWithArguments.find {
|
||||
it.classId?.asSingleFqName()?.asString() == SerializationAnnotations.serializableAnnotationFqName.asString()
|
||||
}!!
|
||||
return target.findArgumentByName(Name.identifier("with")) == null
|
||||
}
|
||||
// ---------------------- annotations utils ----------------------
|
||||
|
||||
val FirBasedSymbol<*>.isSerialInfoAnnotation: Boolean
|
||||
get() = hasAnnotation(serialInfoClassId)
|
||||
|| hasAnnotation(inheritableSerialInfoClassId)
|
||||
|| hasAnnotation(metaSerializableAnnotationClassId)
|
||||
|
||||
val FirBasedSymbol<*>.isInheritableSerialInfoAnnotation: Boolean
|
||||
get() = hasAnnotation(inheritableSerialInfoClassId)
|
||||
|
||||
val FirBasedSymbol<*>.serialNameAnnotation: FirAnnotation?
|
||||
get() = resolvedAnnotationsWithArguments.getAnnotationByClassId(serialNameAnnotationClassId)
|
||||
|
||||
val FirBasedSymbol<*>.serialNameValue: String?
|
||||
get() = serialNameAnnotation?.getStringArgument(AnnotationParameterNames.VALUE)
|
||||
|
||||
val FirBasedSymbol<*>.serialRequired: Boolean
|
||||
get() = hasAnnotation(SerializationAnnotations.requiredAnnotationClassId)
|
||||
|
||||
val FirBasedSymbol<*>.hasSerialTransient: Boolean
|
||||
get() = serialTransientAnnotation != null
|
||||
|
||||
val FirBasedSymbol<*>.serialTransientAnnotation: FirAnnotation?
|
||||
get() = getAnnotationByClassId(SerializationAnnotations.serialTransientClassId)
|
||||
|
||||
context(CheckerContext)
|
||||
val FirBasedSymbol<*>.hasAnySerialAnnotation: Boolean
|
||||
get() = serialNameValue != null || resolvedAnnotationsWithClassIds.any { it.annotationClassSymbol?.isSerialInfoAnnotation == true }
|
||||
|
||||
val FirClassSymbol<*>.hasSerializableAnnotation: Boolean
|
||||
get() = serializableAnnotation(needArguments = false) != null
|
||||
|
||||
fun FirBasedSymbol<*>.serializableAnnotation(needArguments: Boolean): FirAnnotation? {
|
||||
val annotations = if (needArguments) resolvedAnnotationsWithClassIds else resolvedAnnotationsWithArguments
|
||||
return annotations.serializableAnnotation()
|
||||
}
|
||||
|
||||
fun List<FirAnnotation>.serializableAnnotation(): FirAnnotation? {
|
||||
return getAnnotationByClassId(SerializationAnnotations.serializableAnnotationClassId)
|
||||
}
|
||||
|
||||
val FirClassSymbol<*>.hasSerializableAnnotationWithoutArgs: Boolean
|
||||
get() = serializableAnnotation(needArguments = false)?.let {
|
||||
if (it is FirAnnotationCall) {
|
||||
it.arguments.isEmpty()
|
||||
} else {
|
||||
it.argumentMapping.mapping.isEmpty()
|
||||
}
|
||||
} ?: false
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>.hasSerializableOrMetaAnnotationWithoutArgs: Boolean
|
||||
get() = hasSerializableAnnotationWithoutArgs || (!hasSerializableAnnotation && hasMetaSerializableAnnotation)
|
||||
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>.hasSerializableOrMetaAnnotation
|
||||
get() = hasSerializableAnnotation || hasMetaSerializableAnnotation
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>.hasMetaSerializableAnnotation: Boolean
|
||||
get() = metaSerializableAnnotation(needArguments = false) != null
|
||||
|
||||
context(CheckerContext)
|
||||
fun FirClassSymbol<*>.metaSerializableAnnotation(needArguments: Boolean): FirAnnotation? {
|
||||
val annotations = if (needArguments) resolvedAnnotationsWithClassIds else resolvedAnnotationsWithArguments
|
||||
return annotations.firstOrNull { it.isMetaSerializableAnnotation }
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
val FirAnnotation.isMetaSerializableAnnotation: Boolean
|
||||
get() = annotationClassSymbol?.hasAnnotation(metaSerializableAnnotationClassId) ?: false
|
||||
|
||||
context(CheckerContext)
|
||||
val ConeKotlinType.serializableWith: ConeKotlinType?
|
||||
get() = customAnnotations.serializableWith ?: toRegularClassSymbol(session)?.serializableWith
|
||||
|
||||
internal val FirBasedSymbol<*>.serializableWith: ConeKotlinType?
|
||||
get() = serializableAnnotation(needArguments = true)?.getKClassArgument(AnnotationParameterNames.WITH)
|
||||
|
||||
internal val List<FirAnnotation>.serializableWith: ConeKotlinType?
|
||||
get() = serializableAnnotation()?.getKClassArgument(AnnotationParameterNames.WITH)
|
||||
|
||||
internal val FirClassSymbol<*>.serializerForClass: ConeKotlinType?
|
||||
get() = resolvedAnnotationsWithArguments
|
||||
.getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId)
|
||||
?.getKClassArgument(AnnotationParameterNames.FOR_CLASS)
|
||||
|
||||
internal val FirClassSymbol<*>.serializerAnnotation: FirAnnotation?
|
||||
get() = getAnnotationByClassId(SerializationAnnotations.serializerAnnotationClassId)
|
||||
|
||||
context(CheckerContext)
|
||||
private val FirAnnotation.annotationClassSymbol: FirRegularClassSymbol?
|
||||
get() = annotationTypeRef.coneType
|
||||
.fullyExpandedType(session)
|
||||
.toRegularClassSymbol(session)
|
||||
|
||||
// ---------------------- class utils ----------------------
|
||||
|
||||
internal val FirClassSymbol<*>.shouldHaveGeneratedMethodsInCompanion: Boolean
|
||||
get() = this.isSerializableObject || this.isSerializableEnum() || this.classKind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface
|
||||
get() = this.isSerializableObject || this.isSerializableEnum || this.classKind == ClassKind.CLASS && hasSerializableAnnotation || this.isSealedSerializableInterface
|
||||
|
||||
internal val FirClassSymbol<*>.isSerializableObject: Boolean
|
||||
get() = classKind == ClassKind.OBJECT && hasSerializableAnnotation
|
||||
get() = classKind.isObject && hasSerializableAnnotation
|
||||
|
||||
internal val FirClassSymbol<*>.isInternallySerializableObject: Boolean
|
||||
get() = classKind == ClassKind.OBJECT && hasSerializableAnnotationWithoutArgs
|
||||
get() = classKind.isObject && hasSerializableAnnotationWithoutArgs
|
||||
|
||||
internal val FirClassSymbol<*>.isSealedSerializableInterface: Boolean
|
||||
get() = classKind == ClassKind.INTERFACE && rawStatus.modality == Modality.SEALED && hasSerializableAnnotation
|
||||
get() = classKind.isInterface && rawStatus.modality == Modality.SEALED && hasSerializableAnnotation
|
||||
|
||||
internal val FirClassSymbol<*>.isInternalSerializable: Boolean
|
||||
get() {
|
||||
if (classKind != ClassKind.CLASS) return false
|
||||
if (!classKind.isClass) return false
|
||||
return hasSerializableAnnotationWithoutArgs
|
||||
}
|
||||
|
||||
internal fun FirClassSymbol<*>.isSerializableEnum(): Boolean = classKind == ClassKind.ENUM_CLASS && hasSerializableAnnotation
|
||||
internal val FirClassSymbol<*>.isInternallySerializableEnum: Boolean
|
||||
get() = classKind.isEnumClass && hasSerializableAnnotationWithoutArgs
|
||||
|
||||
internal val FirClassSymbol<*>.isSerializableEnum: Boolean
|
||||
get() = classKind.isEnumClass && hasSerializableAnnotation
|
||||
|
||||
internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean {
|
||||
val modality = rawStatus.modality
|
||||
@@ -56,10 +169,102 @@ internal fun FirClassSymbol<*>.isFinalOrOpen(): Boolean {
|
||||
return (modality == null || modality == Modality.FINAL || modality == Modality.OPEN)
|
||||
}
|
||||
|
||||
internal fun FirSession.getSerializableClassDescriptorByCompanion(thisDescriptor: FirClassSymbol<*>): FirClassSymbol<*>? {
|
||||
if (thisDescriptor.isSerializableObject) return thisDescriptor
|
||||
if (!thisDescriptor.isCompanion) return null
|
||||
val classDescriptor = (thisDescriptor.getContainingDeclarationSymbol(this) as? FirClassSymbol<*>) ?: return null
|
||||
internal fun FirClassSymbol<*>.getSerializableClassSymbolIfCompanion(session: FirSession): FirClassSymbol<*>? {
|
||||
if (isSerializableObject) return this
|
||||
if (!isCompanion) return null
|
||||
val classDescriptor = (getContainingDeclarationSymbol(session) as? FirClassSymbol<*>) ?: return null
|
||||
if (!classDescriptor.shouldHaveGeneratedMethodsInCompanion) return null
|
||||
return classDescriptor
|
||||
}
|
||||
}
|
||||
|
||||
val FirClassSymbol<*>.hasCompanionObjectAsSerializer: Boolean
|
||||
get() = isInternallySerializableObject ||
|
||||
(this as? FirRegularClassSymbol)?.companionObjectSymbol?.serializerForClass == this.defaultType()
|
||||
|
||||
fun FirClassSymbol<*>.getSuperClassNotAny(session: FirSession): FirRegularClassSymbol? {
|
||||
return getSuperClassOrAny(session).takeUnless { it.classId == StandardClassIds.Any }
|
||||
}
|
||||
|
||||
fun FirClassSymbol<*>.getSuperClassOrAny(session: FirSession): FirRegularClassSymbol {
|
||||
return resolvedSuperTypes.firstNotNullOfOrNull { superType ->
|
||||
superType.fullyExpandedType(session)
|
||||
.toRegularClassSymbol(session)
|
||||
?.takeIf { it.classKind == ClassKind.CLASS }
|
||||
} ?: session.builtinTypes.anyType.toRegularClassSymbol(session) ?: error("Symbol for kotlin/Any not found")
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>?.classSerializer: FirClassSymbol<*>?
|
||||
get() {
|
||||
if (this == null) return null
|
||||
// serializer annotation on class?
|
||||
serializableWith?.let { return it.toRegularClassSymbol(session) }
|
||||
// companion object serializer?
|
||||
if (this is FirRegularClassSymbol && hasCompanionObjectAsSerializer) return companionObjectSymbol
|
||||
// can infer @Poly?
|
||||
polymorphicSerializerIfApplicableAutomatically?.let { return it }
|
||||
// default serializable?
|
||||
if (shouldHaveGeneratedSerializer) {
|
||||
// $serializer nested class
|
||||
return unsubstitutedScope(this@CheckerContext)
|
||||
.getSingleClassifier(SerialEntityNames.SERIALIZER_CLASS_NAME) as? FirClassSymbol<*>
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>.polymorphicSerializerIfApplicableAutomatically: FirClassSymbol<*>?
|
||||
get() {
|
||||
val serializerName = when {
|
||||
isInterface -> when (modality) {
|
||||
Modality.SEALED -> SpecialBuiltins.sealedSerializer
|
||||
else -> SpecialBuiltins.polymorphicSerializer
|
||||
}
|
||||
isInternalSerializable -> when (modality) {
|
||||
Modality.SEALED -> SpecialBuiltins.sealedSerializer
|
||||
Modality.ABSTRACT -> SpecialBuiltins.polymorphicSerializer
|
||||
else -> null
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
return serializerName?.let { session.dependencySerializationInfoProvider.getClassFromSerializationPackage(Name.identifier(it)) }
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>.isEnumWithLegacyGeneratedSerializer: Boolean
|
||||
get() = isInternallySerializableEnum && session.dependencySerializationInfoProvider.useGeneratedEnumSerializer
|
||||
|
||||
context(CheckerContext)
|
||||
val FirClassSymbol<*>.shouldHaveGeneratedSerializer: Boolean
|
||||
get() = (isInternalSerializable && (modality == Modality.FINAL || modality == Modality.OPEN)) || isEnumWithLegacyGeneratedSerializer
|
||||
|
||||
// ---------------------- type utils ----------------------
|
||||
|
||||
val ConeKotlinType.isKSerializer: Boolean
|
||||
get() = classId == SerialEntityNames.KSERIALIZER_CLASS_ID
|
||||
|
||||
fun ConeKotlinType.serializerForType(session: FirSession): ConeKotlinType? {
|
||||
return this.fullyExpandedType(session)
|
||||
.toRegularClassSymbol(session)
|
||||
?.resolvedSuperTypes
|
||||
?.find { it.isKSerializer }
|
||||
?.typeArguments
|
||||
?.firstOrNull()
|
||||
?.type
|
||||
}
|
||||
|
||||
val ConeKotlinType.isTypeParameter: Boolean
|
||||
get() = this is ConeTypeParameterType
|
||||
|
||||
context(CheckerContext)
|
||||
val ConeKotlinType.overriddenSerializer: ConeKotlinType?
|
||||
get() = toRegularClassSymbol(session)?.serializableWith
|
||||
|
||||
context(CheckerContext)
|
||||
val ConeKotlinType.isGeneratedSerializableObject: Boolean
|
||||
get() = toRegularClassSymbol(session)?.let { it.classKind.isObject && it.hasSerializableOrMetaAnnotationWithoutArgs } ?: false
|
||||
|
||||
// ---------------------- other ----------------------
|
||||
|
||||
val CheckerContext.currentFile: FirFile
|
||||
get() = containingDeclarations.first() as FirFile
|
||||
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* 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.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.DeclarationCheckers
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.extensions.FirAdditionalCheckersExtension
|
||||
|
||||
class FirSerializationCheckersComponent(session: FirSession) : FirAdditionalCheckersExtension(session) {
|
||||
override val declarationCheckers: DeclarationCheckers = object : DeclarationCheckers() {
|
||||
override val classCheckers: Set<FirClassChecker> = setOf(FirSerializationPluginClassChecker)
|
||||
}
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.fir.checkers
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.ConeKotlinType
|
||||
import org.jetbrains.kotlin.psi.KtAnnotationEntry
|
||||
|
||||
object FirSerializationErrors {
|
||||
val INLINE_CLASSES_NOT_SUPPORTED by error2<PsiElement, String, String>()
|
||||
|
||||
val PLUGIN_IS_NOT_ENABLED by warning0<PsiElement>()
|
||||
val ANONYMOUS_OBJECTS_NOT_SUPPORTED by error0<PsiElement>()
|
||||
val INNER_CLASSES_NOT_SUPPORTED by error0<PsiElement>()
|
||||
|
||||
val EXPLICIT_SERIALIZABLE_IS_REQUIRED by warning0<PsiElement>()
|
||||
|
||||
val SERIALIZABLE_ANNOTATION_IGNORED by error0<KtAnnotationEntry>()
|
||||
val NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR by error0<KtAnnotationEntry>()
|
||||
val PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY by error0<KtAnnotationEntry>()
|
||||
val DUPLICATE_SERIAL_NAME by error1<KtAnnotationEntry, String>()
|
||||
val DUPLICATE_SERIAL_NAME_ENUM by error3<PsiElement, FirClassSymbol<*>, String, String>()
|
||||
val SERIALIZER_NOT_FOUND by error1<PsiElement, ConeKotlinType>()
|
||||
val SERIALIZER_NULLABILITY_INCOMPATIBLE by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
||||
val SERIALIZER_TYPE_INCOMPATIBLE by warning3<PsiElement, ConeKotlinType, ConeKotlinType, ConeKotlinType>()
|
||||
val LOCAL_SERIALIZER_USAGE by error1<PsiElement, ConeKotlinType>()
|
||||
val TRANSIENT_MISSING_INITIALIZER by error0<PsiElement>()
|
||||
|
||||
val TRANSIENT_IS_REDUNDANT by warning0<PsiElement>()
|
||||
|
||||
val INCORRECT_TRANSIENT by warning0<PsiElement>()
|
||||
|
||||
val REQUIRED_KOTLIN_TOO_HIGH by error3<KtAnnotationEntry, String, String, String>()
|
||||
val PROVIDED_RUNTIME_TOO_LOW by error3<KtAnnotationEntry, String, String, String>()
|
||||
|
||||
val INCONSISTENT_INHERITABLE_SERIALINFO by error2<PsiElement, ConeKotlinType, ConeKotlinType>()
|
||||
|
||||
val EXTERNAL_CLASS_NOT_SERIALIZABLE by error2<PsiElement, FirClassSymbol<*>, ConeKotlinType>()
|
||||
val EXTERNAL_CLASS_IN_ANOTHER_MODULE by error2<PsiElement, FirClassSymbol<*>, ConeKotlinType>()
|
||||
}
|
||||
+509
@@ -0,0 +1,509 @@
|
||||
/*
|
||||
* 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.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.KtSourceElement
|
||||
import org.jetbrains.kotlin.config.KotlinCompilerVersion
|
||||
import org.jetbrains.kotlin.descriptors.isEnumClass
|
||||
import org.jetbrains.kotlin.diagnostics.*
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.FirClassChecker
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.isInlineClass
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.toRegularClassSymbol
|
||||
import org.jetbrains.kotlin.fir.declarations.*
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.*
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.moduleData
|
||||
import org.jetbrains.kotlin.fir.resolve.defaultType
|
||||
import org.jetbrains.kotlin.fir.resolve.fullyExpandedType
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.*
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.*
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.TRANSIENT_ANNOTATION_CLASS_ID
|
||||
import org.jetbrains.kotlin.types.AbstractTypeChecker
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.RuntimeVersions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.serializerForClass
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.dependencySerializationInfoProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.findTypeSerializerOrContextUnchecked
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.serializablePropertiesProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.services.versionReader
|
||||
|
||||
object FirSerializationPluginClassChecker : FirClassChecker() {
|
||||
private val JAVA_SERIALIZABLE_ID = ClassId.topLevel(FqName("java.io.Serializable"))
|
||||
private const val TOO_LOW = "too low"
|
||||
private const val UNKNOWN = "unknown"
|
||||
private const val NA = "N/A"
|
||||
|
||||
override fun check(declaration: FirClass, context: CheckerContext, reporter: DiagnosticReporter) {
|
||||
with(context) {
|
||||
val classSymbol = declaration.symbol
|
||||
checkEnum(classSymbol, reporter)
|
||||
checkExternalSerializer(classSymbol, reporter)
|
||||
if (!canBeSerializedInternally(classSymbol, reporter)) return
|
||||
if (classSymbol !is FirRegularClassSymbol) return
|
||||
|
||||
val properties = buildSerializableProperties(classSymbol, reporter) ?: return
|
||||
checkCorrectTransientAnnotationIsUsed(classSymbol, properties.serializableProperties, reporter)
|
||||
checkTransients(classSymbol, reporter)
|
||||
analyzePropertiesSerializers(classSymbol, properties.serializableProperties, reporter)
|
||||
checkInheritedAnnotations(classSymbol, reporter)
|
||||
|
||||
checkVersions(classSymbol, reporter)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkExternalSerializer(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||
val serializableKType = classSymbol.serializerForClass ?: return
|
||||
val serializableClassSymbol = serializableKType.toRegularClassSymbol(session) ?: return
|
||||
val properties = session.serializablePropertiesProvider.getSerializablePropertiesForClass(classSymbol)
|
||||
if (!properties.isExternallySerializable) {
|
||||
val source = classSymbol.serializerAnnotation?.source ?: classSymbol.source
|
||||
val error = if (serializableClassSymbol.moduleData == session.moduleData) {
|
||||
FirSerializationErrors.EXTERNAL_CLASS_NOT_SERIALIZABLE
|
||||
} else {
|
||||
FirSerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE
|
||||
}
|
||||
reporter.reportOn(source, error, serializableClassSymbol, serializableKType)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkInheritedAnnotations(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||
fun annotationsFilter(annotations: List<FirAnnotation>): List<Pair<ClassId, FirAnnotation>> {
|
||||
return annotations
|
||||
.filter { it.annotationTypeRef.toRegularClassSymbol(session)?.isInheritableSerialInfoAnnotation == true }
|
||||
.mapNotNull { annotation -> annotation.classId?.let { it to annotation } }
|
||||
}
|
||||
|
||||
val annotationByClassId = buildMap {
|
||||
putAll(annotationsFilter(classSymbol.resolvedAnnotationsWithClassIds))
|
||||
}
|
||||
|
||||
for (superType in classSymbol.resolvedSuperTypes) {
|
||||
val superSymbol = superType.toRegularClassSymbol(session) ?: continue
|
||||
val superAnnotations = annotationsFilter(superSymbol.resolvedAnnotationsWithClassIds)
|
||||
for ((classId, superAnnotation) in superAnnotations) {
|
||||
val existingAnnotation = annotationByClassId[classId] ?: continue
|
||||
if (!existingAnnotation.hasSameArguments(superAnnotation)) {
|
||||
reporter.reportOn(
|
||||
existingAnnotation.source,
|
||||
FirSerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO,
|
||||
existingAnnotation.annotationTypeRef.coneType,
|
||||
classSymbol.defaultType()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun FirAnnotation.hasSameArguments(other: FirAnnotation): Boolean {
|
||||
val m1 = argumentMapping.mapping
|
||||
val m2 = other.argumentMapping.mapping
|
||||
if (m1.keys != m2.keys) return false
|
||||
for ((key, v1) in m1) {
|
||||
val v2 = m2.getValue(key)
|
||||
if (!v1.isEqualTo(v2)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun FirExpression.isEqualTo(other: FirExpression): Boolean {
|
||||
return when {
|
||||
this is FirConstExpression<*> && other is FirConstExpression<*> -> kind == other.kind && value == other.value
|
||||
this is FirGetClassCall && other is FirGetClassCall -> AbstractTypeChecker.equalTypes(
|
||||
session.typeContext,
|
||||
typeRef.coneType,
|
||||
other.typeRef.coneType
|
||||
)
|
||||
|
||||
this is FirPropertyAccessExpression && other is FirPropertyAccessExpression ->
|
||||
this.toResolvedCallableReference()?.resolvedSymbol == other.toResolvedCallableReference()?.resolvedSymbol
|
||||
|
||||
else -> {
|
||||
val argumentsIfArray1 = when (this) {
|
||||
is FirVarargArgumentsExpression -> arguments
|
||||
is FirArrayOfCall -> arguments
|
||||
else -> return false
|
||||
}
|
||||
val argumentsIfArray2 = when (other) {
|
||||
is FirVarargArgumentsExpression -> other.arguments
|
||||
is FirArrayOfCall -> other.arguments
|
||||
else -> return false
|
||||
}
|
||||
argumentsIfArray1.size == argumentsIfArray2.size && argumentsIfArray1.zip(argumentsIfArray2)
|
||||
.all { (a, b) -> a.isEqualTo(b) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkVersions(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||
val currentVersions = session.versionReader.runtimeVersions ?: return
|
||||
if (!currentVersions.implementationVersionMatchSupported()) {
|
||||
reporter.reportOn(
|
||||
classSymbol.serializableOrMetaAnnotationSource,
|
||||
FirSerializationErrors.PROVIDED_RUNTIME_TOO_LOW,
|
||||
KotlinCompilerVersion.getVersion() ?: TOO_LOW,
|
||||
currentVersions.implementationVersion?.toString() ?: UNKNOWN,
|
||||
RuntimeVersions.MINIMAL_SUPPORTED_VERSION.toString()
|
||||
)
|
||||
}
|
||||
if (!currentVersions.currentCompilerMatchRequired()) {
|
||||
reporter.reportOn(
|
||||
classSymbol.serializableOrMetaAnnotationSource,
|
||||
FirSerializationErrors.REQUIRED_KOTLIN_TOO_HIGH,
|
||||
KotlinCompilerVersion.getVersion() ?: TOO_LOW,
|
||||
currentVersions.implementationVersion?.toString() ?: UNKNOWN,
|
||||
currentVersions.requireKotlinVersion?.toString() ?: NA
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkCorrectTransientAnnotationIsUsed(
|
||||
classSymbol: FirClassSymbol<*>,
|
||||
properties: List<FirSerializableProperty>,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
if (classSymbol.resolvedSuperTypes.any { it.classId == JAVA_SERIALIZABLE_ID }) return // do not check
|
||||
for (property in properties) {
|
||||
if (property.transient) continue
|
||||
val incorrectTransient = property.propertySymbol.annotations.getAnnotationByClassId(TRANSIENT_ANNOTATION_CLASS_ID)
|
||||
if (incorrectTransient != null) {
|
||||
reporter.reportOn(
|
||||
source = incorrectTransient.source ?: property.propertySymbol.source,
|
||||
FirSerializationErrors.INCORRECT_TRANSIENT
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun canBeSerializedInternally(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter): Boolean {
|
||||
// if enum has meta or SerialInfo annotation on a class or entries and used plugin-generated serializer
|
||||
if (session.dependencySerializationInfoProvider.useGeneratedEnumSerializer && classSymbol.isSerializableEnumWithMissingSerializer) {
|
||||
reporter.reportOn(
|
||||
classSymbol.source,
|
||||
FirSerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED,
|
||||
positioningStrategy = SourceElementPositioningStrategies.ENUM_MODIFIER
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
if (!classSymbol.hasSerializableOrMetaAnnotation) return false
|
||||
|
||||
if (classSymbol.isAnonymousObjectOrInsideIt) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED)
|
||||
return false
|
||||
}
|
||||
|
||||
if (classSymbol.isInner) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.INNER_CLASSES_NOT_SUPPORTED)
|
||||
return false
|
||||
}
|
||||
|
||||
if (classSymbol.isInline && !session.versionReader.canSupportInlineClasses) {
|
||||
reporter.reportOn(
|
||||
classSymbol.serializableOrMetaAnnotationSource,
|
||||
FirSerializationErrors.INLINE_CLASSES_NOT_SUPPORTED,
|
||||
RuntimeVersions.MINIMAL_VERSION_FOR_INLINE_CLASSES.toString(),
|
||||
session.versionReader.runtimeVersions?.implementationVersion.toString()
|
||||
)
|
||||
return false
|
||||
}
|
||||
|
||||
if (!classSymbol.hasSerializableOrMetaAnnotationWithoutArgs) {
|
||||
// defined custom serializer
|
||||
checkClassWithCustomSerializer(classSymbol, reporter)
|
||||
return false
|
||||
}
|
||||
|
||||
if (classSymbol.serializableAnnotationIsUseless) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED)
|
||||
return false
|
||||
}
|
||||
|
||||
// check that we can instantiate supertype
|
||||
if (!classSymbol.isEnumClass) { // enums are inherited from java.lang.Enum and can't be inherited from other classes
|
||||
val superClassSymbol = classSymbol.getSuperClassOrAny(session)
|
||||
if (!superClassSymbol.isInternalSerializable) {
|
||||
val noArgConstructorSymbol = superClassSymbol.declarationSymbols.firstOrNull { it is FirConstructorSymbol && it.valueParameterSymbols.isEmpty() }
|
||||
if (noArgConstructorSymbol == null) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR)
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkClassWithCustomSerializer(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||
val serializerType = classSymbol.serializableWith?.fullyExpandedType(session) ?: return
|
||||
checkCustomSerializerMatch(classSymbol, source = null, classSymbol.defaultType(), serializerType, reporter)
|
||||
checkCustomSerializerIsNotLocal(source = null, classSymbol, serializerType, reporter)
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private val FirClassSymbol<*>.isAnonymousObjectOrInsideIt: Boolean
|
||||
get() {
|
||||
if (this is FirAnonymousObjectSymbol) return true
|
||||
return containingDeclarations.any { it is FirAnonymousObject }
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkEnum(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||
if (!classSymbol.isEnumClass) return
|
||||
val entryBySerialName = mutableMapOf<String, FirEnumEntrySymbol>()
|
||||
for (enumEntrySymbol in classSymbol.collectEnumEntries()) {
|
||||
val serialNameAnnotation = enumEntrySymbol.serialNameAnnotation
|
||||
val serialName = serialNameAnnotation?.getStringArgument(AnnotationParameterNames.VALUE) ?: enumEntrySymbol.name.asString()
|
||||
val firstEntry = entryBySerialName[serialName]
|
||||
if (firstEntry != null) {
|
||||
reporter.reportOn(
|
||||
source = serialNameAnnotation?.source ?: firstEntry.serialNameAnnotation?.source ?: enumEntrySymbol.source,
|
||||
FirSerializationErrors.DUPLICATE_SERIAL_NAME_ENUM,
|
||||
classSymbol,
|
||||
serialName,
|
||||
enumEntrySymbol.name.asString()
|
||||
)
|
||||
} else {
|
||||
entryBySerialName[serialName] = enumEntrySymbol
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun buildSerializableProperties(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter): FirSerializableProperties? {
|
||||
if (!classSymbol.hasSerializableOrMetaAnnotation) return null
|
||||
if (!classSymbol.isInternalSerializable) return null
|
||||
if (classSymbol.hasCompanionObjectAsSerializer) return null
|
||||
|
||||
val properties = session.serializablePropertiesProvider.getSerializablePropertiesForClass(classSymbol)
|
||||
if (!properties.isExternallySerializable) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY)
|
||||
}
|
||||
|
||||
// check that all names are unique
|
||||
val namesSet = mutableSetOf<String>()
|
||||
for (property in properties.serializableProperties) {
|
||||
val name = property.name
|
||||
if (!namesSet.add(name)) {
|
||||
reporter.reportOn(classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.DUPLICATE_SERIAL_NAME, name)
|
||||
}
|
||||
}
|
||||
return properties
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkTransients(classSymbol: FirClassSymbol<*>, reporter: DiagnosticReporter) {
|
||||
for (propertySymbol in classSymbol.declarationSymbols.filterIsInstance<FirPropertySymbol>()) {
|
||||
val isInitialized = propertySymbol.isLateInit || declarationHasInitializer(propertySymbol)
|
||||
val transientAnnotation = propertySymbol.serialTransientAnnotation ?: continue
|
||||
val hasBackingField = propertySymbol.hasBackingField
|
||||
if (!hasBackingField) {
|
||||
reporter.reportOn(transientAnnotation.source ?: propertySymbol.source, FirSerializationErrors.TRANSIENT_IS_REDUNDANT)
|
||||
} else if (!isInitialized) {
|
||||
reporter.reportOn(propertySymbol.source, FirSerializationErrors.TRANSIENT_MISSING_INITIALIZER)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun declarationHasInitializer(propertySymbol: FirPropertySymbol): Boolean {
|
||||
return when {
|
||||
propertySymbol.fromPrimaryConstructor -> propertySymbol.correspondingValueParameterFromPrimaryConstructor?.hasDefaultValue ?: false
|
||||
else -> propertySymbol.hasInitializer || propertySymbol.hasDelegate
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun analyzePropertiesSerializers(
|
||||
classSymbol: FirClassSymbol<*>,
|
||||
properties: List<FirSerializableProperty>,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
for (property in properties) {
|
||||
// TODO: reporting diagnostics on properties from superclasses looks a bad idea
|
||||
val serializerType = property.serializableWith
|
||||
val serializerSymbol = serializerType?.toRegularClassSymbol(session)
|
||||
val propertySymbol = property.propertySymbol
|
||||
val typeRef = propertySymbol.resolvedReturnTypeRef
|
||||
val propertyType = typeRef.coneType
|
||||
val source = typeRef.source ?: propertySymbol.source
|
||||
if (serializerType != null && serializerSymbol != null) {
|
||||
checkCustomSerializerMatch(
|
||||
classSymbol,
|
||||
source = typeRef.source ?: propertySymbol.source,
|
||||
propertyType,
|
||||
serializerType,
|
||||
reporter
|
||||
)
|
||||
checkCustomSerializerIsNotLocal(
|
||||
source = propertySymbol.serializableAnnotation(needArguments = false)?.source,
|
||||
classSymbol,
|
||||
serializerType,
|
||||
reporter
|
||||
)
|
||||
checkSerializerNullability(propertyType, serializerType, source, reporter)
|
||||
} else {
|
||||
checkType(propertyType, source, reporter)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkTypeArguments(type: ConeKotlinType, source: KtSourceElement?, reporter: DiagnosticReporter) {
|
||||
for (typeArgument in type.typeArguments) {
|
||||
checkType(
|
||||
typeArgument.type ?: continue,
|
||||
source,
|
||||
reporter
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun canSupportInlineClasses(): Boolean {
|
||||
return session.versionReader.canSupportInlineClasses
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private val ConeKotlinType.isUnsupportedInlineType: Boolean
|
||||
get() = isInlineClass(session) && !isPrimitiveOrNullablePrimitive
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkType(type: ConeKotlinType, source: KtSourceElement?, reporter: DiagnosticReporter) {
|
||||
if (type.lowerBoundIfFlexible().isTypeParameter) return // type parameters always have serializer stored in class' field
|
||||
if (type.isUnsupportedInlineType && !canSupportInlineClasses()) {
|
||||
reporter.reportOn(
|
||||
source,
|
||||
FirSerializationErrors.INLINE_CLASSES_NOT_SUPPORTED,
|
||||
RuntimeVersions.MINIMAL_VERSION_FOR_INLINE_CLASSES.toString(),
|
||||
session.versionReader.runtimeVersions?.implementationVersion.toString()
|
||||
)
|
||||
}
|
||||
|
||||
val serializer = findTypeSerializerOrContextUnchecked(type)
|
||||
if (serializer != null) {
|
||||
val classSymbol = type.toRegularClassSymbol(session) ?: return
|
||||
type.serializableWith?.fullyExpandedType(session)?.let { serializerType ->
|
||||
checkCustomSerializerMatch(classSymbol, source, type, serializerType, reporter)
|
||||
checkCustomSerializerIsNotLocal(source, classSymbol, serializerType, reporter)
|
||||
checkSerializerNullability(type, serializerType, source, reporter)
|
||||
}
|
||||
checkTypeArguments(type, source, reporter)
|
||||
} else {
|
||||
reporter.reportOn(source, FirSerializationErrors.SERIALIZER_NOT_FOUND, type)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkCustomSerializerMatch(
|
||||
containingClassSymbol: FirClassSymbol<*>,
|
||||
source: KtSourceElement?,
|
||||
declarationType: ConeKotlinType,
|
||||
serializerType: ConeKotlinType,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val serializerForType = serializerType.serializerForType(session) ?: return
|
||||
|
||||
val declarationTypeClassId = declarationType.classId
|
||||
if (declarationTypeClassId == null || declarationTypeClassId != serializerForType.classId) {
|
||||
// TODO: report on specific type argument (KT-53861)
|
||||
reporter.reportOn(
|
||||
source ?: containingClassSymbol.serializableOrMetaAnnotationSource,
|
||||
FirSerializationErrors.SERIALIZER_TYPE_INCOMPATIBLE,
|
||||
containingClassSymbol.defaultType(),
|
||||
serializerType,
|
||||
serializerForType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkCustomSerializerIsNotLocal(
|
||||
source: KtSourceElement?,
|
||||
classSymbol: FirClassSymbol<*>,
|
||||
serializerType: ConeKotlinType,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
val serializerClassId = serializerType.classId ?: return
|
||||
if (serializerClassId.isLocal) {
|
||||
reporter.reportOn(source ?: classSymbol.serializableOrMetaAnnotationSource, FirSerializationErrors.LOCAL_SERIALIZER_USAGE, serializerType)
|
||||
}
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private fun checkSerializerNullability(
|
||||
classType: ConeKotlinType,
|
||||
serializerType: ConeKotlinType,
|
||||
source: KtSourceElement?,
|
||||
reporter: DiagnosticReporter
|
||||
) {
|
||||
// @Serializable annotation has proper signature so this error would be caught in type checker
|
||||
val serializerForType = serializerType.serializerForType(session) ?: return
|
||||
if (!classType.isMarkedNullable && serializerForType.isMarkedNullable) {
|
||||
reporter.reportOn(source, FirSerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE, serializerType, classType)
|
||||
}
|
||||
}
|
||||
|
||||
// --------------------------------------------------------------------------------------
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private val FirClassSymbol<*>.isSerializableEnumWithMissingSerializer: Boolean
|
||||
get() {
|
||||
if (!isEnumClass) return false
|
||||
if (hasSerializableOrMetaAnnotation) return false
|
||||
if (hasAnySerialAnnotation) return true
|
||||
return collectEnumEntries().any { it.hasAnySerialAnnotation }
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private val FirClassSymbol<*>.serializableOrMetaAnnotationSource: KtSourceElement?
|
||||
get() {
|
||||
serializableAnnotation(needArguments = false)?.source?.let { return it }
|
||||
metaSerializableAnnotation(needArguments = false)?.source?.let { return it }
|
||||
return null
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
@Suppress("IncorrectFormatting") // KTIJ-22227
|
||||
private val FirClassSymbol<*>.serializableAnnotationIsUseless: Boolean
|
||||
get() = !classKind.isEnumClass &&
|
||||
hasSerializableOrMetaAnnotationWithoutArgs &&
|
||||
!isInternalSerializable &&
|
||||
!hasCompanionObjectAsSerializer &&
|
||||
!isSealedSerializableInterface
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* 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.fir.checkers
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnostic
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.KtDiagnosticRenderer
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.CommonRenderers
|
||||
import org.jetbrains.kotlin.fir.analysis.diagnostics.FirDiagnosticRenderers
|
||||
|
||||
object KtDefaultErrorMessagesSerialization {
|
||||
fun getRendererForDiagnostic(diagnostic: KtDiagnostic): KtDiagnosticRenderer {
|
||||
val factory = diagnostic.factory
|
||||
return MAP[factory] ?: factory.ktRenderer
|
||||
}
|
||||
|
||||
val MAP = KtDiagnosticFactoryToRendererMap("Serialization").apply {
|
||||
put(
|
||||
FirSerializationErrors.INLINE_CLASSES_NOT_SUPPORTED,
|
||||
"Inline classes require runtime serialization library version at least {0}, while your classpath has {1}.",
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING,
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.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."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.ANONYMOUS_OBJECTS_NOT_SUPPORTED,
|
||||
"Anonymous objects or contained in it classes can not be serializable."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.INNER_CLASSES_NOT_SUPPORTED,
|
||||
"Inner (with reference to outer this) serializable classes are not supported. Remove @Serializable annotation or 'inner' keyword."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.EXPLICIT_SERIALIZABLE_IS_REQUIRED,
|
||||
"Explicit @Serializable annotation on enum class is required when @SerialName or @SerialInfo annotations are used on its members."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.SERIALIZABLE_ANNOTATION_IGNORED,
|
||||
"@Serializable annotation without arguments can be used only on sealed interfaces." +
|
||||
"Non-sealed interfaces are polymorphically serializable by default."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.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"
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY,
|
||||
"This class is not serializable automatically because it has primary constructor parameters that are not properties"
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.DUPLICATE_SERIAL_NAME,
|
||||
"Serializable class has duplicate serial name of property ''{0}'', either in the class itself or its supertypes",
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.DUPLICATE_SERIAL_NAME_ENUM,
|
||||
"Enum class ''{0}'' has duplicate serial name ''{1}'' in entry ''{2}''",
|
||||
FirDiagnosticRenderers.SYMBOL,
|
||||
CommonRenderers.STRING,
|
||||
CommonRenderers.STRING
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.SERIALIZER_NOT_FOUND,
|
||||
"Serializer has not been found for type ''{0}''. " +
|
||||
"To use context serializer as fallback, explicitly annotate type or property with @Contextual",
|
||||
FirDiagnosticRenderers.RENDER_TYPE_WITH_ANNOTATIONS
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.SERIALIZER_NULLABILITY_INCOMPATIBLE,
|
||||
"Type ''{1}'' is non-nullable and therefore can not be serialized with serializer for nullable type ''{0}''",
|
||||
FirDiagnosticRenderers.RENDER_TYPE,
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.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.",
|
||||
FirDiagnosticRenderers.RENDER_TYPE,
|
||||
FirDiagnosticRenderers.RENDER_TYPE,
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.LOCAL_SERIALIZER_USAGE,
|
||||
"Class ''{0}'' can't be used as a serializer since it is local",
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.TRANSIENT_MISSING_INITIALIZER,
|
||||
"This property is marked as @Transient and therefore must have an initializing expression"
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.TRANSIENT_IS_REDUNDANT,
|
||||
"Property does not have backing field which makes it non-serializable and therefore @Transient is redundant"
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.INCORRECT_TRANSIENT,
|
||||
"@kotlin.jvm.Transient does not affect @Serializable classes. Please use @kotlinx.serialization.Transient instead."
|
||||
)
|
||||
put(
|
||||
FirSerializationErrors.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
|
||||
)
|
||||
|
||||
put(
|
||||
FirSerializationErrors.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
|
||||
)
|
||||
|
||||
put(
|
||||
FirSerializationErrors.INCONSISTENT_INHERITABLE_SERIALINFO,
|
||||
"Argument values for inheritable serial info annotation ''{0}'' must be the same as the values in parent type ''{1}''",
|
||||
FirDiagnosticRenderers.RENDER_TYPE,
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
|
||||
put(
|
||||
FirSerializationErrors.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",
|
||||
FirDiagnosticRenderers.SYMBOL,
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
|
||||
put(
|
||||
FirSerializationErrors.EXTERNAL_CLASS_IN_ANOTHER_MODULE,
|
||||
"Cannot generate external serializer ''{0}'': class ''{1}'' is defined in another module",
|
||||
FirDiagnosticRenderers.SYMBOL,
|
||||
FirDiagnosticRenderers.RENDER_TYPE
|
||||
)
|
||||
}
|
||||
}
|
||||
+156
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* 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.fir.services
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.context.CheckerContext
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.FirFile
|
||||
import org.jetbrains.kotlin.fir.declarations.getAnnotationByClassId
|
||||
import org.jetbrains.kotlin.fir.declarations.getTargetType
|
||||
import org.jetbrains.kotlin.fir.declarations.hasAnnotation
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.isEnumClass
|
||||
import org.jetbrains.kotlin.fir.expressions.*
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.types.*
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.runIf
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.serializableWith
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.*
|
||||
|
||||
|
||||
class ContextualSerializersProvider(session: FirSession) : FirExtensionSessionComponent(session) {
|
||||
private val contextualKClassListCache: FirCache<FirFile, Set<ConeKotlinType>, Nothing?> =
|
||||
session.firCachesFactory.createCache { file ->
|
||||
buildSet {
|
||||
addAll(getKClassListFromFileAnnotation(file, SerializationAnnotations.contextualClassId))
|
||||
addAll(getKClassListFromFileAnnotation(file, SerializationAnnotations.contextualOnFileClassId))
|
||||
}
|
||||
}
|
||||
|
||||
fun getContextualKClassListForFile(file: FirFile): Set<ConeKotlinType> {
|
||||
return contextualKClassListCache.getValue(file)
|
||||
}
|
||||
|
||||
private val additionalSerializersInScopeCache: FirCache<FirFile, Map<Pair<FirClassSymbol<*>, Boolean>, FirClassSymbol<*>>, Nothing?> =
|
||||
session.firCachesFactory.createCache { file ->
|
||||
getKClassListFromFileAnnotation(file, SerializationAnnotations.additionalSerializersClassId).associateBy(
|
||||
keySelector = {
|
||||
val serializerType = it.serializerForType(session)
|
||||
val symbol = serializerType?.toRegularClassSymbol(session)
|
||||
?: throw AssertionError("Argument for ${SerializationAnnotations.additionalSerializersFqName} does not implement KSerializer or does not provide serializer for concrete type")
|
||||
symbol to serializerType.isMarkedNullable
|
||||
},
|
||||
valueTransform = { it.toRegularClassSymbol(session)!! }
|
||||
)
|
||||
}
|
||||
|
||||
fun getAdditionalSerializersInScopeForFile(file: FirFile): Map<Pair<FirClassSymbol<*>, Boolean>, FirClassSymbol<*>> {
|
||||
return additionalSerializersInScopeCache.getValue(file)
|
||||
}
|
||||
|
||||
private fun getKClassListFromFileAnnotation(file: FirFile, annotationClassId: ClassId): List<ConeKotlinType> {
|
||||
val annotation = file.symbol.resolvedAnnotationsWithArguments.getAnnotationByClassId(annotationClassId) ?: return emptyList()
|
||||
val arguments = when (val argument = annotation.argumentMapping.mapping.values.firstOrNull()) {
|
||||
is FirArrayOfCall -> argument.arguments
|
||||
is FirVarargArgumentsExpression -> argument.arguments
|
||||
else -> return emptyList()
|
||||
}
|
||||
return arguments.mapNotNull { (it as? FirGetClassCall)?.getTargetType() }
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.contextualSerializersProvider: ContextualSerializersProvider by FirSession.sessionComponentAccessor()
|
||||
|
||||
context(CheckerContext)
|
||||
fun findTypeSerializerOrContextUnchecked(type: ConeKotlinType): FirClassSymbol<*>? {
|
||||
if (type.isTypeParameter) return null
|
||||
val annotations = type.customAnnotations
|
||||
annotations.serializableWith?.let { return it.toRegularClassSymbol(session) }
|
||||
val classSymbol = type.toRegularClassSymbol(session) ?: return null
|
||||
val currentFile = currentFile
|
||||
val provider = session.contextualSerializersProvider
|
||||
provider.getAdditionalSerializersInScopeForFile(currentFile)[classSymbol to type.isMarkedNullable]?.let { return it }
|
||||
if (type.isMarkedNullable) {
|
||||
return findTypeSerializerOrContextUnchecked(type.withNullability(ConeNullability.NOT_NULL, session.typeContext))
|
||||
}
|
||||
if (type in provider.getContextualKClassListForFile(currentFile)) {
|
||||
return session.dependencySerializationInfoProvider.getClassFromSerializationPackage(SpecialBuiltins.Names.contextSerializer)
|
||||
}
|
||||
return analyzeSpecialSerializers(session, annotations) ?: findTypeSerializer(type)
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns class descriptor for ContextSerializer or PolymorphicSerializer
|
||||
* if [annotations] contains @Contextual or @Polymorphic annotation
|
||||
*/
|
||||
fun analyzeSpecialSerializers(session: FirSession, annotations: List<FirAnnotation>): FirClassSymbol<*>? = when {
|
||||
annotations.hasAnnotation(SerializationAnnotations.contextualClassId) ||
|
||||
annotations.hasAnnotation(SerializationAnnotations.contextualOnPropertyClassId) -> {
|
||||
session.dependencySerializationInfoProvider.getClassFromSerializationPackage(SpecialBuiltins.Names.contextSerializer)
|
||||
}
|
||||
// can be annotation on type usage, e.g. List<@Polymorphic Any>
|
||||
annotations.hasAnnotation(SerializationAnnotations.polymorphicClassId) -> {
|
||||
session.dependencySerializationInfoProvider.getClassFromSerializationPackage(SpecialBuiltins.Names.polymorphicSerializer)
|
||||
}
|
||||
|
||||
else -> null
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
fun findTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
|
||||
val userOverride = type.overriddenSerializer
|
||||
if (userOverride != null) return userOverride.toRegularClassSymbol(session)
|
||||
if (type.isTypeParameter) return null
|
||||
val serializationProvider = session.dependencySerializationInfoProvider
|
||||
if (type.isArrayType) {
|
||||
return serializationProvider.getClassFromInternalSerializationPackage(SpecialBuiltins.Names.referenceArraySerializer)
|
||||
}
|
||||
if (type.isGeneratedSerializableObject) {
|
||||
return serializationProvider.getClassFromInternalSerializationPackage(SpecialBuiltins.Names.objectSerializer)
|
||||
}
|
||||
// see if there is a standard serializer
|
||||
val standardSerializer = findStandardKotlinTypeSerializer(type) ?: findEnumTypeSerializer(type)
|
||||
if (standardSerializer != null) return standardSerializer
|
||||
val symbol = type.toRegularClassSymbol(session) ?: return null
|
||||
if (symbol.isSealedSerializableInterface) {
|
||||
return serializationProvider.getClassFromSerializationPackage(SpecialBuiltins.Names.polymorphicSerializer)
|
||||
}
|
||||
return symbol.classSerializer // check for serializer defined on the type
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
fun findStandardKotlinTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
|
||||
val name = when {
|
||||
type.isBoolean -> PrimitiveBuiltins.booleanSerializer
|
||||
type.isByte -> PrimitiveBuiltins.byteSerializer
|
||||
type.isShort -> PrimitiveBuiltins.shortSerializer
|
||||
type.isInt -> PrimitiveBuiltins.intSerializer
|
||||
type.isLong -> PrimitiveBuiltins.longSerializer
|
||||
type.isFloat -> PrimitiveBuiltins.floatSerializer
|
||||
type.isDouble -> PrimitiveBuiltins.doubleSerializer
|
||||
type.isChar -> PrimitiveBuiltins.charSerializer
|
||||
else -> findStandardKotlinTypeSerializerName(type.classId?.asFqNameString())
|
||||
}?.let(Name::identifier) ?: return null
|
||||
val symbolProvider = session.symbolProvider
|
||||
return symbolProvider.getClassLikeSymbolByClassId(ClassId(SerializationPackages.internalPackageFqName, name)) as? FirClassSymbol<*>
|
||||
?: symbolProvider.getClassLikeSymbolByClassId(ClassId(SerializationPackages.packageFqName, name)) as? FirClassSymbol<*>
|
||||
}
|
||||
|
||||
context(CheckerContext)
|
||||
fun findEnumTypeSerializer(type: ConeKotlinType): FirClassSymbol<*>? {
|
||||
val symbol = type.toRegularClassSymbol(session) ?: return null
|
||||
return runIf(symbol.isEnumClass && !symbol.isEnumWithLegacyGeneratedSerializer) {
|
||||
session.symbolProvider.getClassLikeSymbolByClassId(SerializersClassIds.enumSerializerId) as? FirClassSymbol<*>
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* 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.fir.services
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.name.ClassId
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
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.SerializationPackages
|
||||
|
||||
class DependencySerializationInfoProvider(session: FirSession) : FirExtensionSessionComponent(session) {
|
||||
val useGeneratedEnumSerializer by session.firCachesFactory.createLazyValue {
|
||||
val enumSerializerFactory = session.symbolProvider
|
||||
.getTopLevelFunctionSymbols(SerializationPackages.internalPackageFqName, ENUM_SERIALIZER_FACTORY_FUNC_NAME)
|
||||
|
||||
val markedEnumSerializerFactory = session.symbolProvider
|
||||
.getTopLevelFunctionSymbols(SerializationPackages.internalPackageFqName, MARKED_ENUM_SERIALIZER_FACTORY_FUNC_NAME)
|
||||
|
||||
enumSerializerFactory.isEmpty() || markedEnumSerializerFactory.isEmpty()
|
||||
}
|
||||
|
||||
private val classesFromSerializationPackageCache: FirCache<Name, FirClassSymbol<*>, Nothing?> = session.firCachesFactory.createCache { name ->
|
||||
SerializationPackages.allPublicPackages.firstNotNullOfOrNull { packageName ->
|
||||
session.symbolProvider.getClassLikeSymbolByClassId(ClassId(packageName, name)) as? FirClassSymbol<*>
|
||||
} ?: throw IllegalArgumentException("Can't locate cass ${name.identifier}")
|
||||
}
|
||||
|
||||
fun getClassFromSerializationPackage(name: Name): FirClassSymbol<*> {
|
||||
return classesFromSerializationPackageCache.getValue(name)
|
||||
}
|
||||
|
||||
private val classesFromInternalSerializationPackageCache: FirCache<Name, FirClassSymbol<*>, Nothing?> = session.firCachesFactory.createCache { name ->
|
||||
session.symbolProvider
|
||||
.getClassLikeSymbolByClassId(ClassId(SerializationPackages.internalPackageFqName, name)) as? FirClassSymbol<*>
|
||||
?: throw IllegalArgumentException("Can't locate cass ${name.identifier}")
|
||||
}
|
||||
|
||||
fun getClassFromInternalSerializationPackage(name: Name): FirClassSymbol<*> {
|
||||
return classesFromInternalSerializationPackageCache.getValue(name)
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.dependencySerializationInfoProvider: DependencySerializationInfoProvider by FirSession.sessionComponentAccessor()
|
||||
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.fir.services
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.analysis.checkers.declaration.primaryConstructorSymbol
|
||||
import org.jetbrains.kotlin.fir.caches.FirCache
|
||||
import org.jetbrains.kotlin.fir.caches.createCache
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.correspondingValueParameterFromPrimaryConstructor
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.visibility
|
||||
import org.jetbrains.kotlin.fir.expressions.toResolvedCallableReference
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
|
||||
import org.jetbrains.kotlin.fir.references.impl.FirPropertyFromParameterResolvedNamedReference
|
||||
import org.jetbrains.kotlin.fir.scopes.impl.declaredMemberScope
|
||||
import org.jetbrains.kotlin.fir.scopes.processAllProperties
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirClassSymbol
|
||||
import org.jetbrains.kotlin.fir.symbols.impl.FirValueParameterSymbol
|
||||
import org.jetbrains.kotlin.utils.addIfNotNull
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.*
|
||||
import org.jetbrains.kotlinx.serialization.compiler.fir.isInternalSerializable
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.ISerializableProperty
|
||||
|
||||
class FirSerializablePropertiesProvider(session: FirSession) : FirExtensionSessionComponent(session) {
|
||||
private val cache: FirCache<FirClassSymbol<*>, FirSerializableProperties, Nothing?> =
|
||||
session.firCachesFactory.createCache(this::createSerializableProperties)
|
||||
|
||||
fun getSerializablePropertiesForClass(classSymbol: FirClassSymbol<*>): FirSerializableProperties {
|
||||
return cache.getValue(classSymbol)
|
||||
}
|
||||
|
||||
private fun createSerializableProperties(classSymbol: FirClassSymbol<*>): FirSerializableProperties {
|
||||
val allPropertySymbols = buildList {
|
||||
classSymbol
|
||||
.declaredMemberScope(session)
|
||||
.processAllProperties {
|
||||
addIfNotNull(it as? FirPropertySymbol)
|
||||
}
|
||||
}
|
||||
|
||||
val primaryConstructorProperties = allPropertySymbols.mapNotNull {
|
||||
val parameterSymbol = it.correspondingValueParameterFromPrimaryConstructor ?: return@mapNotNull null
|
||||
it to parameterSymbol.hasDefaultValue
|
||||
}.toMap().withDefault { false }
|
||||
|
||||
val isInternalSerializable = classSymbol.isInternalSerializable
|
||||
|
||||
fun isPropertySerializable(propertySymbol: FirPropertySymbol): Boolean {
|
||||
return when {
|
||||
isInternalSerializable -> !propertySymbol.hasSerialTransient
|
||||
propertySymbol.visibility == Visibilities.Private -> false
|
||||
else -> (propertySymbol.isVar && propertySymbol.hasSerialTransient) || propertySymbol in primaryConstructorProperties
|
||||
}
|
||||
}
|
||||
|
||||
val serializableProperties: List<FirSerializableProperty> = allPropertySymbols.asSequence()
|
||||
.filter { isPropertySerializable(it) }
|
||||
.map {
|
||||
val declaresDefaultValue = it.declaresDefaultValue()
|
||||
FirSerializableProperty(
|
||||
session,
|
||||
it,
|
||||
primaryConstructorProperties.getValue(it),
|
||||
declaresDefaultValue
|
||||
)
|
||||
}
|
||||
.filterNot { it.transient }
|
||||
.partition { it.propertySymbol in primaryConstructorProperties }
|
||||
.let { (fromConstructor, standalone) ->
|
||||
val superClassSymbol = classSymbol.getSuperClassNotAny(session)
|
||||
buildList {
|
||||
if (superClassSymbol != null && superClassSymbol.isInternalSerializable) {
|
||||
addAll(getSerializablePropertiesForClass(superClassSymbol).serializableProperties)
|
||||
}
|
||||
addAll(fromConstructor)
|
||||
addAll(standalone)
|
||||
}
|
||||
}
|
||||
.let { restoreCorrectOrderFromClassProtoExtension(classSymbol, it) }
|
||||
|
||||
val isExternallySerializable = classSymbol.isInternallySerializableEnum ||
|
||||
primaryConstructorProperties.size == classSymbol.primaryConstructorSymbol()?.valueParameterSymbols?.size
|
||||
|
||||
val (serializableConstructorProperties, serializableStandaloneProperties) = serializableProperties.partition { it.propertySymbol in primaryConstructorProperties }
|
||||
return FirSerializableProperties(
|
||||
serializableProperties, isExternallySerializable, serializableConstructorProperties, serializableStandaloneProperties
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.serializablePropertiesProvider: FirSerializablePropertiesProvider by FirSession.sessionComponentAccessor()
|
||||
|
||||
fun FirPropertySymbol.declaresDefaultValue(): Boolean {
|
||||
if (hasInitializer) return true
|
||||
// TODO: handle deserialized properties
|
||||
return false
|
||||
}
|
||||
|
||||
@Suppress("UNUSED_PARAMETER")
|
||||
fun <P : ISerializableProperty> restoreCorrectOrderFromClassProtoExtension(classSymbol: FirClassSymbol<*>, props: List<P>): List<P> {
|
||||
// TODO: handle deserialized properties
|
||||
return props
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* 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.fir.services
|
||||
|
||||
import org.jetbrains.kotlin.fir.FirSession
|
||||
import org.jetbrains.kotlin.fir.caches.firCachesFactory
|
||||
import org.jetbrains.kotlin.fir.caches.getValue
|
||||
import org.jetbrains.kotlin.fir.declarations.utils.sourceElement
|
||||
import org.jetbrains.kotlin.fir.extensions.FirExtensionSessionComponent
|
||||
import org.jetbrains.kotlin.fir.resolve.providers.symbolProvider
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.CommonVersionReader
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.RuntimeVersions
|
||||
import org.jetbrains.kotlinx.serialization.compiler.resolve.SerialEntityNames
|
||||
|
||||
class FirVersionReader(session: FirSession) : FirExtensionSessionComponent(session) {
|
||||
val runtimeVersions: RuntimeVersions? by session.firCachesFactory.createLazyValue lazy@{
|
||||
val markerClass = session.symbolProvider.getClassLikeSymbolByClassId(SerialEntityNames.KSERIALIZER_CLASS_ID) ?: return@lazy null
|
||||
CommonVersionReader.computeRuntimeVersions(markerClass.sourceElement)
|
||||
}
|
||||
|
||||
val canSupportInlineClasses by session.firCachesFactory.createLazyValue lazy@{
|
||||
CommonVersionReader.canSupportInlineClasses(runtimeVersions)
|
||||
}
|
||||
}
|
||||
|
||||
val FirSession.versionReader: FirVersionReader by FirSession.sessionComponentAccessor()
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable
|
||||
open class Parent(open val arg: Int)
|
||||
|
||||
@Serializable
|
||||
class Derived(override val arg: Int): Parent(arg)
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
import kotlinx.serialization.*
|
||||
|
||||
|
||||
@Serializable
|
||||
enum class ImplicitlyDuplicated {
|
||||
@SerialName("foo")
|
||||
FIRST,
|
||||
@SerialName("foo")
|
||||
SECOND
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class ExplicitlyDuplicated {
|
||||
FIRST,
|
||||
SECOND,
|
||||
@SerialName("FIRST")
|
||||
THIRD
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class ReversedExplicitlyDuplicated {
|
||||
@SerialName("THIRD")
|
||||
FIRST,
|
||||
SECOND,
|
||||
THIRD
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import java.io.Serializable as JavaSerializable
|
||||
|
||||
@Serializable
|
||||
class Data(val x: Int, @Transient val y: String)
|
||||
|
||||
@Serializable
|
||||
class Data2(val x: Int, @Transient val y: String) : JavaSerializable
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
-19
@@ -1,19 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlinx.serialization.Transient
|
||||
import java.io.Serializable as JavaSerializable
|
||||
import kotlin.jvm.Transient as JavaTransient
|
||||
|
||||
@Serializable
|
||||
class Data(val x: Int, @Transient val y: String = "a")
|
||||
|
||||
@Serializable
|
||||
class Data2(val x: Int, @Transient val y: String = "a") : JavaSerializable
|
||||
|
||||
@Serializable
|
||||
class Data3(val x: Int, @Transient @JavaTransient val y: String = "a") : JavaSerializable
|
||||
|
||||
@Serializable
|
||||
class Data4(val x: Int, @JavaTransient val y: String)
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
@@ -1,25 +1,24 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
// !USE_EXPERIMENTAL: kotlinx.serialization.ExperimentalSerializationApi
|
||||
// USE_EXPERIMENTAL: kotlinx.serialization.ExperimentalSerializationApi
|
||||
// FILE: test.kt
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlin.reflect.KClass
|
||||
|
||||
// TODO: for this test to work, runtime dependency should be updated to (yet unreleased) serialization 1.3.0
|
||||
//@InheritableSerialInfo
|
||||
@InheritableSerialInfo
|
||||
annotation class I(val value: String)
|
||||
|
||||
enum class E { A, B }
|
||||
|
||||
//@InheritableSerialInfo
|
||||
@InheritableSerialInfo
|
||||
annotation class I2(val e: E, val k: KClass<*>)
|
||||
|
||||
@Serializable
|
||||
@I("a")
|
||||
sealed class Result {
|
||||
// @I("b")
|
||||
<!INCONSISTENT_INHERITABLE_SERIALINFO!>@I("b")<!>
|
||||
@Serializable class OK(val s: String): Result()
|
||||
}
|
||||
|
||||
@@ -35,15 +34,15 @@ open class Correct: A()
|
||||
|
||||
@Serializable
|
||||
@I("a")
|
||||
//@I2(E.B, E::class)
|
||||
<!INCONSISTENT_INHERITABLE_SERIALINFO!>@I2(E.B, E::class)<!>
|
||||
open class B: A()
|
||||
|
||||
@Serializable
|
||||
@I("a")
|
||||
//@I2(E.A, I::class)
|
||||
<!INCONSISTENT_INHERITABLE_SERIALINFO!>@I2(E.A, I::class)<!>
|
||||
open class B2: A()
|
||||
|
||||
@Serializable
|
||||
//@I("b")
|
||||
//@I2(E.A, E::class)
|
||||
<!INCONSISTENT_INHERITABLE_SERIALINFO!>@I("b")<!>
|
||||
<!INCONSISTENT_INHERITABLE_SERIALINFO!>@I2(E.A, E::class)<!>
|
||||
open class C: B()
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
|
||||
fun container() {
|
||||
@Serializable
|
||||
class X // local classes are allowed
|
||||
|
||||
val y = @Serializable object {
|
||||
fun inObjectFun() {
|
||||
@Serializable
|
||||
class X // local classes in anonymous object functions are not allowed
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class LocalSerializer : KSerializer<Any?> {
|
||||
override val descriptor: SerialDescriptor = buildSerialDescriptor("tmp", PrimitiveKind.INT)
|
||||
override fun serialize(encoder: Encoder, value: Any?) {
|
||||
encoder.encodeNull()
|
||||
}
|
||||
|
||||
override fun deserialize(decoder: Decoder): Any? {
|
||||
return decoder.decodeNull()
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class WithLocalSerializerInProperty(@Serializable(with = LocalSerializer::class) val x: Any?)
|
||||
|
||||
@Serializable(with = LocalSerializer::class)
|
||||
data class WithLocalSerializer(val i: Int)
|
||||
}
|
||||
|
||||
val topLevelAnon = @Serializable object {}
|
||||
|
||||
@Serializable class A {
|
||||
@Serializable class B // nested classes are allowed
|
||||
|
||||
@Serializable inner class C // inner classes are not
|
||||
|
||||
@Serializable object F {} // regular named object, OK
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// SKIP_TXT
|
||||
|
||||
|
||||
-8
@@ -1,8 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
open class NonSerializableParent(val arg: Int)
|
||||
|
||||
@Serializable
|
||||
class Derived(val someData: String): NonSerializableParent(42)
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
// WITH_STDLIB
|
||||
// FIR_DIFFERENCE: KT-53861
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
class NonSerializable
|
||||
|
||||
@Serializable
|
||||
class Basic(val foo: NonSerializable)
|
||||
class Basic(val foo: <!SERIALIZER_NOT_FOUND("NonSerializable")!>NonSerializable<!>)
|
||||
|
||||
@Serializable
|
||||
class Inside(val foo: List<NonSerializable>)
|
||||
class Inside(val foo: <!SERIALIZER_NOT_FOUND("NonSerializable")!>List<NonSerializable><!>)
|
||||
|
||||
@Serializable
|
||||
class WithImplicitType {
|
||||
val foo = NonSerializable()
|
||||
<!SERIALIZER_NOT_FOUND("NonSerializable")!>val foo = NonSerializable()<!>
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// WITH_STDLIB
|
||||
// FIR_DIFFERENCE: KT-53861
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
|
||||
-17
@@ -1,17 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
|
||||
@Serializable(NopeNullableSerializer::class)
|
||||
class Nope {}
|
||||
|
||||
class NopeNullableSerializer: KSerializer<Nope?> {
|
||||
override val descriptor: SerialDescriptor get() = TODO()
|
||||
override fun deserialize(decoder: Decoder): Nope? = TODO()
|
||||
override fun serialize(encoder: Encoder, value: Nope?) = TODO()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
class Foo(val foo: Nope)
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable
|
||||
class Test(val someData: String, cantBeDeserialized: Int)
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.encoding.*
|
||||
|
||||
enum class SimpleEnum { A, B }
|
||||
|
||||
enum class MarkedNameEnum { @SerialName("a") A, B}
|
||||
|
||||
@Serializable
|
||||
enum class ExplicitlyMarkedEnum { @SerialName("a") A, B}
|
||||
|
||||
@Serializable(EnumSerializer::class)
|
||||
enum class ExplicitlyMarkedEnumCustom { @SerialName("a") A, B}
|
||||
|
||||
object EnumSerializer: KSerializer<ExplicitlyMarkedEnumCustom> {
|
||||
override val descriptor = TODO()
|
||||
override fun serialize(encoder: Encoder, value: ExplicitlyMarkedEnumCustom) = TODO()
|
||||
override fun deserialize(decoder: Decoder): ExplicitlyMarkedEnumCustom = TODO()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class EnumUsage(val s: SimpleEnum, val m: MarkedNameEnum, val e: ExplicitlyMarkedEnum)
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// SKIP_TXT
|
||||
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable
|
||||
interface INonSerializable
|
||||
|
||||
@Serializable
|
||||
sealed interface SealedSerializable
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// SKIP_TXT
|
||||
|
||||
// FILE: test.kt
|
||||
|
||||
+6
-6
@@ -13,14 +13,14 @@ class Baz
|
||||
@Serializer(forClass = Baz::class)
|
||||
<!ABSTRACT_MEMBER_NOT_IMPLEMENTED!>object NullableBazSerializer<!>: KSerializer<Baz?>
|
||||
|
||||
@Serializable(with = BazSerializer::class)
|
||||
<!SERIALIZER_TYPE_INCOMPATIBLE!>@Serializable(with = BazSerializer::class)<!>
|
||||
class Biz(val i: Int)
|
||||
|
||||
@Serializable
|
||||
class Foo(@Serializable(with = BazSerializer::class) val i: Bar)
|
||||
class Foo(@Serializable(with = BazSerializer::class) val i: <!SERIALIZER_TYPE_INCOMPATIBLE!>Bar<!>)
|
||||
|
||||
@Serializable
|
||||
class Foo2(val li: List<@Serializable(with = BazSerializer::class) Bar>)
|
||||
class Foo2(val li: <!SERIALIZER_TYPE_INCOMPATIBLE!>List<@Serializable(with = BazSerializer::class) Bar><!>)
|
||||
|
||||
@Serializable
|
||||
class Foo3(@Serializable(with = BazSerializer::class) val i: Baz)
|
||||
@@ -29,10 +29,10 @@ class Foo3(@Serializable(with = BazSerializer::class) val i: Baz)
|
||||
class Foo4(val li: List<@Serializable(with = BazSerializer::class) Baz>)
|
||||
|
||||
@Serializable
|
||||
class Foo5(@Serializable(with = BazSerializer::class) val i: Bar?)
|
||||
class Foo5(@Serializable(with = BazSerializer::class) val i: <!SERIALIZER_TYPE_INCOMPATIBLE!>Bar?<!>)
|
||||
|
||||
@Serializable
|
||||
class Foo6(@Serializable(with = NullableBazSerializer::class) val i: Bar)
|
||||
class Foo6(@Serializable(with = NullableBazSerializer::class) val i: <!SERIALIZER_NULLABILITY_INCOMPATIBLE, SERIALIZER_TYPE_INCOMPATIBLE!>Bar<!>)
|
||||
|
||||
@Serializable
|
||||
class Foo7(@Serializable(with = NullableBazSerializer::class) val i: Bar?)
|
||||
class Foo7(@Serializable(with = NullableBazSerializer::class) val i: <!SERIALIZER_TYPE_INCOMPATIBLE!>Bar?<!>)
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
@Serializable
|
||||
data class WithTransients(@Transient val missing: Int) {
|
||||
@Transient val redundant: Int get() = 42
|
||||
|
||||
@Transient
|
||||
lateinit var allowTransientLateinitWithoutInitializer: String
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
// FIR_IDENTICAL
|
||||
// WITH_STDLIB
|
||||
// FILE: test.kt
|
||||
import kotlinx.serialization.*
|
||||
|
||||
+2
-1
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlinx.serialization
|
||||
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.CommonVersionReader
|
||||
import org.jetbrains.kotlinx.serialization.compiler.diagnostic.VersionReader
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
@@ -34,7 +35,7 @@ class RuntimeLibraryInClasspathTest {
|
||||
|
||||
@Test
|
||||
fun testRuntimeHasSufficientVersion() {
|
||||
val version = VersionReader.getVersionsFromManifest(coreLibraryPath!!)
|
||||
val version = CommonVersionReader.getVersionsFromManifest(coreLibraryPath!!)
|
||||
assertTrue(version.currentCompilerMatchRequired(), "Runtime version too high")
|
||||
assertTrue(version.implementationVersionMatchSupported(), "Runtime version too low")
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user