[Parcelize] Reorganize module structure of Parcelize plugin
Also mark parcelize as compatible with K2
This commit is contained in:
committed by
teamcity
parent
2a7dc1cc0c
commit
259862d294
@@ -0,0 +1,25 @@
|
||||
description = "Parcelize compiler plugin (Backend)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":plugins:parcelize:parcelize-compiler:parcelize.common"))
|
||||
|
||||
compileOnly(project(":compiler:util"))
|
||||
compileOnly(project(":compiler:frontend"))
|
||||
compileOnly(project(":compiler:frontend.java"))
|
||||
compileOnly(project(":compiler:backend")) // for KotlinTypeMapper
|
||||
compileOnly(intellijCore())
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
javadocJar()
|
||||
sourcesJar()
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.PsiTreeUtil
|
||||
import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.descriptors.containingPackage
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.DEPRECATED_RUNTIME_PACKAGE
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.IGNORED_ON_PARCEL_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.RAW_VALUE_ANNOTATION_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.TYPE_PARCELER_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_WITH_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.diagnostic.ErrorsParcelize
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallChecker
|
||||
import org.jetbrains.kotlin.resolve.calls.checkers.CallCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.typeUtil.isSubtypeOf
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceAnnotations
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
open class ParcelizeAnnotationChecker : CallChecker {
|
||||
override fun check(resolvedCall: ResolvedCall<*>, reportOn: PsiElement, context: CallCheckerContext) {
|
||||
val constructorDescriptor = resolvedCall.resultingDescriptor as? ClassConstructorDescriptor ?: return
|
||||
val annotationClass = constructorDescriptor.constructedClass.takeIf { it.kind == ClassKind.ANNOTATION_CLASS } ?: return
|
||||
|
||||
val annotationEntry = resolvedCall.call.callElement.getNonStrictParentOfType<KtAnnotationEntry>() ?: return
|
||||
val annotationOwner = annotationEntry.getStrictParentOfType<KtModifierListOwner>() ?: return
|
||||
|
||||
if (annotationClass.fqNameSafe in TYPE_PARCELER_FQ_NAMES) {
|
||||
checkTypeParcelerUsage(resolvedCall, annotationEntry, context, annotationOwner)
|
||||
checkDeprecatedAnnotations(resolvedCall, annotationEntry, context, isForbidden = true)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe in WRITE_WITH_FQ_NAMES) {
|
||||
checkWriteWithUsage(resolvedCall, annotationEntry, context, annotationOwner)
|
||||
checkDeprecatedAnnotations(resolvedCall, annotationEntry, context, isForbidden = true)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe in IGNORED_ON_PARCEL_FQ_NAMES) {
|
||||
checkIgnoredOnParcelUsage(annotationEntry, context, annotationOwner)
|
||||
checkDeprecatedAnnotations(resolvedCall, annotationEntry, context, isForbidden = false)
|
||||
}
|
||||
|
||||
if (annotationClass.fqNameSafe in PARCELIZE_CLASS_FQ_NAMES || annotationClass.fqNameSafe in RAW_VALUE_ANNOTATION_FQ_NAMES) {
|
||||
checkDeprecatedAnnotations(resolvedCall, annotationEntry, context, isForbidden = false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkDeprecatedAnnotations(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext,
|
||||
isForbidden: Boolean
|
||||
) {
|
||||
val descriptorPackage = resolvedCall.resultingDescriptor.containingPackage()
|
||||
if (descriptorPackage == DEPRECATED_RUNTIME_PACKAGE) {
|
||||
val factory = if (isForbidden) ErrorsParcelize.FORBIDDEN_DEPRECATED_ANNOTATION else ErrorsParcelize.DEPRECATED_ANNOTATION
|
||||
context.trace.report(factory.on(annotationEntry))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIgnoredOnParcelUsage(annotationEntry: KtAnnotationEntry, context: CallCheckerContext, element: KtModifierListOwner) {
|
||||
if (element is KtParameter && PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) is KtPrimaryConstructor) {
|
||||
if (!element.hasDefaultValue()) {
|
||||
context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY.on(annotationEntry))
|
||||
}
|
||||
} else if (element !is KtProperty || PsiTreeUtil.getParentOfType(element, KtDeclaration::class.java) !is KtClassOrObject) {
|
||||
context.trace.report(ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL.on(annotationEntry))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkTypeParcelerUsage(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext,
|
||||
element: KtModifierListOwner
|
||||
) {
|
||||
val descriptor = context.trace[BindingContext.DECLARATION_TO_DESCRIPTOR, element] ?: return
|
||||
val thisMappedType = resolvedCall.typeArguments.values.takeIf { it.size == 2 }?.first() ?: return
|
||||
|
||||
val duplicatingAnnotationCount = descriptor.annotations
|
||||
.filter { it.fqName in TYPE_PARCELER_FQ_NAMES }
|
||||
.mapNotNull { it.type.arguments.takeIf { args -> args.size == 2 }?.first()?.type }
|
||||
.count { it == thisMappedType }
|
||||
|
||||
if (duplicatingAnnotationCount > 1) {
|
||||
val reportElement = annotationEntry.typeArguments.firstOrNull() ?: annotationEntry
|
||||
context.trace.report(ErrorsParcelize.DUPLICATING_TYPE_PARCELERS.on(reportElement))
|
||||
return
|
||||
}
|
||||
|
||||
val containingClass = when (element) {
|
||||
is KtClassOrObject -> element
|
||||
is KtParameter -> element.containingClassOrObject
|
||||
else -> null
|
||||
}
|
||||
|
||||
checkIfTheContainingClassIsParcelize(containingClass, annotationEntry, context)
|
||||
|
||||
if (element is KtParameter && element.getStrictParentOfType<KtDeclaration>() is KtPrimaryConstructor) {
|
||||
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
|
||||
val thisAnnotationDescriptor = context.trace[BindingContext.ANNOTATION, annotationEntry]
|
||||
|
||||
if (containingClass != null && containingClassDescriptor != null && thisAnnotationDescriptor != null) {
|
||||
// We can ignore value arguments here cause @TypeParceler is a zero-parameter annotation
|
||||
if (containingClassDescriptor.annotations.any { it.type == thisAnnotationDescriptor.type }) {
|
||||
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
|
||||
context.trace.report(ErrorsParcelize.REDUNDANT_TYPE_PARCELER.on(reportElement, containingClass))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkWriteWithUsage(
|
||||
resolvedCall: ResolvedCall<*>,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext,
|
||||
element: KtModifierListOwner
|
||||
) {
|
||||
if (element !is KtTypeReference) {
|
||||
return
|
||||
}
|
||||
|
||||
val actualType = context.trace[BindingContext.TYPE, element]?.replaceAnnotations(Annotations.EMPTY) ?: return
|
||||
|
||||
val parcelerType = resolvedCall.typeArguments.values.singleOrNull() ?: return
|
||||
val parcelerClass = parcelerType.constructor.declarationDescriptor as? ClassDescriptor ?: return
|
||||
|
||||
val containingClass = element.getStrictParentOfType<KtClassOrObject>()
|
||||
checkIfTheContainingClassIsParcelize(containingClass, annotationEntry, context)
|
||||
|
||||
fun reportElement() = annotationEntry.typeArguments.singleOrNull() ?: annotationEntry
|
||||
|
||||
if (parcelerClass.kind != ClassKind.OBJECT) {
|
||||
context.trace.report(ErrorsParcelize.PARCELER_SHOULD_BE_OBJECT.on(reportElement()))
|
||||
return
|
||||
}
|
||||
|
||||
fun KotlinType.fqName() = constructor.declarationDescriptor?.fqNameSafe
|
||||
val parcelerSuperType = parcelerClass.defaultType.supertypes().firstOrNull { it.fqName() == PARCELER_FQN } ?: return
|
||||
val expectedType = parcelerSuperType.arguments.singleOrNull()?.type ?: return
|
||||
|
||||
if (!actualType.isSubtypeOf(expectedType)) {
|
||||
context.trace.report(ErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE.on(reportElement(), expectedType, actualType))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkIfTheContainingClassIsParcelize(
|
||||
containingClass: KtClassOrObject?,
|
||||
annotationEntry: KtAnnotationEntry,
|
||||
context: CallCheckerContext
|
||||
) {
|
||||
if (containingClass != null) {
|
||||
val containingClassDescriptor = context.trace[BindingContext.CLASS, containingClass]
|
||||
if (containingClassDescriptor != null && !containingClassDescriptor.isParcelize) {
|
||||
val reportElement = (annotationEntry.typeReference?.typeElement as? KtUserType)?.referenceExpression ?: annotationEntry
|
||||
context.trace.report(ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE.on(reportElement, containingClass))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal inline fun <reified T : PsiElement> PsiElement.getStrictParentOfType(): T? {
|
||||
return PsiTreeUtil.getParentOfType(this, T::class.java, true)
|
||||
}
|
||||
|
||||
internal inline fun <reified T : PsiElement> PsiElement.getNonStrictParentOfType(): T? {
|
||||
return PsiTreeUtil.getParentOfType(this, T::class.java, false)
|
||||
}
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
/*
|
||||
* Copyright 2010-2018 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.kotlin.parcelize
|
||||
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderMode
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.config.LanguageVersionSettings
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.annotations.Annotations
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.load.kotlin.TypeMappingMode
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.OLD_PARCELER_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELABLE_FQN
|
||||
import org.jetbrains.kotlin.parcelize.diagnostic.ErrorsParcelize
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelSerializer
|
||||
import org.jetbrains.kotlin.parcelize.serializers.isParcelable
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationChecker
|
||||
import org.jetbrains.kotlin.resolve.checkers.DeclarationCheckerContext
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.jvm.annotations.findJvmFieldAnnotation
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.types.typeUtil.supertypes
|
||||
|
||||
open class ParcelizeDeclarationChecker : DeclarationChecker {
|
||||
private companion object {
|
||||
private val IGNORED_ON_PARCEL_FQ_NAMES = listOf(
|
||||
FqName("kotlinx.parcelize.IgnoredOnParcel"),
|
||||
FqName("kotlinx.android.parcel.IgnoredOnParcel")
|
||||
)
|
||||
}
|
||||
|
||||
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
|
||||
val trace = context.trace
|
||||
|
||||
when (descriptor) {
|
||||
is ClassDescriptor -> {
|
||||
checkParcelableClass(descriptor, declaration, trace, trace.bindingContext, context.languageVersionSettings)
|
||||
checkParcelerClass(descriptor, declaration, trace)
|
||||
}
|
||||
is SimpleFunctionDescriptor -> {
|
||||
val containingClass = descriptor.containingDeclaration as? ClassDescriptor
|
||||
val ktFunction = declaration as? KtFunction
|
||||
if (containingClass != null && ktFunction != null) {
|
||||
checkParcelableClassMethod(descriptor, containingClass, ktFunction, trace)
|
||||
}
|
||||
}
|
||||
is PropertyDescriptor -> {
|
||||
val containingClass = descriptor.containingDeclaration as? ClassDescriptor
|
||||
val ktProperty = declaration as? KtProperty
|
||||
if (containingClass != null && ktProperty != null) {
|
||||
checkParcelableClassProperty(descriptor, containingClass, ktProperty, trace, trace.bindingContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClassMethod(
|
||||
method: SimpleFunctionDescriptor,
|
||||
containingClass: ClassDescriptor,
|
||||
declaration: KtFunction,
|
||||
diagnosticHolder: DiagnosticSink
|
||||
) {
|
||||
if (!containingClass.isParcelize) {
|
||||
return
|
||||
}
|
||||
|
||||
if (method.isWriteToParcel() && declaration.hasModifier(KtTokens.OVERRIDE_KEYWORD)) {
|
||||
val reportElement = declaration.modifierList?.getModifier(KtTokens.OVERRIDE_KEYWORD)
|
||||
?: declaration.nameIdentifier
|
||||
?: declaration
|
||||
|
||||
diagnosticHolder.report(ErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClassProperty(
|
||||
property: PropertyDescriptor,
|
||||
containingClass: ClassDescriptor,
|
||||
declaration: KtProperty,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext
|
||||
) {
|
||||
fun hasIgnoredOnParcel(): Boolean {
|
||||
fun Annotations.hasIgnoredOnParcel() = any { it.fqName in IGNORED_ON_PARCEL_FQ_NAMES }
|
||||
|
||||
return property.annotations.hasIgnoredOnParcel() || (property.getter?.annotations?.hasIgnoredOnParcel() ?: false)
|
||||
}
|
||||
|
||||
if (containingClass.isParcelize
|
||||
&& (declaration.hasDelegate() || bindingContext[BindingContext.BACKING_FIELD_REQUIRED, property] == true)
|
||||
&& !hasIgnoredOnParcel()
|
||||
&& !containingClass.hasCustomParceler()
|
||||
) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED.on(reportElement))
|
||||
}
|
||||
|
||||
// @JvmName is not applicable to property so we can check just the descriptor name
|
||||
if (property.name.asString() == "CREATOR" && property.findJvmFieldAnnotation() != null && containingClass.isCompanionObject) {
|
||||
val outerClass = containingClass.containingDeclaration as? ClassDescriptor
|
||||
if (outerClass != null && outerClass.isParcelize) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelerClass(
|
||||
descriptor: ClassDescriptor,
|
||||
declaration: KtDeclaration,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
) {
|
||||
if (!descriptor.isCompanionObject || declaration !is KtObjectDeclaration) {
|
||||
return
|
||||
}
|
||||
|
||||
for (type in descriptor.defaultType.supertypes()) {
|
||||
if (type.constructor.declarationDescriptor?.fqNameSafe == OLD_PARCELER_FQN) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration.getObjectKeyword() ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.DEPRECATED_PARCELER.on(reportElement))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClass(
|
||||
descriptor: ClassDescriptor,
|
||||
declaration: KtDeclaration,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
bindingContext: BindingContext,
|
||||
languageVersionSettings: LanguageVersionSettings
|
||||
) {
|
||||
if (!descriptor.isParcelize) {
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration !is KtClassOrObject) {
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS.on(declaration))
|
||||
return
|
||||
}
|
||||
|
||||
if (declaration is KtClass && (declaration.isAnnotation() || declaration.isInterface() && !declaration.isSealed())) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS.on(reportElement))
|
||||
return
|
||||
}
|
||||
|
||||
for (companion in declaration.companionObjects) {
|
||||
if (companion.name == "CREATOR") {
|
||||
val reportElement = companion.nameIdentifier ?: companion
|
||||
diagnosticHolder.report(ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
|
||||
val abstractModifier = declaration.modifierList?.let { it.getModifier(KtTokens.ABSTRACT_KEYWORD) }
|
||||
if (abstractModifier != null) {
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE.on(abstractModifier))
|
||||
}
|
||||
|
||||
if (declaration is KtClass && declaration.isInner()) {
|
||||
val reportElement = declaration.modifierList?.getModifier(KtTokens.INNER_KEYWORD) ?: declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS.on(reportElement))
|
||||
}
|
||||
|
||||
if (declaration.isLocal) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS.on(reportElement))
|
||||
}
|
||||
|
||||
val superTypes = TypeUtils.getAllSupertypes(descriptor.defaultType)
|
||||
if (superTypes.none { it.constructor.declarationDescriptor?.fqNameSafe == PARCELABLE_FQN }) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.NO_PARCELABLE_SUPERTYPE.on(reportElement))
|
||||
}
|
||||
|
||||
for (supertypeEntry in declaration.superTypeListEntries) {
|
||||
supertypeEntry as? KtDelegatedSuperTypeEntry ?: continue
|
||||
val delegateExpression = supertypeEntry.delegateExpression ?: continue
|
||||
val type = bindingContext[BindingContext.TYPE, supertypeEntry.typeReference] ?: continue
|
||||
if (type.isParcelable()) {
|
||||
val reportElement = supertypeEntry.byKeywordNode?.psi ?: delegateExpression
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED.on(reportElement))
|
||||
}
|
||||
}
|
||||
|
||||
// The constructor checks are irrelevant for custom parcelers
|
||||
if (descriptor.hasCustomParceler()) {
|
||||
return
|
||||
}
|
||||
|
||||
val primaryConstructor = declaration.primaryConstructor
|
||||
if (primaryConstructor == null && declaration.secondaryConstructors.isNotEmpty()) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR.on(reportElement))
|
||||
} else if (primaryConstructor != null && primaryConstructor.valueParameters.isEmpty()) {
|
||||
val reportElement = declaration.nameIdentifier ?: declaration
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY.on(reportElement))
|
||||
}
|
||||
|
||||
val typeMapper = KotlinTypeMapper(
|
||||
bindingContext,
|
||||
ClassBuilderMode.FULL,
|
||||
descriptor.module.name.asString(),
|
||||
languageVersionSettings,
|
||||
useOldInlineClassesManglingScheme = false
|
||||
)
|
||||
|
||||
for (parameter in primaryConstructor?.valueParameters.orEmpty<KtParameter>()) {
|
||||
checkParcelableClassProperty(parameter, descriptor, diagnosticHolder, typeMapper)
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkParcelableClassProperty(
|
||||
parameter: KtParameter,
|
||||
containerClass: ClassDescriptor,
|
||||
diagnosticHolder: DiagnosticSink,
|
||||
typeMapper: KotlinTypeMapper
|
||||
) {
|
||||
if (!parameter.hasValOrVar()) {
|
||||
val reportElement = parameter.nameIdentifier ?: parameter
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR.on(reportElement))
|
||||
}
|
||||
|
||||
val descriptor = typeMapper.bindingContext[BindingContext.PRIMARY_CONSTRUCTOR_PARAMETER, parameter] ?: return
|
||||
|
||||
// Don't check parameters which won't be serialized
|
||||
if (descriptor.annotations.any { it.fqName in IGNORED_ON_PARCEL_FQ_NAMES }) {
|
||||
return
|
||||
}
|
||||
|
||||
val type = descriptor.type
|
||||
|
||||
if (!type.isError) {
|
||||
val asmType = typeMapper.mapType(type, mode = TypeMappingMode.CLASS_DECLARATION)
|
||||
|
||||
try {
|
||||
val parcelers = getTypeParcelers(descriptor.annotations) + getTypeParcelers(containerClass.annotations)
|
||||
val context = ParcelSerializer.ParcelSerializerContext(
|
||||
typeMapper,
|
||||
typeMapper.mapClass(containerClass),
|
||||
parcelers,
|
||||
FrameMap()
|
||||
)
|
||||
|
||||
ParcelSerializer.get(type, asmType, context, strict = true)
|
||||
} catch (e: IllegalArgumentException) {
|
||||
// get() throws IllegalArgumentException on unknown types
|
||||
val reportElement = parameter.typeReference ?: parameter.nameIdentifier ?: parameter
|
||||
diagnosticHolder.report(ErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED.on(reportElement))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasCustomParceler(): Boolean {
|
||||
val companionObjectSuperTypes = companionObjectDescriptor?.let { TypeUtils.getAllSupertypes(it.defaultType) } ?: return false
|
||||
return companionObjectSuperTypes.any { it.isParceler }
|
||||
}
|
||||
}
|
||||
+223
@@ -0,0 +1,223 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
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
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_ID
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.DESCRIBE_CONTENTS_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCEL_ID
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
|
||||
import org.jetbrains.kotlin.parcelize.serializers.TypeParcelerMapping
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.*
|
||||
import org.jetbrains.kotlin.resolve.extensions.SyntheticResolveExtension
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.types.error.ErrorTypeKind
|
||||
import org.jetbrains.kotlin.types.error.ErrorUtils
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.SimpleType
|
||||
|
||||
open class ParcelizeResolveExtension : SyntheticResolveExtension {
|
||||
companion object {
|
||||
fun resolveParcelClassType(module: ModuleDescriptor): SimpleType? {
|
||||
return module.findClassAcrossModuleDependencies(PARCEL_ID)?.defaultType
|
||||
}
|
||||
|
||||
fun resolveParcelableCreatorClassType(module: ModuleDescriptor): SimpleType? {
|
||||
val creatorClassId = CREATOR_ID
|
||||
return module.findClassAcrossModuleDependencies(creatorClassId)?.defaultType
|
||||
}
|
||||
|
||||
fun createMethod(
|
||||
classDescriptor: ClassDescriptor,
|
||||
componentKind: ParcelizeSyntheticComponent.ComponentKind,
|
||||
modality: Modality,
|
||||
returnType: KotlinType,
|
||||
vararg parameters: Pair<String, KotlinType>
|
||||
): SimpleFunctionDescriptor {
|
||||
val functionDescriptor = object : ParcelizeSyntheticComponent, SimpleFunctionDescriptorImpl(
|
||||
classDescriptor,
|
||||
null,
|
||||
Annotations.EMPTY,
|
||||
Name.identifier(componentKind.methodName),
|
||||
CallableMemberDescriptor.Kind.SYNTHESIZED,
|
||||
classDescriptor.source
|
||||
) {
|
||||
override val componentKind = componentKind
|
||||
}
|
||||
|
||||
val valueParameters = parameters.mapIndexed { index, (name, type) -> functionDescriptor.makeValueParameter(name, type, index) }
|
||||
|
||||
functionDescriptor.initialize(
|
||||
null, classDescriptor.thisAsReceiverParameter, emptyList(), emptyList(), valueParameters,
|
||||
returnType, modality, DescriptorVisibilities.PUBLIC
|
||||
)
|
||||
|
||||
return functionDescriptor
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.makeValueParameter(name: String, type: KotlinType, index: Int): ValueParameterDescriptor {
|
||||
return ValueParameterDescriptorImpl(
|
||||
containingDeclaration = this,
|
||||
original = null,
|
||||
index = index,
|
||||
annotations = Annotations.EMPTY,
|
||||
name = Name.identifier(name),
|
||||
outType = type,
|
||||
declaresDefaultValue = false,
|
||||
isCrossinline = false,
|
||||
isNoinline = false,
|
||||
varargElementType = null,
|
||||
source = this.source
|
||||
)
|
||||
}
|
||||
|
||||
private val parcelizeMethodNames: List<Name> =
|
||||
listOf(Name.identifier(DESCRIBE_CONTENTS.methodName), Name.identifier(WRITE_TO_PARCEL.methodName))
|
||||
}
|
||||
|
||||
open fun isAvailable(element: PsiElement): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getSyntheticCompanionObjectNameIfNeeded(thisDescriptor: ClassDescriptor): Name? = null
|
||||
|
||||
override fun getSyntheticFunctionNames(thisDescriptor: ClassDescriptor): List<Name> {
|
||||
return if (thisDescriptor.isParcelize) parcelizeMethodNames else emptyList()
|
||||
}
|
||||
|
||||
override fun generateSyntheticMethods(
|
||||
thisDescriptor: ClassDescriptor,
|
||||
name: Name,
|
||||
bindingContext: BindingContext,
|
||||
fromSupertypes: List<SimpleFunctionDescriptor>,
|
||||
result: MutableCollection<SimpleFunctionDescriptor>
|
||||
) {
|
||||
fun isParcelizePluginEnabled(): Boolean {
|
||||
val sourceElement = (thisDescriptor.source as? PsiSourceElement)?.psi ?: return false
|
||||
return isAvailable(sourceElement)
|
||||
}
|
||||
|
||||
if (name.asString() == DESCRIBE_CONTENTS.methodName
|
||||
&& thisDescriptor.isParcelize
|
||||
&& !thisDescriptor.isSealed()
|
||||
&& isParcelizePluginEnabled()
|
||||
&& result.none { it.isDescribeContents() }
|
||||
&& fromSupertypes.none { it.isDescribeContents() }
|
||||
) {
|
||||
result += createMethod(thisDescriptor, DESCRIBE_CONTENTS, Modality.OPEN, thisDescriptor.builtIns.intType)
|
||||
} else if (name.asString() == WRITE_TO_PARCEL.methodName
|
||||
&& thisDescriptor.isParcelize
|
||||
&& !thisDescriptor.isSealed()
|
||||
&& isParcelizePluginEnabled()
|
||||
&& result.none { it.isWriteToParcel() }
|
||||
) {
|
||||
val builtIns = thisDescriptor.builtIns
|
||||
val parcelClassType = resolveParcelClassType(thisDescriptor.module) ?: ErrorUtils.createErrorType(ErrorTypeKind.UNRESOLVED_PARCEL_TYPE)
|
||||
result += createMethod(
|
||||
thisDescriptor, WRITE_TO_PARCEL, Modality.OPEN,
|
||||
builtIns.unitType, "parcel" to parcelClassType, "flags" to builtIns.intType
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun SimpleFunctionDescriptor.isDescribeContents(): Boolean {
|
||||
return this.kind != CallableMemberDescriptor.Kind.FAKE_OVERRIDE
|
||||
&& modality != Modality.ABSTRACT
|
||||
&& typeParameters.isEmpty()
|
||||
&& valueParameters.isEmpty()
|
||||
// Unfortunately, we can't check the return type as it's unresolved in IDE light classes
|
||||
}
|
||||
}
|
||||
|
||||
internal fun SimpleFunctionDescriptor.isWriteToParcel(): Boolean {
|
||||
return typeParameters.isEmpty()
|
||||
&& valueParameters.size == 2
|
||||
// Unfortunately, we can't check the first parameter type as it's unresolved in IDE light classes
|
||||
&& KotlinBuiltIns.isInt(valueParameters[1].type)
|
||||
&& returnType?.let { KotlinBuiltIns.isUnit(it) } == true
|
||||
}
|
||||
|
||||
interface ParcelizeSyntheticComponent {
|
||||
val componentKind: ComponentKind
|
||||
|
||||
enum class ComponentKind(val methodName: String) {
|
||||
WRITE_TO_PARCEL(WRITE_TO_PARCEL_NAME.identifier),
|
||||
DESCRIBE_CONTENTS(DESCRIBE_CONTENTS_NAME.identifier),
|
||||
NEW_ARRAY(NEW_ARRAY_NAME.identifier),
|
||||
CREATE_FROM_PARCEL(CREATE_FROM_PARCEL_NAME.identifier)
|
||||
}
|
||||
}
|
||||
|
||||
val ClassDescriptor.hasParcelizeAnnotation: Boolean
|
||||
get() = PARCELIZE_CLASS_FQ_NAMES.any(annotations::hasAnnotation)
|
||||
|
||||
val ClassDescriptor.isParcelize: Boolean
|
||||
get() = hasParcelizeAnnotation
|
||||
|| getSuperClassNotAny()?.takeIf(DescriptorUtils::isSealedClass)?.hasParcelizeAnnotation == true
|
||||
|| getSuperInterfaces().any { DescriptorUtils.isSealedClass(it) && it.hasParcelizeAnnotation }
|
||||
|
||||
val KotlinType.isParceler: Boolean
|
||||
get() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQN
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
fun getTypeParcelers(annotations: Annotations): List<TypeParcelerMapping> {
|
||||
val serializers = mutableListOf<TypeParcelerMapping>()
|
||||
|
||||
for (annotation in annotations.filter { it.fqName in ParcelizeNames.TYPE_PARCELER_FQ_NAMES }) {
|
||||
val (mappedType, parcelerType) = annotation.type.arguments.takeIf { it.size == 2 } ?: continue
|
||||
serializers += TypeParcelerMapping(mappedType.type, parcelerType.type)
|
||||
}
|
||||
|
||||
return serializers
|
||||
}
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.diagnostic
|
||||
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DefaultErrorMessages
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.DiagnosticFactoryToRendererMap
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_CLASS_OR_OBJECT
|
||||
import org.jetbrains.kotlin.diagnostics.rendering.Renderers.RENDER_TYPE_WITH_ANNOTATIONS
|
||||
|
||||
object DefaultErrorMessagesParcelize : DefaultErrorMessages.Extension {
|
||||
private val MAP = DiagnosticFactoryToRendererMap("Parcelize")
|
||||
override fun getMap() = MAP
|
||||
|
||||
init {
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_BE_CLASS,
|
||||
"'Parcelable' should be a class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_DELEGATE_IS_NOT_ALLOWED,
|
||||
"Delegating 'Parcelable' is not allowed"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS,
|
||||
"'Parcelable' should not be a 'enum class'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_BE_INSTANTIABLE,
|
||||
"'Parcelable' should not be an 'abstract' class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_CANT_BE_INNER_CLASS,
|
||||
"'Parcelable' can't be an inner class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_CANT_BE_LOCAL_CLASS,
|
||||
"'Parcelable' can't be a local class"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.NO_PARCELABLE_SUPERTYPE,
|
||||
"No 'Parcelable' supertype"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR,
|
||||
"'Parcelable' should have a primary constructor"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY,
|
||||
"The primary constructor is empty, no data will be serialized to 'Parcel'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR,
|
||||
"'Parcelable' constructor parameter should be 'val' or 'var'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PROPERTY_WONT_BE_SERIALIZED,
|
||||
"Property would not be serialized into a 'Parcel'. Add '@IgnoredOnParcel' annotation to remove the warning"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED,
|
||||
"Overriding 'writeToParcel' is not allowed. Use 'Parceler' companion object instead"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.CREATOR_DEFINITION_IS_NOT_ALLOWED,
|
||||
"'CREATOR' definition is not allowed. Use 'Parceler' companion object instead"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELABLE_TYPE_NOT_SUPPORTED,
|
||||
"Type is not directly supported by 'Parcelize'. " +
|
||||
"Annotate the parameter type with '@RawValue' if you want it to be serialized using 'writeValue()'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELER_SHOULD_BE_OBJECT,
|
||||
"Parceler should be an object"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.PARCELER_TYPE_INCOMPATIBLE,
|
||||
"Parceler type {0} is incompatible with {1}",
|
||||
RENDER_TYPE_WITH_ANNOTATIONS, RENDER_TYPE_WITH_ANNOTATIONS
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.DUPLICATING_TYPE_PARCELERS,
|
||||
"Duplicating ''TypeParceler'' annotations"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.REDUNDANT_TYPE_PARCELER,
|
||||
"This ''TypeParceler'' is already provided for {0}",
|
||||
RENDER_CLASS_OR_OBJECT
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.CLASS_SHOULD_BE_PARCELIZE,
|
||||
"{0} should be annotated with ''@Parcelize''",
|
||||
RENDER_CLASS_OR_OBJECT
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL,
|
||||
"'@IgnoredOnParcel' is only applicable to class properties"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY,
|
||||
"'@IgnoredOnParcel' is inapplicable to properties without default value declared in the primary constructor"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.FORBIDDEN_DEPRECATED_ANNOTATION,
|
||||
"Parceler-related annotations from package 'kotlinx.android.parcel' are forbidden. Change package to 'kotlinx.parcelize'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.DEPRECATED_ANNOTATION,
|
||||
"Parcelize annotations from package 'kotlinx.android.parcel' are deprecated. Change package to 'kotlinx.parcelize'"
|
||||
)
|
||||
|
||||
MAP.put(
|
||||
ErrorsParcelize.DEPRECATED_PARCELER,
|
||||
"'kotlinx.android.parcel.Parceler' is deprecated. Use 'kotlinx.parcelize.Parceler' instead"
|
||||
)
|
||||
}
|
||||
}
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright 2010-2015 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize.diagnostic;
|
||||
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory0;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory1;
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticFactory2;
|
||||
import org.jetbrains.kotlin.diagnostics.Errors;
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject;
|
||||
import org.jetbrains.kotlin.types.KotlinType;
|
||||
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.ERROR;
|
||||
import static org.jetbrains.kotlin.diagnostics.Severity.WARNING;
|
||||
|
||||
public interface ErrorsParcelize {
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_BE_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_DELEGATE_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_NOT_BE_ENUM_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_BE_INSTANTIABLE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_CANT_BE_INNER_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_CANT_BE_LOCAL_CLASS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> NO_PARCELABLE_SUPERTYPE = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_SHOULD_HAVE_PRIMARY_CONSTRUCTOR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_CONSTRUCTOR_PARAMETER_SHOULD_BE_VAL_OR_VAR = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PROPERTY_WONT_BE_SERIALIZED = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> OVERRIDING_WRITE_TO_PARCEL_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> CREATOR_DEFINITION_IS_NOT_ALLOWED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELABLE_TYPE_NOT_SUPPORTED = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> PARCELER_SHOULD_BE_OBJECT = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory2<PsiElement, KotlinType, KotlinType> PARCELER_TYPE_INCOMPATIBLE = DiagnosticFactory2.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DUPLICATING_TYPE_PARCELERS = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory1<PsiElement, KtClassOrObject> REDUNDANT_TYPE_PARCELER = DiagnosticFactory1.create(WARNING);
|
||||
DiagnosticFactory1<PsiElement, KtClassOrObject> CLASS_SHOULD_BE_PARCELIZE = DiagnosticFactory1.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> FORBIDDEN_DEPRECATED_ANNOTATION = DiagnosticFactory0.create(ERROR);
|
||||
DiagnosticFactory0<PsiElement> DEPRECATED_ANNOTATION = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> DEPRECATED_PARCELER = DiagnosticFactory0.create(ERROR);
|
||||
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_IGNORED_ON_PARCEL = DiagnosticFactory0.create(WARNING);
|
||||
DiagnosticFactory0<PsiElement> INAPPLICABLE_IGNORED_ON_PARCEL_CONSTRUCTOR_PROPERTY = DiagnosticFactory0.create(WARNING);
|
||||
|
||||
@SuppressWarnings("UnusedDeclaration")
|
||||
Object _initializer = new Object() {
|
||||
{
|
||||
Errors.Initializer.initializeFactoryNamesAndDefaultErrorMessages(ErrorsParcelize.class, DefaultErrorMessagesParcelize.INSTANCE);
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
+434
@@ -0,0 +1,434 @@
|
||||
/*
|
||||
* 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.kotlin.parcelize.serializers
|
||||
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
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.ParcelizeNames.RAW_VALUE_ANNOTATION_FQ_NAMES
|
||||
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.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
import org.jetbrains.kotlin.synthetic.isVisibleOutside
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.isError
|
||||
import org.jetbrains.kotlin.types.typeUtil.builtIns
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
interface ParcelSerializer {
|
||||
val asmType: Type
|
||||
|
||||
fun writeValue(v: InstructionAdapter)
|
||||
fun readValue(v: InstructionAdapter)
|
||||
|
||||
data class ParcelSerializerContext(
|
||||
val typeMapper: KotlinTypeMapper,
|
||||
val containerClassType: Type,
|
||||
val typeParcelers: List<TypeParcelerMapping>,
|
||||
val frameMap: FrameMap
|
||||
) {
|
||||
fun findParcelerClass(type: KotlinType): KotlinType? {
|
||||
return typeParcelers.firstOrNull { it.first == type }?.second
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val WRITE_WITH_FQ_NAMES = listOf(
|
||||
FqName("kotlinx.parcelize.WriteWith"),
|
||||
FqName("kotlinx.android.parcel.WriteWith"),
|
||||
)
|
||||
|
||||
private fun KotlinTypeMapper.mapTypeSafe(type: KotlinType, forceBoxed: Boolean) = when {
|
||||
type.isError -> Type.getObjectType("java/lang/Object")
|
||||
else -> mapType(type, null, if (forceBoxed) TypeMappingMode.GENERIC_ARGUMENT else TypeMappingMode.DEFAULT)
|
||||
}
|
||||
|
||||
fun get(
|
||||
type: KotlinType,
|
||||
asmType: Type,
|
||||
context: ParcelSerializerContext,
|
||||
forceBoxed: Boolean = false,
|
||||
strict: Boolean = false
|
||||
): ParcelSerializer {
|
||||
val typeMapper = context.typeMapper
|
||||
|
||||
val className = asmType.className
|
||||
fun strict() = strict && !type.hasAnyAnnotation(RAW_VALUE_ANNOTATION_FQ_NAMES)
|
||||
|
||||
fun findCustomParcelerType(type: KotlinType): KotlinType? {
|
||||
type.findAnyAnnotation(WRITE_WITH_FQ_NAMES)?.let { writeWith ->
|
||||
val parceler = writeWith.type.arguments.singleOrNull()?.type
|
||||
if (parceler != null && !parceler.isError) {
|
||||
return parceler
|
||||
}
|
||||
}
|
||||
|
||||
return context.findParcelerClass(type)?.takeIf { !it.isError }
|
||||
}
|
||||
|
||||
findCustomParcelerType(type)?.let { return TypeParcelerParcelSerializer(asmType, it, context.typeMapper) }
|
||||
|
||||
return when {
|
||||
// Somewhat inconsistently, there are no `Parcel.writeShortArray/createShortArray` methods,
|
||||
// so we need to exclude "[S" here.
|
||||
asmType.descriptor == "[I"
|
||||
|| asmType.descriptor == "[Z"
|
||||
|| asmType.descriptor == "[B"
|
||||
|| asmType.descriptor == "[C"
|
||||
|| asmType.descriptor == "[D"
|
||||
|| asmType.descriptor == "[F"
|
||||
|| asmType.descriptor == "[J"
|
||||
-> {
|
||||
val customElementParcelerType = findCustomParcelerType(type.builtIns.getArrayElementType(type))
|
||||
if (customElementParcelerType != null) {
|
||||
val elementType = asmType.elementType
|
||||
val elementParceler = TypeParcelerParcelSerializer(elementType, customElementParcelerType, context.typeMapper)
|
||||
ArrayParcelSerializer(asmType, elementParceler)
|
||||
} else {
|
||||
PrimitiveArrayParcelSerializer(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
asmType.descriptor == "[Landroid/os/IBinder;" -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBinderArray"), Method("createBinderArray")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.descriptor == "[Ljava/lang/String;" -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStringArray"), Method("createStringArray")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.sort == Type.ARRAY -> {
|
||||
val elementType = type.builtIns.getArrayElementType(type)
|
||||
val elementSerializer =
|
||||
get(elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, strict = strict())
|
||||
|
||||
wrapToNullAwareIfNeeded(type, ArrayParcelSerializer(asmType, elementSerializer))
|
||||
}
|
||||
|
||||
asmType.isPrimitive() -> {
|
||||
if (forceBoxed || type.isMarkedNullable)
|
||||
wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forUnboxedType(asmType))
|
||||
else
|
||||
PrimitiveTypeParcelSerializer.getInstance(asmType)
|
||||
}
|
||||
|
||||
asmType.isUnsigned() -> {
|
||||
ParcelSerializerStub(asmType, type)
|
||||
}
|
||||
|
||||
asmType.isString() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeString"),
|
||||
Method("readString")
|
||||
)
|
||||
}
|
||||
|
||||
className == List::class.java.canonicalName
|
||||
|| className == ArrayList::class.java.canonicalName
|
||||
|| className == LinkedList::class.java.canonicalName
|
||||
|| className == Set::class.java.canonicalName
|
||||
|| className == SortedSet::class.java.canonicalName
|
||||
|| className == NavigableSet::class.java.canonicalName
|
||||
|| className == HashSet::class.java.canonicalName
|
||||
|| className == LinkedHashSet::class.java.canonicalName
|
||||
|| className == TreeSet::class.java.canonicalName
|
||||
-> {
|
||||
val elementType = type.arguments.single().type
|
||||
val elementAsmType = typeMapper.mapTypeSafe(elementType, forceBoxed = true)
|
||||
|
||||
if (className == List::class.java.canonicalName) {
|
||||
// Don't care if the element type is nullable cause both writeStrongBinder() and writeString() support null values
|
||||
if (elementAsmType.descriptor == "Landroid/os/IBinder;") {
|
||||
return NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBinderList"), Method("createBinderArrayList", "()Ljava/util/ArrayList;")
|
||||
)
|
||||
} else if (elementAsmType.descriptor == "Ljava/lang/String;") {
|
||||
return NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStringList"), Method("createStringArrayList", "()Ljava/util/ArrayList;")
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val elementSerializer = get(elementType, elementAsmType, context, forceBoxed = true, strict = strict())
|
||||
wrapToNullAwareIfNeeded(type, ListSetParcelSerializer(asmType, elementSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
className == Map::class.java.canonicalName
|
||||
|| className == SortedMap::class.java.canonicalName
|
||||
|| className == NavigableMap::class.java.canonicalName
|
||||
|| className == HashMap::class.java.canonicalName
|
||||
|| className == LinkedHashMap::class.java.canonicalName
|
||||
|| className == TreeMap::class.java.canonicalName
|
||||
|| className == ConcurrentHashMap::class.java.canonicalName
|
||||
-> {
|
||||
val (keyType, valueType) = type.arguments.apply { assert(this.size == 2) }
|
||||
val keySerializer = get(
|
||||
keyType.type, typeMapper.mapTypeSafe(keyType.type, forceBoxed = true), context, forceBoxed = true, strict = strict()
|
||||
)
|
||||
val valueSerializer = get(
|
||||
valueType.type,
|
||||
typeMapper.mapTypeSafe(valueType.type, forceBoxed = true),
|
||||
context,
|
||||
forceBoxed = true,
|
||||
strict = strict()
|
||||
)
|
||||
wrapToNullAwareIfNeeded(type, MapParcelSerializer(asmType, keySerializer, valueSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
asmType.isBoxedPrimitive() -> {
|
||||
wrapToNullAwareIfNeeded(type, BoxedPrimitiveTypeParcelSerializer.forBoxedType(asmType))
|
||||
}
|
||||
|
||||
asmType.isBlob() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBlob"),
|
||||
Method("readBlob")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSize() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeSize"),
|
||||
Method("readSize")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSizeF() -> wrapToNullAwareIfNeeded(
|
||||
type, NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeSizeF"),
|
||||
Method("readSizeF")
|
||||
)
|
||||
)
|
||||
|
||||
asmType.isBundle() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBundle"),
|
||||
Method("readBundle")
|
||||
)
|
||||
}
|
||||
|
||||
type.isIBinder() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStrongBinder", "(Landroid/os/IBinder;)V"),
|
||||
Method("readStrongBinder", "()Landroid/os/IBinder;")
|
||||
)
|
||||
}
|
||||
|
||||
type.isIInterface() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeStrongInterface", "(Landroid/os/IInterface;)V"),
|
||||
Method("readStrongInterface", "()Landroid/os/IInterface;")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isPersistableBundle() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeBundle"),
|
||||
Method("readBundle")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSparseBooleanArray() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeSparseBooleanArray"),
|
||||
Method("readSparseBooleanArray")
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSparseIntArray() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.INT_TYPE), context.frameMap
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSparseLongArray() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, SparseArrayParcelSerializer(
|
||||
asmType, PrimitiveTypeParcelSerializer.getInstance(Type.LONG_TYPE), context.frameMap
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isSparseArray() -> {
|
||||
val elementType = type.arguments.single().type
|
||||
val elementSerializer = get(
|
||||
elementType, typeMapper.mapTypeSafe(elementType, forceBoxed = true), context, forceBoxed = true, strict = strict()
|
||||
)
|
||||
wrapToNullAwareIfNeeded(type, SparseArrayParcelSerializer(asmType, elementSerializer, context.frameMap))
|
||||
}
|
||||
|
||||
type.isCharSequence() -> {
|
||||
CharSequenceParcelSerializer(asmType)
|
||||
}
|
||||
|
||||
type.isException() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeException"),
|
||||
Method("readException")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
asmType.isFileDescriptor() -> {
|
||||
wrapToNullAwareIfNeeded(
|
||||
type, NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeRawFileDescriptor"),
|
||||
Method("readRawFileDescriptor")
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// Write at least a nullability byte.
|
||||
// We don't want parcel to be empty in case if all constructor parameters are objects
|
||||
type.isNamedObject() -> {
|
||||
NullAwareParcelSerializerWrapper(ObjectParcelSerializer(asmType, type, typeMapper))
|
||||
}
|
||||
|
||||
type.isEnum() -> {
|
||||
wrapToNullAwareIfNeeded(type, EnumParcelSerializer(asmType))
|
||||
}
|
||||
|
||||
type.isParcelable() -> {
|
||||
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
if (clazz != null && clazz.modality == Modality.FINAL && clazz.source is PsiSourceElement) {
|
||||
|
||||
fun MemberScope.findCreatorField() = getContributedVariables(
|
||||
Name.identifier("CREATOR"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS
|
||||
).firstOrNull()
|
||||
|
||||
val creatorVar = when (clazz) {
|
||||
is JavaClassDescriptor -> clazz.staticScope.findCreatorField()
|
||||
else -> clazz.companionObjectDescriptor?.unsubstitutedMemberScope?.findCreatorField()
|
||||
?.takeIf { it.annotations.hasAnnotation(FqName(JvmField::class.java.name)) }
|
||||
}
|
||||
|
||||
val creatorAsmType = when {
|
||||
creatorVar != null -> typeMapper.mapTypeSafe(creatorVar.type, forceBoxed = true)
|
||||
clazz.isParcelize -> Type.getObjectType(asmType.internalName + "\$Creator")
|
||||
else -> null
|
||||
}
|
||||
|
||||
creatorAsmType?.let { wrapToNullAwareIfNeeded(type, EfficientParcelableParcelSerializer(asmType)) }
|
||||
?: GenericParcelableParcelSerializer(asmType, context.containerClassType)
|
||||
} else {
|
||||
GenericParcelableParcelSerializer(asmType, context.containerClassType)
|
||||
}
|
||||
}
|
||||
|
||||
type.isSerializable() -> {
|
||||
NullCompliantObjectParcelSerializer(
|
||||
asmType,
|
||||
Method("writeSerializable", "(Ljava/io/Serializable;)V"),
|
||||
Method("readSerializable", "()Ljava/io/Serializable;")
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (strict && !type.hasAnyAnnotation(RAW_VALUE_ANNOTATION_FQ_NAMES))
|
||||
throw IllegalArgumentException("Illegal type")
|
||||
else
|
||||
GenericParcelSerializer(asmType)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapToNullAwareIfNeeded(type: KotlinType, serializer: ParcelSerializer): ParcelSerializer {
|
||||
return when {
|
||||
type.isMarkedNullable -> NullAwareParcelSerializerWrapper(serializer)
|
||||
else -> serializer
|
||||
}
|
||||
}
|
||||
|
||||
private fun Type.isBlob() = this.sort == Type.ARRAY && this.elementType == Type.BYTE_TYPE
|
||||
private fun Type.isString() = this.descriptor == "Ljava/lang/String;"
|
||||
private fun Type.isSize() = this.descriptor == "Landroid/util/Size;"
|
||||
private fun Type.isSizeF() = this.descriptor == "Landroid/util/SizeF;"
|
||||
private fun Type.isFileDescriptor() = this.descriptor == "Ljava/io/FileDescriptor;"
|
||||
private fun Type.isBundle() = this.descriptor == "Landroid/os/Bundle;"
|
||||
private fun Type.isPersistableBundle() = this.descriptor == "Landroid/os/PersistableBundle;"
|
||||
private fun Type.isSparseBooleanArray() = this.descriptor == "Landroid/util/SparseBooleanArray;"
|
||||
private fun Type.isSparseIntArray() = this.descriptor == "Landroid/util/SparseIntArray;"
|
||||
private fun Type.isSparseLongArray() = this.descriptor == "Landroid/util/SparseLongArray;"
|
||||
private fun Type.isSparseArray() = this.descriptor == "Landroid/util/SparseArray;"
|
||||
private fun KotlinType.isException() = matchesFqNameWithSupertypes("java.lang.Exception")
|
||||
private fun KotlinType.isIBinder() = matchesFqNameWithSupertypes("android.os.IBinder")
|
||||
private fun KotlinType.isIInterface() = matchesFqNameWithSupertypes("android.os.IInterface")
|
||||
private fun KotlinType.isCharSequence() = matchesFqName("kotlin.CharSequence") || matchesFqName("java.lang.CharSequence")
|
||||
|
||||
private fun KotlinType.isSerializable() = matchesFqNameWithSupertypes("java.io.Serializable")
|
||||
|| matchesFqNameWithSupertypes("kotlin.Function")
|
||||
|
||||
private fun KotlinType.isNamedObject(): Boolean {
|
||||
val classDescriptor = constructor.declarationDescriptor as? ClassDescriptor ?: return false
|
||||
if (!classDescriptor.visibility.isVisibleOutside()) return false
|
||||
if (DescriptorUtils.isAnonymousObject(classDescriptor)) return false
|
||||
return classDescriptor.kind == ClassKind.OBJECT
|
||||
}
|
||||
|
||||
private fun KotlinType.isEnum() = (constructor.declarationDescriptor as? ClassDescriptor)?.kind == ClassKind.ENUM_CLASS
|
||||
|
||||
private fun Type.isPrimitive(): Boolean = when (this.sort) {
|
||||
Type.BOOLEAN, Type.CHAR, Type.BYTE, Type.SHORT, Type.INT, Type.FLOAT, Type.LONG, Type.DOUBLE -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun Type.isUnsigned(): Boolean = when (descriptor) {
|
||||
"Lkotlin/UByte;", "Lkotlin/UShort;", "Lkotlin/UInt;", "Lkotlin/ULong;",
|
||||
"Lkotlin/UByteArray;", "Lkotlin/UShortArray;", "Lkotlin/UIntArray;", "Lkotlin/ULongArray;" -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
private fun Type.isBoxedPrimitive(): Boolean = when (this.descriptor) {
|
||||
"Ljava/lang/Boolean;",
|
||||
"Ljava/lang/Character;",
|
||||
"Ljava/lang/Byte;",
|
||||
"Ljava/lang/Short;",
|
||||
"Ljava/lang/Integer;",
|
||||
"Ljava/lang/Float;",
|
||||
"Ljava/lang/Long;",
|
||||
"Ljava/lang/Double;"
|
||||
-> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
}
|
||||
+837
@@ -0,0 +1,837 @@
|
||||
/*
|
||||
* 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.kotlin.parcelize.serializers
|
||||
|
||||
import org.jetbrains.kotlin.codegen.AsmUtil
|
||||
import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.codegen.useTmpVar
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.parcelize.serializers.BoxedPrimitiveTypeParcelSerializer.Companion.BOXED_VALUE_METHOD_NAMES
|
||||
import org.jetbrains.kotlin.renderer.DescriptorRenderer
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
val PARCEL_TYPE = Type.getObjectType("android/os/Parcel")
|
||||
val PARCELER_TYPE = Type.getObjectType("kotlinx/parcelize/Parceler")
|
||||
|
||||
internal class GenericParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeValue", "(Ljava/lang/Object;)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.aconst(asmType) // -> parcel, type
|
||||
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readValue", "(Ljava/lang/ClassLoader;)Ljava/lang/Object;", false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class TypeParcelerParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val parcelerType: KotlinType,
|
||||
private val typeMapper: KotlinTypeMapper
|
||||
) : ParcelSerializer {
|
||||
private val parcelerAsmType = typeMapper.mapType(parcelerType)
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, value(?)
|
||||
boxTypeIfNeeded(v) // -> parcel, (boxed)value
|
||||
|
||||
v.swap() // -> value, parcel
|
||||
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> value, parcel, parceler
|
||||
v.dupX2() // -> parceler, value, parcel, parceler
|
||||
v.pop() // -> parceler, value, parcel
|
||||
v.load(2, Type.INT_TYPE) // -> parceler, value, parcel, flags
|
||||
v.invokeinterface(PARCELER_TYPE.internalName, "write", "(Ljava/lang/Object;Landroid/os/Parcel;I)V")
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
|
||||
v.swap() // -> parceler, parcel
|
||||
v.invokeinterface(PARCELER_TYPE.internalName, "create", "(Landroid/os/Parcel;)Ljava/lang/Object;") // -> obj
|
||||
unboxTypeIfNeeded(v)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
|
||||
private fun handleSpecialBoxingCases(v: InstructionAdapter): Type? {
|
||||
assert(asmType.sort != Type.METHOD)
|
||||
|
||||
if (asmType.sort == Type.OBJECT || asmType.sort == Type.ARRAY) {
|
||||
return null
|
||||
}
|
||||
|
||||
if (asmType == Type.VOID_TYPE) {
|
||||
v.pop()
|
||||
v.aconst(null)
|
||||
return null
|
||||
}
|
||||
|
||||
return AsmUtil.boxType(asmType)
|
||||
}
|
||||
|
||||
private fun boxTypeIfNeeded(v: InstructionAdapter) {
|
||||
val boxedType = handleSpecialBoxingCases(v) ?: return
|
||||
v.invokestatic(boxedType.internalName, "valueOf", "(${asmType.descriptor})${boxedType.descriptor}", false)
|
||||
}
|
||||
|
||||
private fun unboxTypeIfNeeded(v: InstructionAdapter) {
|
||||
val boxedType = handleSpecialBoxingCases(v) ?: return
|
||||
val getValueMethodName = BOXED_VALUE_METHOD_NAMES.getValue(boxedType.internalName)
|
||||
v.castIfNeeded(boxedType)
|
||||
v.invokevirtual(boxedType.internalName, getValueMethodName, "()${asmType.descriptor}", false)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ArrayParcelSerializer(override val asmType: Type, private val elementSerializer: ParcelSerializer) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.dupX1() // -> arr, parcel, arr
|
||||
v.arraylength() // -> arr, parcel, length
|
||||
v.dupX2() // -> length, arr, parcel, length
|
||||
|
||||
// Write array size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> length, arr
|
||||
v.swap() // -> arr, length
|
||||
v.aconst(0) // -> arr, length, <index>
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
|
||||
// Loop
|
||||
v.dup2() // -> arr, length, index, length, index
|
||||
v.ificmple(loopIsOver) // -> arr, length, index
|
||||
|
||||
v.swap() // -> arr, index, length
|
||||
v.dupX2() // -> length, arr, index, length
|
||||
v.pop() // -> length, arr, index
|
||||
v.dup2() // -> length, arr, index, arr, index
|
||||
v.load(1, PARCEL_TYPE) // -> length, arr, index, arr, index, parcel
|
||||
v.dupX2() // -> length, arr, index, parcel, arr, index, parcel
|
||||
v.pop() // -> length, arr, index, parcel, arr, index
|
||||
v.aload(elementSerializer.asmType) // -> length, arr, index, parcel, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
elementSerializer.writeValue(v) // -> length, arr, index
|
||||
|
||||
v.aconst(1) // -> length, arr, index, (1)
|
||||
v.add(Type.INT_TYPE) // -> length, arr, (index + 1)
|
||||
v.swap() // -> length, (index + 1), arr
|
||||
v.dupX2() // -> arr, length, (index + 1), arr
|
||||
v.pop() // -> arr, length, (index + 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop2() // -> arr
|
||||
v.pop()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> length
|
||||
v.dup() // -> length, length
|
||||
v.newarray(elementSerializer.asmType) // -> length, arr
|
||||
v.swap() // -> arr, length
|
||||
v.aconst(0) // -> arr, length, index
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dup2() // -> arr, length, index, length, index
|
||||
v.ificmple(loopIsOver) // -> arr, length, index
|
||||
|
||||
v.swap() // -> arr, index, length
|
||||
v.dupX2() // -> length, arr, index, length
|
||||
v.pop() // -> length, arr, index
|
||||
v.dup2() // -> length, arr, index, arr, index
|
||||
|
||||
v.load(1, PARCEL_TYPE) // -> length, arr, index, arr, index, parcel
|
||||
elementSerializer.readValue(v) // -> length, arr, index, arr, index, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
v.astore(elementSerializer.asmType) // -> length, arr, index
|
||||
v.aconst(1) // -> length, arr, index, (1)
|
||||
v.add(Type.INT_TYPE) // -> length, arr, (index + 1)
|
||||
v.swap() // -> length, (index + 1), arr
|
||||
v.dupX2() // -> arr, length, (index + 1), arr
|
||||
v.pop() // -> arr, length, (index + 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop2() // -> arr
|
||||
}
|
||||
}
|
||||
|
||||
internal fun InstructionAdapter.castIfNeeded(targetType: Type) {
|
||||
if (targetType.sort != Type.OBJECT && targetType.sort != Type.ARRAY) return
|
||||
if (targetType.descriptor == "Ljava/lang/Object;") return
|
||||
checkcast(targetType)
|
||||
}
|
||||
|
||||
internal class ListSetParcelSerializer(
|
||||
asmType: Type,
|
||||
elementSerializer: ParcelSerializer,
|
||||
frameMap: FrameMap
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
|
||||
override fun getSize(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Collection", "size", "()I")
|
||||
}
|
||||
|
||||
override fun getIterator(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Collection", "iterator", "()Ljava/util/Iterator;")
|
||||
}
|
||||
|
||||
override fun doWriteValue(v: InstructionAdapter) {
|
||||
// -> parcel, obj
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
elementSerializer.writeValue(v)
|
||||
}
|
||||
|
||||
override fun doReadValue(v: InstructionAdapter) {
|
||||
// -> collection, parcel
|
||||
|
||||
elementSerializer.readValue(v) // -> collection, element
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
|
||||
v.invokevirtual(collectionType.internalName, "add", "(Ljava/lang/Object;)Z", false) // -> bool
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal class MapParcelSerializer(
|
||||
asmType: Type,
|
||||
private val keySerializer: ParcelSerializer,
|
||||
elementSerializer: ParcelSerializer,
|
||||
frameMap: FrameMap
|
||||
) : AbstractCollectionParcelSerializer(asmType, elementSerializer, frameMap) {
|
||||
override fun getSize(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Map", "size", "()I")
|
||||
}
|
||||
|
||||
override fun getIterator(v: InstructionAdapter) {
|
||||
v.invokeinterface("java/util/Map", "entrySet", "()Ljava/util/Set;")
|
||||
v.invokeinterface("java/util/Set", "iterator", "()Ljava/util/Iterator;")
|
||||
}
|
||||
|
||||
override fun doWriteValue(v: InstructionAdapter) {
|
||||
// -> parcel, obj
|
||||
|
||||
v.dup2() // -> parcel, obj, parcel, obj
|
||||
|
||||
v.invokeinterface("java/util/Map\$Entry", "getKey", "()Ljava/lang/Object;") // -> parcel, obj, parcel, key
|
||||
v.castIfNeeded(keySerializer.asmType)
|
||||
keySerializer.writeValue(v) // -> parcel, obj
|
||||
|
||||
v.invokeinterface("java/util/Map\$Entry", "getValue", "()Ljava/lang/Object;") // -> parcel, value
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
elementSerializer.writeValue(v)
|
||||
}
|
||||
|
||||
override fun doReadValue(v: InstructionAdapter) {
|
||||
// -> map, parcel
|
||||
v.dup() // -> map, parcel, parcel
|
||||
|
||||
keySerializer.readValue(v) // -> map, parcel, key
|
||||
v.castIfNeeded(keySerializer.asmType)
|
||||
|
||||
v.swap() // -> map, key, parcel
|
||||
|
||||
elementSerializer.readValue(v) // -> map, key, value
|
||||
v.castIfNeeded(elementSerializer.asmType)
|
||||
|
||||
v.invokeinterface("java/util/Map", "put", "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;") // -> obj
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal abstract class AbstractCollectionParcelSerializer(
|
||||
final override val asmType: Type,
|
||||
protected val elementSerializer: ParcelSerializer,
|
||||
private val frameMap: FrameMap
|
||||
) : ParcelSerializer {
|
||||
protected val collectionType: Type = Type.getObjectType(
|
||||
when (asmType.internalName) {
|
||||
"java/util/List" -> "java/util/ArrayList"
|
||||
"java/util/Set" -> "java/util/LinkedHashSet"
|
||||
"java/util/SortedSet" -> "java/util/TreeSet"
|
||||
"java/util/NavigableSet" -> "java/util/TreeSet"
|
||||
"java/util/Map" -> "java/util/LinkedHashMap"
|
||||
"java/util/SortedMap" -> "java/util/TreeMap"
|
||||
"java/util/NavigableMap" -> "java/util/TreeMap"
|
||||
else -> asmType.internalName
|
||||
}
|
||||
)
|
||||
|
||||
private var hasConstructorWithCapacity = when (collectionType.internalName) {
|
||||
"java/util/LinkedList", "java/util/TreeSet", "java/util/TreeMap" -> false
|
||||
else -> true
|
||||
}
|
||||
|
||||
/**
|
||||
* Stack before: collection
|
||||
* Stack after: size
|
||||
*/
|
||||
protected abstract fun getSize(v: InstructionAdapter)
|
||||
|
||||
/**
|
||||
* Stack before: collection
|
||||
* Stack after: iterator
|
||||
*/
|
||||
protected abstract fun getIterator(v: InstructionAdapter)
|
||||
|
||||
/**
|
||||
* Stack before: parcel, obj
|
||||
* Stack after: <empty>
|
||||
*/
|
||||
protected abstract fun doWriteValue(v: InstructionAdapter)
|
||||
|
||||
/**
|
||||
* Stack before: collection, parcel
|
||||
* Stack after: <empty>
|
||||
*/
|
||||
protected abstract fun doReadValue(v: InstructionAdapter)
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
val labelIteratorLoop = Label()
|
||||
val labelReturn = Label()
|
||||
|
||||
v.dupX1() // -> collection, parcel, collection
|
||||
getSize(v) // -> collection, parcel, size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // collection
|
||||
getIterator(v) // -> iterator
|
||||
|
||||
v.visitLabel(labelIteratorLoop)
|
||||
v.dup() // -> iterator, iterator
|
||||
v.invokeinterface("java/util/Iterator", "hasNext", "()Z") // -> iterator, hasNext
|
||||
v.ifeq(labelReturn) // -> iterator
|
||||
|
||||
v.dup() // -> iterator, iterator
|
||||
|
||||
v.load(1, PARCEL_TYPE) // iterator, iterator, parcel
|
||||
v.swap() // iterator, parcel, iterator
|
||||
v.invokeinterface("java/util/Iterator", "next", "()Ljava/lang/Object;") // -> iterator, parcel, obj
|
||||
|
||||
doWriteValue(v) // -> iterator
|
||||
|
||||
v.goTo(labelIteratorLoop)
|
||||
|
||||
v.visitLabel(labelReturn)
|
||||
v.pop()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.store(sizeVarIndex, Type.INT_TYPE)
|
||||
|
||||
v.anew(collectionType) // -> list
|
||||
v.dup() // -> list, list
|
||||
|
||||
if (hasConstructorWithCapacity) {
|
||||
v.load(sizeVarIndex, Type.INT_TYPE)
|
||||
v.invokespecial(collectionType.internalName, "<init>", "(I)V", false) // -> list
|
||||
} else {
|
||||
v.invokespecial(collectionType.internalName, "<init>", "()V", false) // -> list
|
||||
}
|
||||
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> list, size
|
||||
}
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dupX1() // -> size, list, size
|
||||
v.ifeq(loopIsOver) // -> size, list
|
||||
v.dup() // -> size, list, list
|
||||
v.load(1, PARCEL_TYPE) // -> size, list, list, parcel
|
||||
doReadValue(v) // -> size, list
|
||||
|
||||
v.swap() // -> list, size
|
||||
v.aconst(-1) // -> list, size, (-1)
|
||||
v.add(Type.INT_TYPE) // -> list, (size - 1)
|
||||
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.swap() // -> list, size
|
||||
v.pop()
|
||||
}
|
||||
}
|
||||
|
||||
internal class SparseArrayParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val valueSerializer: ParcelSerializer,
|
||||
private val frameMap: FrameMap
|
||||
) : ParcelSerializer {
|
||||
private val valueType = (valueSerializer as? PrimitiveTypeParcelSerializer)?.asmType ?: Type.getObjectType("java/lang/Object")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.dup() // -> parcel, arr, arr
|
||||
v.invokevirtual(asmType.internalName, "size", "()I", false) // -> parcel, arr, size
|
||||
v.dup2X1() // -> arr, size, parcel, arr, size
|
||||
v.swap() // -> arr, size, parcel, size, arr
|
||||
v.pop() // -> arr, size, parcel, size
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> arr, size
|
||||
|
||||
v.aconst(0) // -> arr, size, <index>
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dup2() // -> arr, size, index, size, index
|
||||
v.ificmple(loopIsOver) // -> arr, size, index
|
||||
|
||||
v.swap() // -> arr, index, size
|
||||
v.dupX2() // -> size, arr, index, size
|
||||
v.pop() // -> size, arr, index
|
||||
v.dup2() // -> size, arr, index, arr, index
|
||||
v.invokevirtual(asmType.internalName, "keyAt", "(I)I", false) // -> size, arr, index, key
|
||||
v.load(1, PARCEL_TYPE) // size, arr, index, key, parcel
|
||||
v.dupX2() // -> size, arr, parcel, index, key, parcel
|
||||
v.swap() // -> size, arr, parcel, index, parcel, key
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false) // -> size, arr, parcel, index
|
||||
|
||||
v.swap() // -> size, arr, index, parcel
|
||||
v.dupX2() // -> size, parcel, arr, index, parcel
|
||||
v.pop() // -> size, parcel, arr, index
|
||||
v.dup2X1() // -> size, arr, index, parcel, arr, index
|
||||
v.invokevirtual(asmType.internalName, "valueAt", "(I)${valueType.descriptor}", false) // -> size, arr, index, parcel, value
|
||||
valueSerializer.writeValue(v) // -> size, arr, index
|
||||
|
||||
v.aconst(1) // -> size, arr, index, (1)
|
||||
v.add(Type.INT_TYPE) // -> size, arr, (index + 1)
|
||||
v.dup2X1() // -> arr, (index + 1), size, arr, (index + 1)
|
||||
v.pop2() // -> arr, (index + 1), size
|
||||
v.swap() // -> arr, size, (index + 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop2()
|
||||
v.pop()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
frameMap.useTmpVar(Type.INT_TYPE) { sizeVarIndex ->
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> size
|
||||
v.store(sizeVarIndex, Type.INT_TYPE) // -> (empty)
|
||||
|
||||
v.anew(asmType) // -> arr
|
||||
v.dup() // -> arr, arr
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, arr, size
|
||||
v.invokespecial(asmType.internalName, "<init>", "(I)V", false) // -> arr
|
||||
|
||||
v.load(sizeVarIndex, Type.INT_TYPE) // -> arr, size
|
||||
}
|
||||
|
||||
val nextLoopIteration = Label()
|
||||
val loopIsOver = Label()
|
||||
|
||||
v.visitLabel(nextLoopIteration)
|
||||
v.dup() // -> arr, size, size
|
||||
v.ifle(loopIsOver) // -> arr, size
|
||||
v.swap() // -> size, arr
|
||||
v.dupX1() // -> arr, size, arr
|
||||
|
||||
v.load(1, PARCEL_TYPE) // -> arr, size, arr, parcel
|
||||
v.dup() // -> arr, size, arr, parcel, parcel
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false) // -> arr, size, arr, parcel, key
|
||||
v.swap() // -> arr, size, arr, key, parcel
|
||||
valueSerializer.readValue(v) // -> arr, size, arr, key, value
|
||||
v.invokevirtual(asmType.internalName, "put", "(I${valueType.descriptor})V", false) // -> arr, size
|
||||
v.aconst(-1) // -> arr, size, (-1)
|
||||
v.add(Type.INT_TYPE) // -> arr, (size - 1)
|
||||
v.goTo(nextLoopIteration)
|
||||
|
||||
v.visitLabel(loopIsOver)
|
||||
v.pop() // -> arr
|
||||
}
|
||||
}
|
||||
|
||||
internal class ObjectParcelSerializer(
|
||||
override val asmType: Type,
|
||||
private val type: KotlinType,
|
||||
private val typeMapper: KotlinTypeMapper
|
||||
) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.pop2()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.pop()
|
||||
putObjectOrClassInstanceOnStack(type, asmType, typeMapper, v)
|
||||
}
|
||||
}
|
||||
|
||||
class ZeroParameterClassSerializer(
|
||||
override val asmType: Type,
|
||||
type: KotlinType
|
||||
) : ParcelSerializer {
|
||||
private val clazz = type.constructor.declarationDescriptor as ClassDescriptor
|
||||
|
||||
init {
|
||||
assert(clazz.kind == ClassKind.CLASS)
|
||||
}
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.pop2()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.pop()
|
||||
|
||||
val constructor = clazz.unsubstitutedPrimaryConstructor
|
||||
assert(constructor == null || constructor.valueParameters.isEmpty())
|
||||
v.anew(asmType)
|
||||
v.dup()
|
||||
v.invokespecial(asmType.internalName, "<init>", "()V", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun putObjectOrClassInstanceOnStack(type: KotlinType, asmType: Type, typeMapper: KotlinTypeMapper, v: InstructionAdapter) {
|
||||
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
|
||||
if (clazz != null) {
|
||||
if (clazz.isCompanionObject) {
|
||||
val outerClass = clazz.containingDeclaration as? ClassDescriptor
|
||||
if (outerClass != null) {
|
||||
v.getstatic(typeMapper.mapType(outerClass.defaultType).internalName, clazz.name.asString(), asmType.descriptor)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v.getstatic(asmType.internalName, "INSTANCE", asmType.descriptor)
|
||||
}
|
||||
|
||||
internal class EnumParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual("java/lang/Enum", "name", "()Ljava/lang/String;", false)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeString", "(Ljava/lang/String;)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readString", "()Ljava/lang/String;", false)
|
||||
v.aconst(asmType)
|
||||
v.swap()
|
||||
v.invokestatic("java/lang/Enum", "valueOf", "(Ljava/lang/Class;Ljava/lang/String;)Ljava/lang/Enum;", false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class CharSequenceParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, seq
|
||||
v.swap() // -> seq, parcel
|
||||
v.aconst(0) // -> seq, parcel, flags
|
||||
v.invokestatic("android/text/TextUtils", "writeToParcel", "(Ljava/lang/CharSequence;Landroid/os/Parcel;I)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.getstatic("android/text/TextUtils", "CHAR_SEQUENCE_CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
|
||||
v.swap() // -> creator, parcel
|
||||
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class EfficientParcelableParcelSerializer(override val asmType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, parcelable
|
||||
v.swap() // -> parcelable, parcel
|
||||
v.aconst(0) // -> parcelable, parcel, flags
|
||||
v.invokeinterface("android/os/Parcelable", "writeToParcel", "(Landroid/os/Parcel;I)V")
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.getstatic(asmType.internalName, "CREATOR", "Landroid/os/Parcelable\$Creator;") // -> parcel, creator
|
||||
v.swap() // -> creator, parcel
|
||||
v.invokeinterface("android/os/Parcelable\$Creator", "createFromParcel", "(Landroid/os/Parcel;)Ljava/lang/Object;")
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class GenericParcelableParcelSerializer(override val asmType: Type, private val containerClassType: Type) : ParcelSerializer {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
// -> parcel, parcelable
|
||||
v.load(2, Type.INT_TYPE) // -> parcel, parcelable, flags
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeParcelable", "(Landroid/os/Parcelable;I)V", false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
v.aconst(containerClassType) // -> parcel, type
|
||||
v.invokevirtual("java/lang/Class", "getClassLoader", "()Ljava/lang/ClassLoader;", false) // -> parcel, classloader
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readParcelable", "(Ljava/lang/ClassLoader;)Landroid/os/Parcelable;", false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
class NullAwareParcelSerializerWrapper(private val delegate: ParcelSerializer) : ParcelSerializer {
|
||||
override val asmType: Type
|
||||
get() = delegate.asmType
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) = writeValueNullAware(v) {
|
||||
delegate.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) = readValueNullAware(v) {
|
||||
delegate.readValue(v)
|
||||
}
|
||||
}
|
||||
|
||||
internal class PrimitiveArrayParcelSerializer(
|
||||
override val asmType: Type
|
||||
) : ParcelSerializer {
|
||||
private val methodNameBase = when (asmType.elementType) {
|
||||
Type.INT_TYPE -> "Int"
|
||||
Type.BOOLEAN_TYPE -> "Boolean"
|
||||
Type.BYTE_TYPE -> "Byte"
|
||||
Type.CHAR_TYPE -> "Char"
|
||||
Type.DOUBLE_TYPE -> "Double"
|
||||
Type.FLOAT_TYPE -> "Float"
|
||||
Type.LONG_TYPE -> "Long"
|
||||
else -> error("Unsupported type ${asmType.elementType.descriptor}")
|
||||
}
|
||||
|
||||
private val writeMethod = Method("write${methodNameBase}Array", "(${asmType.descriptor})V")
|
||||
private val createArrayMethod = Method("create${methodNameBase}Array", "()${asmType.descriptor}")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, createArrayMethod.name, createArrayMethod.signature, false)
|
||||
}
|
||||
}
|
||||
|
||||
/** write...() and get...() methods in Android should support passing `null` values. */
|
||||
internal class NullCompliantObjectParcelSerializer(
|
||||
override val asmType: Type,
|
||||
writeMethod: Method<String?>,
|
||||
readMethod: Method<String?>
|
||||
) : ParcelSerializer {
|
||||
private val writeMethod = Method(writeMethod.name, writeMethod.signature ?: "(${asmType.descriptor})V")
|
||||
private val readMethod = Method(readMethod.name, readMethod.signature ?: "()${asmType.descriptor}")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, readMethod.name, readMethod.signature, false)
|
||||
v.castIfNeeded(asmType)
|
||||
}
|
||||
}
|
||||
|
||||
internal class BoxedPrimitiveTypeParcelSerializer private constructor(private val unboxedType: Type) : ParcelSerializer {
|
||||
companion object {
|
||||
private val BOXED_TO_UNBOXED_TYPE_MAPPINGS = mapOf(
|
||||
"java/lang/Boolean" to Type.BOOLEAN_TYPE,
|
||||
"java/lang/Character" to Type.CHAR_TYPE,
|
||||
"java/lang/Byte" to Type.BYTE_TYPE,
|
||||
"java/lang/Short" to Type.SHORT_TYPE,
|
||||
"java/lang/Integer" to Type.INT_TYPE,
|
||||
"java/lang/Float" to Type.FLOAT_TYPE,
|
||||
"java/lang/Long" to Type.LONG_TYPE,
|
||||
"java/lang/Double" to Type.DOUBLE_TYPE
|
||||
)
|
||||
|
||||
private val UNBOXED_TO_BOXED_TYPE_MAPPINGS = BOXED_TO_UNBOXED_TYPE_MAPPINGS.map { it.value to it.key }.toMap()
|
||||
|
||||
internal val BOXED_VALUE_METHOD_NAMES = mapOf(
|
||||
"java/lang/Boolean" to "booleanValue",
|
||||
"java/lang/Character" to "charValue",
|
||||
"java/lang/Byte" to "byteValue",
|
||||
"java/lang/Short" to "shortValue",
|
||||
"java/lang/Integer" to "intValue",
|
||||
"java/lang/Float" to "floatValue",
|
||||
"java/lang/Long" to "longValue",
|
||||
"java/lang/Double" to "doubleValue"
|
||||
)
|
||||
|
||||
private val INSTANCES = BOXED_TO_UNBOXED_TYPE_MAPPINGS.values.map { type ->
|
||||
type to BoxedPrimitiveTypeParcelSerializer(type)
|
||||
}.toMap()
|
||||
|
||||
fun forUnboxedType(type: Type) = INSTANCES[type] ?: error("Unsupported type $type")
|
||||
fun forBoxedType(type: Type) = INSTANCES[BOXED_TO_UNBOXED_TYPE_MAPPINGS[type.internalName]] ?: error("Unsupported type $type")
|
||||
}
|
||||
|
||||
override val asmType: Type = Type.getObjectType(UNBOXED_TO_BOXED_TYPE_MAPPINGS[unboxedType] ?: error("Unsupported type $unboxedType"))
|
||||
|
||||
private val unboxedSerializer = PrimitiveTypeParcelSerializer.getInstance(unboxedType)
|
||||
private val typeValueMethodName = BOXED_VALUE_METHOD_NAMES[asmType.internalName] ?: error("Boxed method name not found for $asmType")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(asmType.internalName, typeValueMethodName, "()${unboxedType.descriptor}", false)
|
||||
unboxedSerializer.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
unboxedSerializer.readValue(v)
|
||||
v.invokestatic(asmType.internalName, "valueOf", "(${unboxedType.descriptor})${asmType.descriptor}", false)
|
||||
}
|
||||
}
|
||||
|
||||
internal open class PrimitiveTypeParcelSerializer private constructor(final override val asmType: Type) : ParcelSerializer {
|
||||
companion object {
|
||||
private val WRITE_METHOD_NAMES = mapOf(
|
||||
Type.BOOLEAN_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.CHAR_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.BYTE_TYPE to Method("writeByte", "(B)V"),
|
||||
Type.SHORT_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.INT_TYPE to Method("writeInt", "(I)V"),
|
||||
Type.FLOAT_TYPE to Method("writeFloat", "(F)V"),
|
||||
Type.LONG_TYPE to Method("writeLong", "(J)V"),
|
||||
Type.DOUBLE_TYPE to Method("writeDouble", "(D)V")
|
||||
)
|
||||
|
||||
private val READ_METHOD_NAMES = mapOf(
|
||||
Type.BOOLEAN_TYPE to Method("readInt", "()I"),
|
||||
Type.CHAR_TYPE to Method("readInt", "()I"),
|
||||
Type.BYTE_TYPE to Method("readByte", "()B"),
|
||||
Type.SHORT_TYPE to Method("readInt", "()I"),
|
||||
Type.INT_TYPE to Method("readInt", "()I"),
|
||||
Type.FLOAT_TYPE to Method("readFloat", "()F"),
|
||||
Type.LONG_TYPE to Method("readLong", "()J"),
|
||||
Type.DOUBLE_TYPE to Method("readDouble", "()D")
|
||||
)
|
||||
|
||||
private val INSTANCES = READ_METHOD_NAMES.keys.map {
|
||||
it to when (it) {
|
||||
Type.CHAR_TYPE -> CharParcelSerializer
|
||||
Type.SHORT_TYPE -> ShortParcelSerializer
|
||||
Type.BOOLEAN_TYPE -> BooleanParcelSerializer
|
||||
else -> PrimitiveTypeParcelSerializer(it)
|
||||
}
|
||||
}.toMap()
|
||||
|
||||
fun getInstance(type: Type) = INSTANCES[type] ?: error("Unsupported type ${type.descriptor}")
|
||||
}
|
||||
|
||||
object CharParcelSerializer : PrimitiveTypeParcelSerializer(Type.CHAR_TYPE) {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.cast(Type.CHAR_TYPE, Type.INT_TYPE)
|
||||
super.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
super.readValue(v)
|
||||
v.cast(Type.INT_TYPE, Type.CHAR_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
object ShortParcelSerializer : PrimitiveTypeParcelSerializer(Type.SHORT_TYPE) {
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.cast(Type.SHORT_TYPE, Type.INT_TYPE)
|
||||
super.writeValue(v)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
super.readValue(v)
|
||||
v.cast(Type.INT_TYPE, Type.SHORT_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
object BooleanParcelSerializer : PrimitiveTypeParcelSerializer(Type.BOOLEAN_TYPE) {
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
super.readValue(v)
|
||||
|
||||
val falseLabel = Label()
|
||||
val conditionIsOver = Label()
|
||||
|
||||
v.ifeq(falseLabel)
|
||||
v.iconst(1)
|
||||
v.goTo(conditionIsOver)
|
||||
|
||||
v.visitLabel(falseLabel)
|
||||
v.iconst(0)
|
||||
|
||||
v.visitLabel(conditionIsOver)
|
||||
}
|
||||
}
|
||||
|
||||
private val writeMethod = WRITE_METHOD_NAMES[asmType] ?: error("Write method not found for $asmType")
|
||||
private val readMethod = READ_METHOD_NAMES[asmType] ?: error("Read method not found for $asmType")
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, writeMethod.name, writeMethod.signature, false)
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, readMethod.name, readMethod.signature, false)
|
||||
}
|
||||
}
|
||||
|
||||
internal class ParcelSerializerStub(override val asmType: Type, private val kotlinType: KotlinType) : ParcelSerializer {
|
||||
private fun throwError() {
|
||||
TODO("Type is only supported in the IR backend: ${DescriptorRenderer.SHORT_NAMES_IN_TYPES.renderType(kotlinType)}")
|
||||
}
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
throwError()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
throwError()
|
||||
}
|
||||
}
|
||||
|
||||
private fun readValueNullAware(v: InstructionAdapter, block: () -> Unit) {
|
||||
val labelNull = Label()
|
||||
val labelReturn = Label()
|
||||
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "readInt", "()I", false)
|
||||
v.ifeq(labelNull)
|
||||
|
||||
v.load(1, PARCEL_TYPE)
|
||||
block()
|
||||
v.goTo(labelReturn)
|
||||
|
||||
// Just push null on stack if the value is null
|
||||
v.visitLabel(labelNull)
|
||||
v.aconst(null)
|
||||
|
||||
v.visitLabel(labelReturn)
|
||||
}
|
||||
|
||||
private fun writeValueNullAware(v: InstructionAdapter, block: () -> Unit) {
|
||||
val labelReturn = Label()
|
||||
val labelNull = Label()
|
||||
v.dup()
|
||||
v.ifnull(labelNull)
|
||||
|
||||
// Write 1 if non-null, 0 if null
|
||||
|
||||
v.load(1, PARCEL_TYPE)
|
||||
v.aconst(1)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
|
||||
block()
|
||||
|
||||
v.goTo(labelReturn)
|
||||
|
||||
v.visitLabel(labelNull)
|
||||
v.pop()
|
||||
v.aconst(0)
|
||||
v.invokevirtual(PARCEL_TYPE.internalName, "writeInt", "(I)V", false)
|
||||
|
||||
v.visitLabel(labelReturn)
|
||||
}
|
||||
|
||||
internal class Method<out T : String?>(val name: String, val signature: T) {
|
||||
companion object {
|
||||
operator fun invoke(name: String) = Method(name, null)
|
||||
}
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* 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.kotlin.parcelize.serializers
|
||||
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
|
||||
import org.jetbrains.kotlin.parcelize.isParcelize
|
||||
import java.io.FileDescriptor
|
||||
|
||||
interface ParcelizeExtensionBase {
|
||||
companion object {
|
||||
val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
||||
val ALLOWED_CLASS_KINDS = listOf(ClassKind.CLASS, ClassKind.OBJECT, ClassKind.ENUM_CLASS)
|
||||
}
|
||||
|
||||
fun ClassDescriptor.hasCreatorField(): Boolean {
|
||||
val companionObject = companionObjectDescriptor ?: return false
|
||||
|
||||
if (companionObject.name == CREATOR_NAME) {
|
||||
return true
|
||||
}
|
||||
|
||||
return companionObject.unsubstitutedMemberScope
|
||||
.getContributedVariables(CREATOR_NAME, NoLookupLocation.FROM_BACKEND)
|
||||
.isNotEmpty()
|
||||
}
|
||||
|
||||
val ClassDescriptor.isParcelizeClassDescriptor get() = kind in ALLOWED_CLASS_KINDS && isParcelize
|
||||
|
||||
fun ClassDescriptor.hasSyntheticDescribeContents() = hasParcelizeSyntheticMethod(DESCRIBE_CONTENTS)
|
||||
|
||||
fun ClassDescriptor.hasSyntheticWriteToParcel() = hasParcelizeSyntheticMethod(WRITE_TO_PARCEL)
|
||||
|
||||
fun ClassDescriptor.findFunction(componentKind: ParcelizeSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
|
||||
return unsubstitutedMemberScope
|
||||
.getContributedFunctions(Name.identifier(componentKind.methodName), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull { (it as? ParcelizeSyntheticComponent)?.componentKind == componentKind }
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.hasParcelizeSyntheticMethod(componentKind: ParcelizeSyntheticComponent.ComponentKind): Boolean {
|
||||
val methodName = Name.identifier(componentKind.methodName)
|
||||
|
||||
val writeToParcelMethods = unsubstitutedMemberScope
|
||||
.getContributedFunctions(methodName, NoLookupLocation.FROM_BACKEND)
|
||||
.filter { it is ParcelizeSyntheticComponent && it.componentKind == componentKind }
|
||||
|
||||
return writeToParcelMethods.size == 1
|
||||
}
|
||||
}
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* 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.kotlin.parcelize.serializers
|
||||
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
|
||||
typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
|
||||
|
||||
fun KotlinType.isParcelable() = matchesFqNameWithSupertypes(ParcelizeNames.PARCELABLE_FQN.asString())
|
||||
|
||||
fun KotlinType.matchesFqNameWithSupertypes(fqName: String): Boolean {
|
||||
if (this.matchesFqName(fqName)) {
|
||||
return true
|
||||
}
|
||||
|
||||
return TypeUtils.getAllSupertypes(this).any { it.matchesFqName(fqName) }
|
||||
}
|
||||
|
||||
fun KotlinType.matchesFqName(fqName: String): Boolean {
|
||||
return this.constructor.declarationDescriptor?.fqNameSafe?.asString() == fqName
|
||||
}
|
||||
Reference in New Issue
Block a user