Parcelize: Support old (kotlinx.android.parcel) annotations in Parcelize plugin
This commit is contained in:
+19
-10
@@ -18,9 +18,6 @@ package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.TypeParceler
|
||||
import kotlinx.parcelize.WriteWith
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
@@ -40,10 +37,22 @@ import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
class ParcelizeAnnotationChecker : CallChecker {
|
||||
@Suppress("DEPRECATION")
|
||||
companion object {
|
||||
val TYPE_PARCELER_FQNAME = FqName(TypeParceler::class.java.name)
|
||||
val WRITE_WITH_FQNAME = FqName(WriteWith::class.java.name)
|
||||
val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.name)
|
||||
val TYPE_PARCELER_FQ_NAMES = listOf(
|
||||
FqName(kotlinx.parcelize.TypeParceler::class.java.name),
|
||||
FqName(kotlinx.android.parcel.TypeParceler::class.java.name)
|
||||
)
|
||||
|
||||
val WRITE_WITH_FQ_NAMES = listOf(
|
||||
FqName(kotlinx.parcelize.WriteWith::class.java.name),
|
||||
FqName(kotlinx.android.parcel.WriteWith::class.java.name)
|
||||
)
|
||||
|
||||
val IGNORED_ON_PARCEL_FQ_NAMES = listOf(
|
||||
FqName(kotlinx.parcelize.IgnoredOnParcel::class.java.name),
|
||||
FqName(kotlinx.android.parcel.IgnoredOnParcel::class.java.name)
|
||||
)
|
||||
}
|
||||
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
@@ -53,15 +62,15 @@ class ParcelizeAnnotationChecker : CallChecker {
|
||||
val annotationEntry = resolvedCall.call.callElement.getNonStrictParentOfType<KtAnnotationEntry>() ?: return
|
||||
val annotationOwner = annotationEntry.getStrictParentOfType<KtModifierListOwner>() ?: return
|
||||
|
||||
if (annotationClass.fqNameSafe == TYPE_PARCELER_FQNAME) {
|
||||
if (annotationClass.fqNameSafe in TYPE_PARCELER_FQ_NAMES) {
|
||||
checkTypeParcelerUsage(resolvedCall, annotationEntry, context, annotationOwner)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe == WRITE_WITH_FQNAME) {
|
||||
if (annotationClass.fqNameSafe in WRITE_WITH_FQ_NAMES) {
|
||||
checkWriteWithUsage(resolvedCall, annotationEntry, context, annotationOwner)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe == IGNORED_ON_PARCEL_FQNAME) {
|
||||
if (annotationClass.fqNameSafe in IGNORED_ON_PARCEL_FQ_NAMES) {
|
||||
checkIgnoredOnParcelUsage(annotationEntry, context, annotationOwner)
|
||||
}
|
||||
}
|
||||
@@ -84,7 +93,7 @@ class ParcelizeAnnotationChecker : CallChecker {
|
||||
val thisMappedType = resolvedCall.typeArguments.values.takeIf { it.size == 2 }?.first() ?: return
|
||||
|
||||
val duplicatingAnnotationCount = descriptor.annotations
|
||||
.filter { it.fqName == TYPE_PARCELER_FQNAME }
|
||||
.filter { it.fqName in TYPE_PARCELER_FQ_NAMES }
|
||||
.mapNotNull { it.type.arguments.takeIf { args -> args.size == 2 }?.first()?.type }
|
||||
.count { it == thisMappedType }
|
||||
|
||||
|
||||
+2
-4
@@ -6,7 +6,6 @@
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import kotlinx.parcelize.TypeParceler
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy.CodegenBased
|
||||
@@ -17,8 +16,8 @@ import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeAnnotationChecker.Companion.TYPE_PARCELER_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeResolveExtension.Companion.createMethod
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.*
|
||||
@@ -427,10 +426,9 @@ open class ParcelizeCodegenExtension : ParcelizeExtensionBase, ExpressionCodegen
|
||||
}
|
||||
|
||||
internal fun getTypeParcelers(annotations: Annotations): List<TypeParcelerMapping> {
|
||||
val typeParcelerFqName = FqName(TypeParceler::class.java.name)
|
||||
val serializers = mutableListOf<TypeParcelerMapping>()
|
||||
|
||||
for (annotation in annotations.filter { it.fqName == typeParcelerFqName }) {
|
||||
for (annotation in annotations.filter { it.fqName in TYPE_PARCELER_FQ_NAMES }) {
|
||||
val (mappedType, parcelerType) = annotation.type.arguments.takeIf { it.size == 2 } ?: continue
|
||||
serializers += TypeParcelerMapping(mappedType.type, parcelerType.type)
|
||||
}
|
||||
|
||||
+5
-3
@@ -5,7 +5,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
@@ -37,7 +36,10 @@ val ANDROID_PARCEL_CLASS_FQNAME = FqName("android.os.Parcel")
|
||||
|
||||
class ParcelizeDeclarationChecker : DeclarationChecker {
|
||||
private companion object {
|
||||
private val IGNORED_ON_PARCEL_FQNAME = FqName(IgnoredOnParcel::class.java.canonicalName)
|
||||
private val IGNORED_ON_PARCEL_FQ_NAMES = listOf(
|
||||
FqName(kotlinx.parcelize.IgnoredOnParcel::class.java.canonicalName),
|
||||
FqName(kotlinx.android.parcel.IgnoredOnParcel::class.java.canonicalName)
|
||||
)
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
@@ -91,7 +93,7 @@ class ParcelizeDeclarationChecker : DeclarationChecker {
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
fun hasIgnoredOnParcel(): Boolean {
|
||||
fun Annotations.hasIgnoredOnParcel() = any { it.fqName == IGNORED_ON_PARCEL_FQNAME }
|
||||
fun Annotations.hasIgnoredOnParcel() = any { it.fqName in IGNORED_ON_PARCEL_FQ_NAMES }
|
||||
|
||||
return property.annotations.hasIgnoredOnParcel() || (property.getter?.annotations?.hasIgnoredOnParcel() ?: false)
|
||||
}
|
||||
|
||||
+40
-7
@@ -17,10 +17,10 @@
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import kotlinx.parcelize.Parceler
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotated
|
||||
import org.jetbrains.kotlin.descriptors.annotations.AnnotationDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.impl.SimpleFunctionDescriptorImpl
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
@@ -169,11 +169,44 @@ interface ParcelizeSyntheticComponent {
|
||||
}
|
||||
}
|
||||
|
||||
val PARCELIZE_CLASS_FQNAME: FqName = FqName(Parcelize::class.java.canonicalName)
|
||||
internal val PARCELER_FQNAME: FqName = FqName(Parceler::class.java.canonicalName)
|
||||
val PARCELIZE_CLASS_FQ_NAMES: List<FqName> = listOf(
|
||||
FqName(kotlinx.parcelize.Parcelize::class.java.canonicalName),
|
||||
FqName(kotlinx.android.parcel.Parcelize::class.java.canonicalName)
|
||||
)
|
||||
|
||||
internal val PARCELER_FQNAME = FqName(kotlinx.parcelize.Parceler::class.java.canonicalName)
|
||||
|
||||
val ClassDescriptor.isParcelize: Boolean
|
||||
get() = this.annotations.hasAnnotation(PARCELIZE_CLASS_FQNAME)
|
||||
get() {
|
||||
for (fqName in PARCELIZE_CLASS_FQ_NAMES) {
|
||||
if (this.annotations.hasAnnotation(fqName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
val KotlinType.isParceler
|
||||
get() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQNAME
|
||||
return false
|
||||
}
|
||||
|
||||
val KotlinType.isParceler: Boolean
|
||||
get() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQNAME
|
||||
|
||||
fun Annotated.findAnyAnnotation(fqNames: List<FqName>): AnnotationDescriptor? {
|
||||
for (fqName in fqNames) {
|
||||
val annotation = annotations.findAnnotation(fqName)
|
||||
if (annotation != null) {
|
||||
return annotation
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun Annotated.hasAnyAnnotation(fqNames: List<FqName>): Boolean {
|
||||
for (fqName in fqNames) {
|
||||
if (annotations.hasAnnotation(fqName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
+2
-2
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.RAW_VALUE_ANNOTATION_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.serializers.RAW_VALUE_ANNOTATION_FQ_NAMES
|
||||
|
||||
class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
||||
/**
|
||||
@@ -28,7 +28,7 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
||||
strict: Boolean = false,
|
||||
toplevel: Boolean = false
|
||||
): IrParcelSerializer {
|
||||
fun strict() = strict && !irType.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME)
|
||||
fun strict() = strict && !irType.hasAnyAnnotation(RAW_VALUE_ANNOTATION_FQ_NAMES)
|
||||
|
||||
scope.getCustomSerializer(irType)?.let { parceler ->
|
||||
return IrCustomParcelSerializer(parceler)
|
||||
|
||||
+2
-3
@@ -13,7 +13,6 @@ import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.typeOrNull
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.getAnnotation
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeAnnotationChecker
|
||||
|
||||
// Keep track of all custom parcelers which are currently in scope.
|
||||
@@ -30,7 +29,7 @@ class IrParcelerScope(private val parent: IrParcelerScope? = null) {
|
||||
}
|
||||
|
||||
fun IrParcelerScope?.getCustomSerializer(irType: IrType): IrClass? {
|
||||
return irType.getAnnotation(ParcelizeAnnotationChecker.WRITE_WITH_FQNAME)?.let { writeWith ->
|
||||
return irType.getAnyAnnotation(ParcelizeAnnotationChecker.WRITE_WITH_FQ_NAMES)?.let { writeWith ->
|
||||
(writeWith.type as IrSimpleType).arguments.single().typeOrNull!!.getClass()!!
|
||||
} ?: this?.get(irType)
|
||||
}
|
||||
@@ -41,7 +40,7 @@ fun IrParcelerScope?.hasCustomSerializer(irType: IrType): Boolean {
|
||||
|
||||
fun IrAnnotationContainer.getParcelerScope(parent: IrParcelerScope? = null): IrParcelerScope? {
|
||||
val typeParcelerAnnotations = annotations.filterTo(mutableListOf()) {
|
||||
it.symbol.owner.constructedClass.fqNameWhenAvailable == ParcelizeAnnotationChecker.TYPE_PARCELER_FQNAME
|
||||
it.symbol.owner.constructedClass.fqNameWhenAvailable in ParcelizeAnnotationChecker.TYPE_PARCELER_FQ_NAMES
|
||||
}
|
||||
|
||||
if (typeParcelerAnnotations.isEmpty())
|
||||
|
||||
+25
-6
@@ -8,12 +8,10 @@ package org.jetbrains.kotlin.parcelize.ir
|
||||
import org.jetbrains.kotlin.backend.common.ir.allOverridden
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
@@ -23,14 +21,14 @@ import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.PARCELER_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.PARCELIZE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.parcelize.PARCELIZE_CLASS_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
// true if the class should be processed by the parcelize plugin
|
||||
val IrClass.isParcelize: Boolean
|
||||
get() = kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS && hasAnnotation(PARCELIZE_CLASS_FQNAME)
|
||||
get() = kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS && hasAnyAnnotation(PARCELIZE_CLASS_FQ_NAMES)
|
||||
|
||||
// Finds the getter for a pre-existing CREATOR field on the class companion, which is used for manual Parcelable implementations in Kotlin.
|
||||
val IrClass.creatorGetter: IrSimpleFunctionSymbol?
|
||||
@@ -175,3 +173,24 @@ fun IrClass.getMethodWithoutArguments(name: String): IrSimpleFunction =
|
||||
function.name.asString() == name && function.dispatchReceiverParameter != null
|
||||
&& function.extensionReceiverParameter == null && function.valueParameters.isEmpty()
|
||||
}
|
||||
|
||||
internal fun IrAnnotationContainer.hasAnyAnnotation(fqNames: List<FqName>): Boolean {
|
||||
for (fqName in fqNames) {
|
||||
if (hasAnnotation(fqName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
internal fun IrAnnotationContainer.getAnyAnnotation(fqNames: List<FqName>): IrConstructorCall? {
|
||||
for (fqName in fqNames) {
|
||||
val annotation = getAnnotation(fqName)
|
||||
if (annotation != null) {
|
||||
return annotation
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
+13
-6
@@ -16,7 +16,6 @@
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.serializers
|
||||
|
||||
import kotlinx.parcelize.WriteWith
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -27,6 +26,8 @@ import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.findAnyAnnotation
|
||||
import org.jetbrains.kotlin.parcelize.hasAnyAnnotation
|
||||
import org.jetbrains.kotlin.parcelize.isParcelize
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
@@ -42,7 +43,10 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
val RAW_VALUE_ANNOTATION_FQNAME = FqName("kotlinx.parcelize.RawValue")
|
||||
val RAW_VALUE_ANNOTATION_FQ_NAMES = listOf(
|
||||
FqName(kotlinx.parcelize.RawValue::class.java.name),
|
||||
FqName(kotlinx.android.parcel.RawValue::class.java.name)
|
||||
)
|
||||
|
||||
internal typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
|
||||
|
||||
@@ -64,7 +68,10 @@ interface ParcelSerializer {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val WRITE_WITH_FQNAME = FqName(WriteWith::class.java.name)
|
||||
private val WRITE_WITH_FQ_NAMES = listOf(
|
||||
FqName(kotlinx.parcelize.WriteWith::class.java.name),
|
||||
FqName(kotlinx.android.parcel.WriteWith::class.java.name),
|
||||
)
|
||||
|
||||
private fun KotlinTypeMapper.mapTypeSafe(type: KotlinType, forceBoxed: Boolean) = when {
|
||||
type.isError -> Type.getObjectType("java/lang/Object")
|
||||
@@ -81,10 +88,10 @@ interface ParcelSerializer {
|
||||
val typeMapper = context.typeMapper
|
||||
|
||||
val className = asmType.className
|
||||
fun strict() = strict && !type.annotations.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME)
|
||||
fun strict() = strict && !type.hasAnyAnnotation(RAW_VALUE_ANNOTATION_FQ_NAMES)
|
||||
|
||||
fun findCustomParcelerType(type: KotlinType): KotlinType? {
|
||||
type.annotations.findAnnotation(WRITE_WITH_FQNAME)?.let { writeWith ->
|
||||
type.findAnyAnnotation(WRITE_WITH_FQ_NAMES)?.let { writeWith ->
|
||||
val parceler = writeWith.type.arguments.singleOrNull()?.type
|
||||
if (parceler != null && !parceler.isError) {
|
||||
return parceler
|
||||
@@ -371,7 +378,7 @@ interface ParcelSerializer {
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (strict && !type.annotations.hasAnnotation(RAW_VALUE_ANNOTATION_FQNAME))
|
||||
if (strict && !type.hasAnyAnnotation(RAW_VALUE_ANNOTATION_FQ_NAMES))
|
||||
throw IllegalArgumentException("Illegal type")
|
||||
else
|
||||
GenericParcelSerializer(asmType)
|
||||
|
||||
Reference in New Issue
Block a user