diff --git a/build.gradle.kts b/build.gradle.kts index 5741dbcd04c..80b9990a6ae 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -372,6 +372,7 @@ val projectsWithEnabledContextReceivers by extra { ":compiler:frontend.common", ":compiler:fir:fir2ir", ":kotlin-lombok-compiler-plugin.k1", + ":kotlinx-serialization-compiler-plugin.k2", ) } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt index 54277c6d670..20bd74aa90e 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/FirHelpers.kt @@ -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()?.containingClass()?.toSymbol(session) + (this as? FirCallableDeclaration)?.containingClass()?.toSymbol(session) @OptIn(SymbolInternals::class) fun FirBasedSymbol<*>.getContainingClassSymbol(session: FirSession): FirClassLikeSymbol<*>? = fir.getContainingClassSymbol(session) diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt index 974bc9b5040..81f6d9d5a87 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/FirAnnotationClassDeclarationChecker.kt @@ -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 - } } diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt index 3ea5b7bb9d0..b09dd153db5 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/checkers/declaration/declarationUtils.kt @@ -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 +} diff --git a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticRenderers.kt b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticRenderers.kt index 3becaa10076..40d2f04ed2d 100644 --- a/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticRenderers.kt +++ b/compiler/fir/checkers/src/org/jetbrains/kotlin/fir/analysis/diagnostics/FirDiagnosticRenderers.kt @@ -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( diff --git a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt index fd8ba97a34e..5613ba8142e 100644 --- a/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt +++ b/compiler/fir/cones/src/org/jetbrains/kotlin/fir/types/ConeBuiltinTypeUtils.kt @@ -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) diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt index 28aeabb1186..9d8bdf99210 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/FirAnnotationUtils.kt @@ -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 = USE_SITE_TARGET_NAME_MAP.values.fold(setOf()) { 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.hasAnnotation(classId: ClassId): Boolean { + return this.any { it.toAnnotationClassId() == classId } } fun FirBasedSymbol.getAnnotationByClassId(classId: ClassId): FirAnnotation? where D : FirAnnotationContainer, D : FirDeclaration { @@ -111,7 +112,11 @@ fun FirBasedSymbol.getAnnotationByClassId(classId: ClassId): FirAnnot } fun FirAnnotationContainer.getAnnotationByClassId(classId: ClassId): FirAnnotation? { - return annotations.find { + return annotations.getAnnotationByClassId(classId) +} + +fun List.getAnnotationByClassId(classId: ClassId): FirAnnotation? { + return find { it.annotationTypeRef.coneTypeSafe()?.lookupTag?.classId == classId } } @@ -173,6 +178,15 @@ private inline fun 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 -> diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt index 7f70392bd88..f611e5dbc91 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/declarations/declarationUtils.kt @@ -32,12 +32,13 @@ fun FirClass.primaryConstructorIfAny(session: FirSession): FirConstructorSymbol? return constructors(session).find(FirConstructorSymbol::isPrimary) } -fun FirRegularClass.collectEnumEntries(): Collection { +// TODO: dog shit, rewrite with scopes +fun FirClass.collectEnumEntries(): Collection { assert(classKind == ClassKind.ENUM_CLASS) return declarations.filterIsInstance() } -fun FirRegularClassSymbol.collectEnumEntries(): Collection { +fun FirClassSymbol<*>.collectEnumEntries(): Collection { return fir.collectEnumEntries().map { it.symbol } } diff --git a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt index 29ad8c8a239..f54b8ad3d47 100644 --- a/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt +++ b/compiler/fir/providers/src/org/jetbrains/kotlin/fir/scopes/impl/FirDeclaredMemberScopeProvider.kt @@ -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, diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt index b593685c004..590fa4e7330 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/caches/FirCacheWithPostCompute.kt @@ -5,6 +5,8 @@ package org.jetbrains.kotlin.fir.caches +import kotlin.reflect.KProperty + abstract class FirCache { abstract fun getValue(key: K, context: CONTEXT): V abstract fun getValueIfComputed(key: K): V? @@ -24,6 +26,10 @@ class FirLazyValue(private val cache: FirCache FirLazyValue.getValue(thisRef: Any?, property: KProperty<*>): V { + return getValue(context = null) +} + @Suppress("NOTHING_TO_INLINE") inline fun FirLazyValue.getValue(): V { return getValue(null) diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/declarationAttributes.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/declarationAttributes.kt index 605cfd694d0..e5e68377bfb 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/declarationAttributes.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/declarations/utils/declarationAttributes.kt @@ -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 { return when (this) { is FirRegularClass -> danglingTypeConstraints @@ -101,3 +118,11 @@ fun FirDeclaration.getDanglingTypeConstraintsOrEmpty(): List 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 + } diff --git a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt index d29f01dcc56..ad7e7a9e9ea 100644 --- a/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt +++ b/compiler/fir/tree/src/org/jetbrains/kotlin/fir/symbols/impl/FirVariableSymbol.kt @@ -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 diff --git a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt index 5c5cde61801..02899fa36dc 100644 --- a/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt +++ b/compiler/frontend.java/src/org/jetbrains/kotlin/resolve/jvm/annotations/jvmAnnotationUtil.kt @@ -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") diff --git a/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt b/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt index 68fde07bdf8..6c26357fd39 100644 --- a/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt +++ b/core/compiler.common/src/org/jetbrains/kotlin/descriptors/ClassKind.kt @@ -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 \ No newline at end of file +inline val ClassKind.isAnnotationClass: Boolean + get() = this == ClassKind.ANNOTATION_CLASS + +inline val ClassKind.isObject: Boolean + get() = this == ClassKind.OBJECT diff --git a/libraries/tools/kotlin-maven-serialization/pom.xml b/libraries/tools/kotlin-maven-serialization/pom.xml index cddc97228eb..78ca8218fe3 100755 --- a/libraries/tools/kotlin-maven-serialization/pom.xml +++ b/libraries/tools/kotlin-maven-serialization/pom.xml @@ -86,6 +86,7 @@ -Xjvm-default=enable -opt-in=org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI -opt-in=org.jetbrains.kotlin.compiler.plugin.ExperimentalCompilerApi + -Xcontext-receivers diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt index 0e143bd8dd7..dc7259913ec 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/BaseIrGenerator.kt @@ -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 ) } -} \ No newline at end of file +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt index a9fa0bce761..49cbcaf7256 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/ir/SerializerSearchUtil.kt @@ -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) } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt index e65bd7d2543..079533dd573 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/js/JsCodegenUtil.kt @@ -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() diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt index d864bf8124e..87c9a2f3d4f 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.backend/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/JVMCodegenUtil.kt @@ -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 diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/build.gradle.kts b/plugins/kotlinx-serialization/kotlinx-serialization.common/build.gradle.kts index fbfa0287d38..e59a61bb35f 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.common/build.gradle.kts +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/build.gradle.kts @@ -8,6 +8,7 @@ plugins { dependencies { compileOnly(project(":compiler:util")) compileOnly(project(":core:compiler.common")) + compileOnly(project(":core:deserialization.common.jvm")) compileOnly(intellijCore()) } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/RuntimeVersions.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/RuntimeVersions.kt new file mode 100644 index 00000000000..0a53961ac05 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/RuntimeVersions.kt @@ -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 + } +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/ISerializableProperties.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/ISerializableProperties.kt new file mode 100644 index 00000000000..db679c7b9f9 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/ISerializableProperties.kt @@ -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 { + val serializableProperties: List + val isExternallySerializable: Boolean + val serializableConstructorProperties: List + val serializableStandaloneProperties: List +} + +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 + 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.bitMaskSlotCount(): Int = size / 32 + 1 +fun bitMaskSlotAt(propertyIndex: Int): Int = propertyIndex / 32 diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/ISerializableProperty.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/ISerializableProperty.kt new file mode 100644 index 00000000000..4a2e46c6342 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/ISerializableProperty.kt @@ -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 +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt index a21bb740c09..8ca5526b9df 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.common/src/org/jetbrains/kotlinx/serialization/compiler/resolve/NamingConventions.kt @@ -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 + } +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt index 945c51eaebc..d96e2b90790 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/common/TypeUtil.kt @@ -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()) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializationJvmNames.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializationJvmNames.kt deleted file mode 100644 index de79b28f79d..00000000000 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/backend/jvm/SerializationJvmNames.kt +++ /dev/null @@ -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)) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt index 719e9cf541f..0e1dabf1745 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/SerializationPluginDeclarationChecker.kt @@ -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() ) ) diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt index 6342d2c7f45..640a9ac504d 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/diagnostic/VersionReader.kt @@ -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 = 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) } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt index 95fc84e6e1c..f7125a57381 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperties.kt @@ -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 { - val serializableProperties: List - val isExternallySerializable: Boolean - val serializableConstructorProperties: List - val serializableStandaloneProperties: List -} - class SerializableProperties(private val serializableClass: ClassDescriptor, val bindingContext: BindingContext) : ISerializableProperties { private val primaryConstructorParameters: List = @@ -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 - 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.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 restoreCorrectOrderFromClassProtoExtension(descriptor: ClassDescriptor, props: List

): List

{ +fun

restoreCorrectOrderFromClassProtoExtension(descriptor: ClassDescriptor, props: List

): List

{ if (descriptor !is DeserializedClassDescriptor) return props val correctOrder: List = descriptor.classProto.getExtension(SerializationPluginMetadataExtensions.propertiesNamesInProgramOrder) .map { descriptor.c.nameResolver.getName(it) } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt index 167d8870046..14cf1579289 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k1/src/org/jetbrains/kotlinx/serialization/compiler/resolve/SerializableProperty.kt @@ -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, diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializableProperty.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializableProperty.kt new file mode 100644 index 00000000000..04c604248f8 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializableProperty.kt @@ -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, + override val isExternallySerializable: Boolean, + override val serializableConstructorProperties: List, + override val serializableStandaloneProperties: List, +) : ISerializableProperties diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationExtensionRegistrar.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationExtensionRegistrar.kt index c7a07a2ffe5..ed2fd402a8b 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationExtensionRegistrar.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/FirSerializationExtensionRegistrar.kt @@ -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 } } diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt index 393144cfec5..a887778a714 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirResolveExtension.kt @@ -135,7 +135,7 @@ class SerializationFirResolveExtension(session: FirSession) : FirDeclarationGene override fun generateFunctions(callableId: CallableId, context: MemberGenerationContext?): List { 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() diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt index 5ff6e1811cb..4387d096ba4 100644 --- a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/SerializationFirUtils.kt @@ -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.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.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 -} \ No newline at end of file +} + +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 diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationCheckersComponent.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationCheckersComponent.kt new file mode 100644 index 00000000000..01224d003a5 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationCheckersComponent.kt @@ -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 = setOf(FirSerializationPluginClassChecker) + } +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt new file mode 100644 index 00000000000..ca35caec0b9 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationErrors.kt @@ -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() + + val PLUGIN_IS_NOT_ENABLED by warning0() + val ANONYMOUS_OBJECTS_NOT_SUPPORTED by error0() + val INNER_CLASSES_NOT_SUPPORTED by error0() + + val EXPLICIT_SERIALIZABLE_IS_REQUIRED by warning0() + + val SERIALIZABLE_ANNOTATION_IGNORED by error0() + val NON_SERIALIZABLE_PARENT_MUST_HAVE_NOARG_CTOR by error0() + val PRIMARY_CONSTRUCTOR_PARAMETER_IS_NOT_A_PROPERTY by error0() + val DUPLICATE_SERIAL_NAME by error1() + val DUPLICATE_SERIAL_NAME_ENUM by error3, String, String>() + val SERIALIZER_NOT_FOUND by error1() + val SERIALIZER_NULLABILITY_INCOMPATIBLE by error2() + val SERIALIZER_TYPE_INCOMPATIBLE by warning3() + val LOCAL_SERIALIZER_USAGE by error1() + val TRANSIENT_MISSING_INITIALIZER by error0() + + val TRANSIENT_IS_REDUNDANT by warning0() + + val INCORRECT_TRANSIENT by warning0() + + val REQUIRED_KOTLIN_TOO_HIGH by error3() + val PROVIDED_RUNTIME_TOO_LOW by error3() + + val INCONSISTENT_INHERITABLE_SERIALINFO by error2() + + val EXTERNAL_CLASS_NOT_SERIALIZABLE by error2, ConeKotlinType>() + val EXTERNAL_CLASS_IN_ANOTHER_MODULE by error2, ConeKotlinType>() +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt new file mode 100644 index 00000000000..7385789b1b6 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/FirSerializationPluginClassChecker.kt @@ -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): List> { + 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, + 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() + 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() + 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()) { + 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, + 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 +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt new file mode 100644 index 00000000000..910785e7726 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/checkers/KtDefaultErrorMessagesSerialization.kt @@ -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 + ) + } +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt new file mode 100644 index 00000000000..ba7d764f60c --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/ContextualSerializersProvider.kt @@ -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, Nothing?> = + session.firCachesFactory.createCache { file -> + buildSet { + addAll(getKClassListFromFileAnnotation(file, SerializationAnnotations.contextualClassId)) + addAll(getKClassListFromFileAnnotation(file, SerializationAnnotations.contextualOnFileClassId)) + } + } + + fun getContextualKClassListForFile(file: FirFile): Set { + return contextualKClassListCache.getValue(file) + } + + private val additionalSerializersInScopeCache: FirCache, 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, Boolean>, FirClassSymbol<*>> { + return additionalSerializersInScopeCache.getValue(file) + } + + private fun getKClassListFromFileAnnotation(file: FirFile, annotationClassId: ClassId): List { + 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): 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<*> + } +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/DependencySerializationInfoProvider.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/DependencySerializationInfoProvider.kt new file mode 100644 index 00000000000..bf7b7a34714 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/DependencySerializationInfoProvider.kt @@ -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, 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, 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() + diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt new file mode 100644 index 00000000000..7f86d354c47 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirSerializablePropertiesProvider.kt @@ -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, 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 = 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

restoreCorrectOrderFromClassProtoExtension(classSymbol: FirClassSymbol<*>, props: List

): List

{ + // TODO: handle deserialized properties + return props +} diff --git a/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirVersionReader.kt b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirVersionReader.kt new file mode 100644 index 00000000000..f8682fd3d67 --- /dev/null +++ b/plugins/kotlinx-serialization/kotlinx-serialization.k2/src/org/jetbrains/kotlinx/serialization/compiler/fir/services/FirVersionReader.kt @@ -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() + diff --git a/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.fir.kt deleted file mode 100644 index 5d9186265cf..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.fir.kt +++ /dev/null @@ -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) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.kt b/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.kt index 88498ea9db9..bf540162333 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/DuplicateSerialName.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.fir.kt deleted file mode 100644 index 5601a709a4b..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.fir.kt +++ /dev/null @@ -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 -} diff --git a/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.kt b/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.kt index 89ab930ebe3..0de11f60c92 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/EnumDuplicateSerialName.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // SKIP_TXT import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.fir.kt deleted file mode 100644 index ce7bac3efe0..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.fir.kt +++ /dev/null @@ -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 diff --git a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.kt b/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.kt index e350948f1b7..724e7330cc6 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // SKIP_TXT diff --git a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.fir.kt deleted file mode 100644 index 5b4ce4a6d4e..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.fir.kt +++ /dev/null @@ -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) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.kt b/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.kt index b870d8f86f4..b33843a08ab 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/IncorrectTransient2.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // SKIP_TXT diff --git a/plugins/kotlinx-serialization/testData/diagnostics/InheritableInfo.kt b/plugins/kotlinx-serialization/testData/diagnostics/InheritableInfo.kt index b7e705bfc95..523a8b1708d 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/InheritableInfo.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/InheritableInfo.kt @@ -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") + @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) +@I2(E.B, E::class) open class B: A() @Serializable @I("a") -//@I2(E.A, I::class) +@I2(E.A, I::class) open class B2: A() @Serializable -//@I("b") -//@I2(E.A, E::class) +@I("b") +@I2(E.A, E::class) open class C: B() diff --git a/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.fir.kt deleted file mode 100644 index 6b52bd6bb95..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.fir.kt +++ /dev/null @@ -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 { - 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 -} diff --git a/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.kt b/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.kt index 207ccf55629..815a1e7367e 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/LocalAndAnonymous.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // SKIP_TXT diff --git a/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.fir.kt deleted file mode 100644 index c15f8293837..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.fir.kt +++ /dev/null @@ -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) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.kt b/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.kt index 0b7e907d781..3f7ec34dd91 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/NoSuitableCtorInParent.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.fir.kt index 27b49634951..7155b4d22a1 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.fir.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.fir.kt @@ -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: NonSerializable) @Serializable -class Inside(val foo: List) +class Inside(val foo: List) @Serializable class WithImplicitType { - val foo = NonSerializable() + val foo = NonSerializable() } diff --git a/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.kt b/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.kt index 8daa9cc5102..30e66f04a4f 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/NonSerializable.kt @@ -1,4 +1,5 @@ // WITH_STDLIB +// FIR_DIFFERENCE: KT-53861 // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.fir.kt deleted file mode 100644 index 1d951491b9e..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.fir.kt +++ /dev/null @@ -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 { - 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) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.kt b/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.kt index f35c86bd684..95242c14ed3 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/NullabilityIncompatible.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.fir.kt deleted file mode 100644 index 1136d34460e..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.fir.kt +++ /dev/null @@ -1,6 +0,0 @@ -// WITH_STDLIB -// FILE: test.kt -import kotlinx.serialization.* - -@Serializable -class Test(val someData: String, cantBeDeserialized: Int) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt b/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt index 05d011fdc19..235706b4d29 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/ParamIsNotProperty.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.fir.kt deleted file mode 100644 index b3303fd6bb6..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.fir.kt +++ /dev/null @@ -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 { - 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) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.kt index d09709832ce..bb2baac4f16 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/SerializableEnums.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.fir.kt deleted file mode 100644 index 5a9faf9a06f..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.fir.kt +++ /dev/null @@ -1,10 +0,0 @@ -// SKIP_TXT - -// FILE: test.kt -import kotlinx.serialization.* - -@Serializable -interface INonSerializable - -@Serializable -sealed interface SealedSerializable diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.kt index bfcb9ea8bb6..59e8d8b0cb5 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/SerializableIgnored.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // SKIP_TXT // FILE: test.kt diff --git a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt index 21137764862..0f49c9e3195 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/SerializerTypeIncompatible.fir.kt @@ -13,14 +13,14 @@ class Baz @Serializer(forClass = Baz::class) object NullableBazSerializer: KSerializer -@Serializable(with = BazSerializer::class) +@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: Bar) @Serializable -class Foo2(val li: List<@Serializable(with = BazSerializer::class) Bar>) +class Foo2(val li: 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: Bar?) @Serializable -class Foo6(@Serializable(with = NullableBazSerializer::class) val i: Bar) +class Foo6(@Serializable(with = NullableBazSerializer::class) val i: Bar) @Serializable -class Foo7(@Serializable(with = NullableBazSerializer::class) val i: Bar?) +class Foo7(@Serializable(with = NullableBazSerializer::class) val i: Bar?) diff --git a/plugins/kotlinx-serialization/testData/diagnostics/Transients.fir.kt b/plugins/kotlinx-serialization/testData/diagnostics/Transients.fir.kt deleted file mode 100644 index bcf1b38b130..00000000000 --- a/plugins/kotlinx-serialization/testData/diagnostics/Transients.fir.kt +++ /dev/null @@ -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 -} diff --git a/plugins/kotlinx-serialization/testData/diagnostics/Transients.kt b/plugins/kotlinx-serialization/testData/diagnostics/Transients.kt index 6336b5b95b4..ea7e8003a7b 100644 --- a/plugins/kotlinx-serialization/testData/diagnostics/Transients.kt +++ b/plugins/kotlinx-serialization/testData/diagnostics/Transients.kt @@ -1,3 +1,4 @@ +// FIR_IDENTICAL // WITH_STDLIB // FILE: test.kt import kotlinx.serialization.* diff --git a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt index aebe2230af4..10d07558d1f 100644 --- a/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt +++ b/plugins/kotlinx-serialization/tests/org/jetbrains/kotlinx/serialization/RuntimeSearch.kt @@ -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") }