[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,30 @@
|
||||
description = "Parcelize compiler plugin (Backend)"
|
||||
|
||||
plugins {
|
||||
kotlin("jvm")
|
||||
id("jps-compatible")
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation(project(":plugins:parcelize:parcelize-compiler:parcelize.common"))
|
||||
implementation(project(":plugins:parcelize:parcelize-compiler:parcelize.k1"))
|
||||
implementation(project(":plugins:parcelize:parcelize-compiler:parcelize.k2"))
|
||||
|
||||
compileOnly(project(":compiler:backend"))
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(project(":compiler:backend.jvm"))
|
||||
compileOnly(project(":compiler:ir.tree.impl"))
|
||||
compileOnly(project(":compiler:fir:tree"))
|
||||
compileOnly(project(":compiler:fir:fir2ir"))
|
||||
compileOnly(intellijCore())
|
||||
compileOnly(commonDependency("org.jetbrains.intellij.deps:asm-all"))
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
"main" { projectDefault() }
|
||||
"test" { none() }
|
||||
}
|
||||
|
||||
runtimeJar()
|
||||
javadocJar()
|
||||
sourcesJar()
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2010-2022 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.parcelize
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.codegen.AbstractClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.ClassBuilderFactory
|
||||
import org.jetbrains.kotlin.codegen.DelegatingClassBuilder
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.diagnostics.DiagnosticSink
|
||||
import org.jetbrains.kotlin.psi.KtClassOrObject
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.org.objectweb.asm.MethodVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes
|
||||
import org.jetbrains.org.objectweb.asm.RecordComponentVisitor
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
|
||||
class ParcelizeClinitClassBuilderInterceptorExtension : ClassBuilderInterceptorExtension {
|
||||
override fun interceptClassBuilderFactory(
|
||||
interceptedFactory: ClassBuilderFactory,
|
||||
bindingContext: BindingContext,
|
||||
diagnostics: DiagnosticSink
|
||||
): ClassBuilderFactory {
|
||||
return ParcelableClinitClassBuilderFactory(interceptedFactory, bindingContext)
|
||||
}
|
||||
|
||||
private inner class ParcelableClinitClassBuilderFactory(
|
||||
private val delegateFactory: ClassBuilderFactory,
|
||||
val bindingContext: BindingContext
|
||||
) : ClassBuilderFactory {
|
||||
|
||||
override fun newClassBuilder(origin: JvmDeclarationOrigin): ClassBuilder {
|
||||
return AndroidOnDestroyCollectorClassBuilder(origin, delegateFactory.newClassBuilder(origin), bindingContext)
|
||||
}
|
||||
|
||||
override fun getClassBuilderMode() = delegateFactory.classBuilderMode
|
||||
|
||||
override fun asText(builder: ClassBuilder?): String? {
|
||||
return delegateFactory.asText((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun asBytes(builder: ClassBuilder?): ByteArray? {
|
||||
return delegateFactory.asBytes((builder as AndroidOnDestroyCollectorClassBuilder).delegateClassBuilder)
|
||||
}
|
||||
|
||||
override fun close() {
|
||||
delegateFactory.close()
|
||||
}
|
||||
}
|
||||
|
||||
private class AndroidOnDestroyCollectorClassBuilder(
|
||||
val declarationOrigin: JvmDeclarationOrigin,
|
||||
val delegateClassBuilder: ClassBuilder,
|
||||
val bindingContext: BindingContext
|
||||
) : DelegatingClassBuilder() {
|
||||
private var currentClass: KtClassOrObject? = null
|
||||
private var currentClassName: String? = null
|
||||
private var isClinitGenerated = false
|
||||
|
||||
override fun getDelegate() = delegateClassBuilder
|
||||
|
||||
override fun defineClass(
|
||||
origin: PsiElement?,
|
||||
version: Int,
|
||||
access: Int,
|
||||
name: String,
|
||||
signature: String?,
|
||||
superName: String,
|
||||
interfaces: Array<out String>
|
||||
) {
|
||||
currentClass = origin as? KtClassOrObject
|
||||
currentClassName = name
|
||||
isClinitGenerated = false
|
||||
|
||||
super.defineClass(origin, version, access, name, signature, superName, interfaces)
|
||||
}
|
||||
|
||||
override fun done() {
|
||||
if (!isClinitGenerated && currentClass != null && currentClassName != null) {
|
||||
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
|
||||
if (descriptor != null && declarationOrigin.descriptor == descriptor && descriptor.isParcelize) {
|
||||
val baseVisitor = super.newMethod(JvmDeclarationOrigin.NO_ORIGIN, Opcodes.ACC_STATIC, "<clinit>", "()V", null, null)
|
||||
val visitor = ClinitAwareMethodVisitor(currentClassName!!, baseVisitor)
|
||||
|
||||
visitor.visitCode()
|
||||
visitor.visitInsn(Opcodes.RETURN)
|
||||
visitor.visitEnd()
|
||||
}
|
||||
}
|
||||
|
||||
super.done()
|
||||
}
|
||||
|
||||
override fun newMethod(
|
||||
origin: JvmDeclarationOrigin,
|
||||
access: Int,
|
||||
name: String,
|
||||
desc: String,
|
||||
signature: String?,
|
||||
exceptions: Array<out String>?
|
||||
): MethodVisitor {
|
||||
if (name == "<clinit>" && currentClass != null && currentClassName != null) {
|
||||
isClinitGenerated = true
|
||||
|
||||
val descriptor = bindingContext[BindingContext.CLASS, currentClass]
|
||||
if (descriptor != null && declarationOrigin.descriptor == descriptor && descriptor.isParcelize) {
|
||||
return ClinitAwareMethodVisitor(
|
||||
currentClassName!!,
|
||||
super.newMethod(origin, access, name, desc, signature, exceptions)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
return super.newMethod(origin, access, name, desc, signature, exceptions)
|
||||
}
|
||||
|
||||
override fun newRecordComponent(name: String, desc: String, signature: String?): RecordComponentVisitor {
|
||||
return AbstractClassBuilder.EMPTY_RECORD_VISITOR
|
||||
}
|
||||
}
|
||||
|
||||
private class ClinitAwareMethodVisitor(val parcelableName: String, mv: MethodVisitor) : MethodVisitor(Opcodes.API_VERSION, mv) {
|
||||
override fun visitInsn(opcode: Int) {
|
||||
if (opcode == Opcodes.RETURN) {
|
||||
val iv = InstructionAdapter(this)
|
||||
val creatorName = "$parcelableName\$Creator"
|
||||
val creatorType = Type.getObjectType(creatorName)
|
||||
|
||||
iv.anew(creatorType)
|
||||
iv.dup()
|
||||
iv.invokespecial(creatorName, "<init>", "()V", false)
|
||||
iv.putstatic(parcelableName, "CREATOR", "Landroid/os/Parcelable\$Creator;")
|
||||
}
|
||||
|
||||
super.visitInsn(opcode)
|
||||
}
|
||||
}
|
||||
}
|
||||
+426
@@ -0,0 +1,426 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.FunctionGenerationStrategy.CodegenBased
|
||||
import org.jetbrains.kotlin.codegen.context.ClassContext
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.descriptors.*
|
||||
import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeResolveExtension.Companion.createMethod
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.*
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase.Companion.FILE_DESCRIPTOR_FQNAME
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.DescriptorFactory
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.builtIns
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
|
||||
import org.jetbrains.kotlin.resolve.descriptorUtil.module
|
||||
import org.jetbrains.kotlin.resolve.inline.InlineUtil
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOrigin
|
||||
import org.jetbrains.kotlin.resolve.jvm.diagnostics.JvmDeclarationOriginKind
|
||||
import org.jetbrains.kotlin.resolve.jvm.jvmSignature.JvmMethodSignature
|
||||
import org.jetbrains.kotlin.resolve.scopes.MemberScope
|
||||
import org.jetbrains.kotlin.storage.LockBasedStorageManager
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.kotlin.types.TypeUtils
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.types.typeUtil.replaceArgumentsWithStarProjections
|
||||
import org.jetbrains.org.objectweb.asm.Opcodes.*
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
|
||||
open class ParcelizeCodegenExtension : ParcelizeExtensionBase, ExpressionCodegenExtension {
|
||||
open fun isAvailable(element: PsiElement): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override val shouldGenerateClassSyntheticPartsInLightClassesMode: Boolean
|
||||
get() = true
|
||||
|
||||
override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) {
|
||||
val declaration = codegen.myClass as? PsiElement ?: return
|
||||
if (!isAvailable(declaration)) {
|
||||
return
|
||||
}
|
||||
|
||||
val parcelableClass = codegen.descriptor
|
||||
if (!parcelableClass.isParcelizeClassDescriptor) {
|
||||
return
|
||||
}
|
||||
|
||||
val propertiesToSerialize = getPropertiesToSerialize(codegen, parcelableClass)
|
||||
|
||||
val parcelerObject = parcelableClass.companionObjectDescriptor?.takeIf {
|
||||
TypeUtils.getAllSupertypes(it.defaultType).any { supertype -> supertype.isParceler }
|
||||
}
|
||||
|
||||
with(parcelableClass) {
|
||||
if (hasSyntheticDescribeContents()) {
|
||||
writeDescribeContentsFunction(codegen, propertiesToSerialize)
|
||||
}
|
||||
|
||||
if (hasSyntheticWriteToParcel()) {
|
||||
writeWriteToParcel(codegen, propertiesToSerialize, PARCEL_TYPE, parcelerObject)
|
||||
}
|
||||
|
||||
if (!hasCreatorField()) {
|
||||
writeCreatorAccessField(codegen, parcelableClass)
|
||||
}
|
||||
}
|
||||
|
||||
if (codegen.state.classBuilderMode != ClassBuilderMode.LIGHT_CLASSES) {
|
||||
val parcelClassType = ParcelizeResolveExtension.resolveParcelClassType(parcelableClass.module)
|
||||
?: error("Can't resolve 'android.os.Parcel' class")
|
||||
|
||||
val parcelableCreatorClassType = ParcelizeResolveExtension.resolveParcelableCreatorClassType(parcelableClass.module)
|
||||
?: error("Can't resolve 'android.os.Parcelable.Creator' class")
|
||||
|
||||
writeCreatorClass(
|
||||
codegen,
|
||||
parcelableClass, parcelClassType, parcelableCreatorClassType,
|
||||
PARCEL_TYPE, parcelerObject, propertiesToSerialize
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCompanionClassType(containerAsmType: Type, parcelerObject: ClassDescriptor): Pair<Type, String> {
|
||||
val shortName = parcelerObject.name
|
||||
return Pair(Type.getObjectType(containerAsmType.internalName + "\$$shortName"), shortName.asString())
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.writeWriteToParcel(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
properties: List<PropertyToSerialize>,
|
||||
parcelAsmType: Type,
|
||||
parcelerObject: ClassDescriptor?
|
||||
): Unit? {
|
||||
val containerAsmType = codegen.typeMapper.mapType(this.defaultType)
|
||||
|
||||
return findFunction(WRITE_TO_PARCEL)?.write(codegen) {
|
||||
if (parcelerObject != null) {
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
|
||||
v.load(0, containerAsmType)
|
||||
v.load(1, PARCEL_TYPE)
|
||||
v.load(2, Type.INT_TYPE)
|
||||
v.invokevirtual(
|
||||
companionAsmType.internalName, "write",
|
||||
"(${containerAsmType.descriptor}${PARCEL_TYPE.descriptor}I)V", false
|
||||
)
|
||||
} else {
|
||||
val frameMap = FrameMap().apply {
|
||||
enterTemp(containerAsmType)
|
||||
enterTemp(PARCEL_TYPE)
|
||||
enterTemp(Type.INT_TYPE)
|
||||
}
|
||||
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
val entityType = this@writeWriteToParcel.defaultType
|
||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
||||
val serializer = if (this@writeWriteToParcel.kind == ClassKind.CLASS) {
|
||||
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
|
||||
} else {
|
||||
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
}
|
||||
|
||||
v.load(1, parcelAsmType)
|
||||
v.load(0, containerAsmType)
|
||||
serializer.writeValue(v)
|
||||
} else {
|
||||
for ((fieldName, type, parcelers) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
|
||||
v.load(1, parcelAsmType)
|
||||
v.load(0, containerAsmType)
|
||||
v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor)
|
||||
|
||||
val serializer = ParcelSerializer.get(type, asmType, globalContext.copy(typeParcelers = parcelers))
|
||||
serializer.writeValue(v)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
v.areturn(Type.VOID_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ClassDescriptor.writeDescribeContentsFunction(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
propertiesToSerialize: List<PropertyToSerialize>
|
||||
): Unit? {
|
||||
val hasFileDescriptorAnywhere = propertiesToSerialize.any { it.type.containsFileDescriptor() }
|
||||
|
||||
return findFunction(DESCRIBE_CONTENTS)?.write(codegen) {
|
||||
v.aconst(if (hasFileDescriptorAnywhere) 1 /* CONTENTS_FILE_DESCRIPTOR */ else 0)
|
||||
v.areturn(Type.INT_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun KotlinType.containsFileDescriptor(): Boolean {
|
||||
val declarationDescriptor = this.constructor.declarationDescriptor
|
||||
if (declarationDescriptor != null) {
|
||||
if (declarationDescriptor.fqNameSafe == FILE_DESCRIPTOR_FQNAME) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return this.arguments.any { it.type.containsFileDescriptor() }
|
||||
}
|
||||
|
||||
data class PropertyToSerialize(val name: String, val type: KotlinType, val parcelers: List<TypeParcelerMapping>)
|
||||
|
||||
private fun getPropertiesToSerialize(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor
|
||||
): List<PropertyToSerialize> {
|
||||
if (parcelableClass.kind != ClassKind.CLASS) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
|
||||
|
||||
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
|
||||
codegen.bindingContext[BindingContext.VALUE_PARAMETER_AS_PROPERTY, param]
|
||||
}
|
||||
|
||||
val classParcelers = getTypeParcelers(parcelableClass.annotations)
|
||||
|
||||
return propertiesToSerialize.map {
|
||||
PropertyToSerialize(it.name.asString(), it.type, classParcelers + getTypeParcelers(it.annotations))
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeCreateFromParcel(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
parcelableCreatorClassType: KotlinType,
|
||||
creatorClass: ClassDescriptorImpl,
|
||||
parcelClassType: KotlinType,
|
||||
parcelAsmType: Type,
|
||||
parcelerObject: ClassDescriptor?,
|
||||
properties: List<PropertyToSerialize>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
val creatorAsmType = codegen.typeMapper.mapType(creatorClass)
|
||||
|
||||
val overriddenFunction = parcelableCreatorClassType.findFunction(CREATE_FROM_PARCEL)
|
||||
?: error("Can't resolve 'android.os.Parcelable.Creator.${CREATE_FROM_PARCEL.methodName}' method")
|
||||
|
||||
createMethod(
|
||||
creatorClass, CREATE_FROM_PARCEL, Modality.FINAL,
|
||||
parcelableClass.defaultType.replaceArgumentsWithStarProjections(), "in" to parcelClassType
|
||||
).write(codegen, overriddenFunction) {
|
||||
if (parcelerObject != null) {
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
|
||||
v.load(1, PARCEL_TYPE)
|
||||
v.invokevirtual(companionAsmType.internalName, "create", "(${PARCEL_TYPE.descriptor})$containerAsmType", false)
|
||||
} else {
|
||||
v.anew(containerAsmType)
|
||||
v.dup()
|
||||
|
||||
val asmConstructorParameters = StringBuilder()
|
||||
val frameMap = FrameMap().apply {
|
||||
enterTemp(creatorAsmType)
|
||||
enterTemp(PARCEL_TYPE)
|
||||
}
|
||||
|
||||
val globalContext = ParcelSerializer.ParcelSerializerContext(codegen.typeMapper, containerAsmType, emptyList(), frameMap)
|
||||
|
||||
if (properties.isEmpty()) {
|
||||
val entityType = parcelableClass.defaultType
|
||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
||||
val serializer = if (parcelableClass.kind == ClassKind.CLASS) {
|
||||
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
|
||||
} else {
|
||||
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
}
|
||||
v.load(1, parcelAsmType)
|
||||
serializer.readValue(v)
|
||||
} else {
|
||||
for ((_, type, parcelers) in properties) {
|
||||
val asmType = codegen.typeMapper.mapType(type)
|
||||
asmConstructorParameters.append(asmType.descriptor)
|
||||
|
||||
val serializer = ParcelSerializer.get(type, asmType, globalContext.copy(typeParcelers = parcelers))
|
||||
v.load(1, parcelAsmType)
|
||||
serializer.readValue(v)
|
||||
}
|
||||
|
||||
v.invokespecial(containerAsmType.internalName, "<init>", "($asmConstructorParameters)V", false)
|
||||
}
|
||||
}
|
||||
|
||||
v.areturn(containerAsmType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeCreatorAccessField(codegen: ImplementationBodyCodegen, parcelableClass: ClassDescriptor) {
|
||||
val creatorType = Type.getObjectType("android/os/Parcelable\$Creator")
|
||||
val parcelableType = codegen.typeMapper.mapType(parcelableClass)
|
||||
val fieldSignature = "L${creatorType.internalName}<${parcelableType.descriptor}>;"
|
||||
|
||||
codegen.v.newField(
|
||||
JvmDeclarationOrigin.NO_ORIGIN,
|
||||
ACC_STATIC or ACC_PUBLIC or ACC_FINAL,
|
||||
"CREATOR",
|
||||
creatorType.descriptor,
|
||||
fieldSignature,
|
||||
null
|
||||
)
|
||||
}
|
||||
|
||||
private fun writeCreatorClass(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
parcelClassType: KotlinType,
|
||||
parcelableCreatorClassType: KotlinType,
|
||||
parcelAsmType: Type,
|
||||
parcelerObject: ClassDescriptor?,
|
||||
properties: List<PropertyToSerialize>
|
||||
) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val creatorAsmType = Type.getObjectType(containerAsmType.internalName + "\$Creator")
|
||||
|
||||
val creatorClass = ClassDescriptorImpl(
|
||||
parcelableClass, Name.identifier("Creator"), Modality.FINAL, ClassKind.CLASS, listOf(parcelableCreatorClassType),
|
||||
parcelableClass.source, false, LockBasedStorageManager.NO_LOCKS
|
||||
)
|
||||
|
||||
creatorClass.initialize(
|
||||
MemberScope.Empty, emptySet(),
|
||||
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source)
|
||||
)
|
||||
|
||||
val classBuilderForCreator = codegen.state.factory.newVisitor(
|
||||
JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, creatorClass),
|
||||
Type.getObjectType(creatorAsmType.internalName),
|
||||
codegen.myClass.containingKtFile
|
||||
)
|
||||
|
||||
val classContextForCreator = ClassContext(
|
||||
codegen.typeMapper, creatorClass, OwnerKind.IMPLEMENTATION, codegen.context.parentContext, null
|
||||
)
|
||||
val codegenForCreator = ImplementationBodyCodegen(
|
||||
codegen.myClass, classContextForCreator, classBuilderForCreator, codegen.state, codegen.parentCodegen, false
|
||||
)
|
||||
|
||||
val classSignature = "Ljava/lang/Object;Landroid/os/Parcelable\$Creator<${containerAsmType.descriptor}>;"
|
||||
classBuilderForCreator.defineClass(
|
||||
null, V1_6, ACC_PUBLIC or ACC_FINAL or ACC_SUPER,
|
||||
creatorAsmType.internalName, classSignature, "java/lang/Object",
|
||||
arrayOf("android/os/Parcelable\$Creator")
|
||||
)
|
||||
|
||||
codegen.v.visitInnerClass(creatorAsmType.internalName, containerAsmType.internalName, "Creator", ACC_PUBLIC or ACC_STATIC)
|
||||
codegenForCreator.v.visitInnerClass(creatorAsmType.internalName, containerAsmType.internalName, "Creator", ACC_PUBLIC or ACC_STATIC)
|
||||
|
||||
writeSyntheticClassMetadata(classBuilderForCreator, codegen.state, InlineUtil.isInPublicInlineScope(parcelableClass))
|
||||
|
||||
writeCreatorConstructor(codegenForCreator, creatorClass, creatorAsmType)
|
||||
writeNewArrayMethod(codegenForCreator, parcelableClass, parcelableCreatorClassType, creatorClass, parcelerObject)
|
||||
|
||||
writeCreateFromParcel(
|
||||
codegenForCreator,
|
||||
parcelableClass, parcelableCreatorClassType, creatorClass, parcelClassType,
|
||||
parcelAsmType, parcelerObject, properties
|
||||
)
|
||||
|
||||
classBuilderForCreator.done()
|
||||
}
|
||||
|
||||
private fun writeCreatorConstructor(codegen: ImplementationBodyCodegen, creatorClass: ClassDescriptor, creatorAsmType: Type) {
|
||||
DescriptorFactory.createPrimaryConstructorForObject(creatorClass, creatorClass.source)
|
||||
.apply {
|
||||
returnType = creatorClass.defaultType
|
||||
}.write(codegen) {
|
||||
v.load(0, creatorAsmType)
|
||||
v.invokespecial("java/lang/Object", "<init>", "()V", false)
|
||||
v.areturn(Type.VOID_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun writeNewArrayMethod(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor,
|
||||
parcelableCreatorClassType: KotlinType,
|
||||
creatorClass: ClassDescriptorImpl,
|
||||
parcelerObject: ClassDescriptor?
|
||||
) {
|
||||
val builtIns = parcelableClass.builtIns
|
||||
val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass)
|
||||
|
||||
val overriddenFunction = parcelableCreatorClassType.findFunction(NEW_ARRAY)
|
||||
?: error("Can't resolve 'android.os.Parcelable.Creator.${NEW_ARRAY.methodName}' method")
|
||||
|
||||
createMethod(
|
||||
creatorClass, NEW_ARRAY, Modality.FINAL,
|
||||
builtIns.getArrayType(Variance.INVARIANT, parcelableClass.defaultType.replaceArgumentsWithStarProjections()),
|
||||
"size" to builtIns.intType
|
||||
).write(codegen, overriddenFunction) {
|
||||
if (parcelerObject != null) {
|
||||
val newArrayMethod = parcelerObject.unsubstitutedMemberScope
|
||||
.getContributedFunctions(NEW_ARRAY_NAME, NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull {
|
||||
it.typeParameters.isEmpty()
|
||||
&& it.kind == CallableMemberDescriptor.Kind.DECLARATION
|
||||
&& (it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type))
|
||||
&& !((it.containingDeclaration as? ClassDescriptor)?.defaultType?.isParceler ?: true)
|
||||
}
|
||||
|
||||
if (newArrayMethod != null) {
|
||||
val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType)
|
||||
val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject)
|
||||
|
||||
v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor)
|
||||
v.load(1, Type.INT_TYPE)
|
||||
v.invokevirtual(companionAsmType.internalName, "newArray", "(I)[$parcelableAsmType", false)
|
||||
v.areturn(Type.getType("[$parcelableAsmType"))
|
||||
|
||||
return@write
|
||||
}
|
||||
}
|
||||
|
||||
v.load(1, Type.INT_TYPE)
|
||||
v.newarray(parcelableAsmType)
|
||||
v.areturn(Type.getType("[$parcelableAsmType"))
|
||||
}
|
||||
}
|
||||
|
||||
private fun FunctionDescriptor.write(
|
||||
codegen: ImplementationBodyCodegen,
|
||||
overriddenDescriptor: FunctionDescriptor? = null,
|
||||
code: ExpressionCodegen.() -> Unit
|
||||
) {
|
||||
val declarationOrigin = JvmDeclarationOrigin(JvmDeclarationOriginKind.OTHER, null, this)
|
||||
if (overriddenDescriptor != null) {
|
||||
this.overriddenDescriptors = listOf(overriddenDescriptor)
|
||||
}
|
||||
|
||||
codegen.functionCodegen.generateMethod(declarationOrigin, this, object : CodegenBased(codegen.state) {
|
||||
override fun doGenerateBody(e: ExpressionCodegen, signature: JvmMethodSignature) = with(e) {
|
||||
e.code()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun KotlinType.findFunction(componentKind: ParcelizeSyntheticComponent.ComponentKind): SimpleFunctionDescriptor? {
|
||||
return memberScope
|
||||
.getContributedFunctions(Name.identifier(componentKind.methodName), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS)
|
||||
.firstOrNull()
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.parcelize.ir.AndroidSymbols
|
||||
import org.jetbrains.kotlin.parcelize.ir.ParcelizeFirIrTransformer
|
||||
|
||||
class ParcelizeFirIrGeneratorExtension : IrGenerationExtension {
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, moduleFragment)
|
||||
ParcelizeFirIrTransformer(pluginContext, androidSymbols).transform(moduleFragment)
|
||||
}
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.parcelize.ir.AndroidSymbols
|
||||
import org.jetbrains.kotlin.parcelize.ir.ParcelizeIrTransformer
|
||||
|
||||
class ParcelizeIrGeneratorExtension : IrGenerationExtension {
|
||||
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
|
||||
val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, moduleFragment)
|
||||
ParcelizeIrTransformer(pluginContext, androidSymbols).transform(moduleFragment)
|
||||
}
|
||||
}
|
||||
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
|
||||
// An IR builder with access to AndroidSymbols and convenience methods to build calls to some of these methods.
|
||||
class AndroidIrBuilder internal constructor(
|
||||
val androidSymbols: AndroidSymbols,
|
||||
symbol: IrSymbol,
|
||||
startOffset: Int,
|
||||
endOffset: Int
|
||||
) : IrBuilderWithScope(IrGeneratorContextBase(androidSymbols.irBuiltIns), Scope(symbol), startOffset, endOffset) {
|
||||
fun parcelReadInt(receiver: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadInt).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelReadParcelable(receiver: IrExpression, loader: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadParcelable).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, loader)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelReadString(receiver: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelReadString).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteInt(receiver: IrExpression, value: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteInt).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteParcelable(receiver: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteParcelable).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, p)
|
||||
putValueArgument(1, parcelableFlags)
|
||||
}
|
||||
}
|
||||
|
||||
fun parcelWriteString(receiver: IrExpression, value: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.parcelWriteString).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
|
||||
fun textUtilsWriteToParcel(cs: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.textUtilsWriteToParcel).apply {
|
||||
putValueArgument(0, cs)
|
||||
putValueArgument(1, p)
|
||||
putValueArgument(2, parcelableFlags)
|
||||
}
|
||||
}
|
||||
|
||||
fun classGetClassLoader(receiver: IrExpression): IrExpression {
|
||||
return irCall(androidSymbols.classGetClassLoader).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
}
|
||||
|
||||
fun getTextUtilsCharSequenceCreator(): IrExpression {
|
||||
return irGetField(null, androidSymbols.textUtilsCharSequenceCreator.owner)
|
||||
}
|
||||
|
||||
fun unsafeCoerce(value: IrExpression, fromType: IrType, toType: IrType): IrExpression {
|
||||
return IrCallImpl.fromSymbolOwner(UNDEFINED_OFFSET, UNDEFINED_OFFSET, toType, androidSymbols.unsafeCoerceIntrinsic).apply {
|
||||
putTypeArgument(0, fromType)
|
||||
putTypeArgument(1, toType)
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
+540
@@ -0,0 +1,540 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.InlineClassRepresentation
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.declarations.IrFactory
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrPackageFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.ir.util.defaultType
|
||||
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.NEW_ARRAY_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME
|
||||
|
||||
// All of the IR declarations needed by the parcelize plugin. Note that the declarations are generated based on JVM descriptors and
|
||||
// hence contain just enough information to produce correct JVM bytecode for *calls*. In particular, we omit generic types and
|
||||
// supertypes, which are not needed to produce correct bytecode.
|
||||
class AndroidSymbols(
|
||||
val irBuiltIns: IrBuiltIns,
|
||||
private val moduleFragment: IrModuleFragment
|
||||
) {
|
||||
private val irFactory: IrFactory = IrFactoryImpl
|
||||
|
||||
private val javaIo: IrPackageFragment = createPackage("java.io")
|
||||
private val javaLang: IrPackageFragment = createPackage("java.lang")
|
||||
private val javaUtil: IrPackageFragment = createPackage("java.util")
|
||||
|
||||
private val kotlin: IrPackageFragment = createPackage("kotlin")
|
||||
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
|
||||
private val kotlinJvmInternalPackage: IrPackageFragment = createPackage("kotlin.jvm.internal")
|
||||
|
||||
private val androidOs: IrPackageFragment = createPackage("android.os")
|
||||
private val androidUtil: IrPackageFragment = createPackage("android.util")
|
||||
private val androidText: IrPackageFragment = createPackage("android.text")
|
||||
|
||||
private val androidOsBundle: IrClassSymbol =
|
||||
createClass(androidOs, "Bundle", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidOsIBinder: IrClassSymbol =
|
||||
createClass(androidOs, "IBinder", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
val androidOsParcel: IrClassSymbol =
|
||||
createClass(androidOs, "Parcel", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidOsParcelFileDescriptor: IrClassSymbol =
|
||||
createClass(androidOs, "ParcelFileDescriptor", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val androidOsParcelable: IrClassSymbol =
|
||||
createClass(androidOs, "Parcelable", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
private val androidOsPersistableBundle: IrClassSymbol =
|
||||
createClass(androidOs, "PersistableBundle", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidTextTextUtils: IrClassSymbol =
|
||||
createClass(androidText, "TextUtils", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val androidUtilSize: IrClassSymbol =
|
||||
createClass(androidUtil, "Size", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidUtilSizeF: IrClassSymbol =
|
||||
createClass(androidUtil, "SizeF", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val androidUtilSparseBooleanArray: IrClassSymbol =
|
||||
createClass(androidUtil, "SparseBooleanArray", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaIoFileDescriptor: IrClassSymbol =
|
||||
createClass(javaIo, "FileDescriptor", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val javaIoSerializable: IrClassSymbol =
|
||||
createClass(javaIo, "Serializable", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
val javaLangClass: IrClassSymbol =
|
||||
createClass(javaLang, "Class", ClassKind.CLASS, Modality.FINAL)
|
||||
|
||||
private val javaLangClassLoader: IrClassSymbol =
|
||||
createClass(javaLang, "ClassLoader", ClassKind.CLASS, Modality.ABSTRACT)
|
||||
|
||||
private val javaUtilArrayList: IrClassSymbol =
|
||||
createClass(javaUtil, "ArrayList", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilLinkedHashMap: IrClassSymbol =
|
||||
createClass(javaUtil, "LinkedHashMap", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilLinkedHashSet: IrClassSymbol =
|
||||
createClass(javaUtil, "LinkedHashSet", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilList: IrClassSymbol =
|
||||
createClass(javaUtil, "List", ClassKind.INTERFACE, Modality.ABSTRACT)
|
||||
|
||||
private val javaUtilTreeMap: IrClassSymbol =
|
||||
createClass(javaUtil, "TreeMap", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
private val javaUtilTreeSet: IrClassSymbol =
|
||||
createClass(javaUtil, "TreeSet", ClassKind.CLASS, Modality.OPEN)
|
||||
|
||||
val kotlinUByte: IrClassSymbol =
|
||||
createClass(kotlin, "UByte", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.byteType as IrSimpleType)
|
||||
}
|
||||
|
||||
val kotlinUShort: IrClassSymbol =
|
||||
createClass(kotlin, "UShort", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.shortType as IrSimpleType)
|
||||
}
|
||||
|
||||
val kotlinUInt: IrClassSymbol =
|
||||
createClass(kotlin, "UInt", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.intType as IrSimpleType)
|
||||
}
|
||||
|
||||
val kotlinULong: IrClassSymbol =
|
||||
createClass(kotlin, "ULong", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.longType as IrSimpleType)
|
||||
}
|
||||
|
||||
val kotlinUByteArray: IrClassSymbol =
|
||||
createClass(kotlin, "UByteArray", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(
|
||||
Name.identifier("storage"),
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).owner.defaultType
|
||||
)
|
||||
}
|
||||
|
||||
val kotlinUShortArray: IrClassSymbol =
|
||||
createClass(kotlin, "UShortArray", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(
|
||||
Name.identifier("storage"),
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.shortType).owner.defaultType
|
||||
)
|
||||
}
|
||||
|
||||
val kotlinUIntArray: IrClassSymbol =
|
||||
createClass(kotlin, "UIntArray", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(
|
||||
Name.identifier("storage"),
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).owner.defaultType
|
||||
)
|
||||
}
|
||||
|
||||
val kotlinULongArray: IrClassSymbol =
|
||||
createClass(kotlin, "ULongArray", ClassKind.CLASS, Modality.FINAL, true).apply {
|
||||
owner.valueClassRepresentation = InlineClassRepresentation(
|
||||
Name.identifier("storage"),
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).owner.defaultType
|
||||
)
|
||||
}
|
||||
|
||||
val androidOsParcelableCreator: IrClassSymbol = irFactory.buildClass {
|
||||
name = Name.identifier("Creator")
|
||||
kind = ClassKind.INTERFACE
|
||||
modality = Modality.ABSTRACT
|
||||
}.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
val t = addTypeParameter("T", irBuiltIns.anyNType)
|
||||
parent = androidOsParcelable.owner
|
||||
|
||||
addFunction(CREATE_FROM_PARCEL_NAME.identifier, t.defaultType, Modality.ABSTRACT).apply {
|
||||
addValueParameter("source", androidOsParcel.defaultType)
|
||||
}
|
||||
|
||||
addFunction(
|
||||
NEW_ARRAY_NAME.identifier, irBuiltIns.arrayClass.typeWith(t.defaultType.makeNullable()),
|
||||
Modality.ABSTRACT
|
||||
).apply {
|
||||
addValueParameter("size", irBuiltIns.intType)
|
||||
}
|
||||
}.symbol
|
||||
|
||||
val kotlinKClassJava: IrPropertySymbol = irFactory.buildProperty {
|
||||
name = Name.identifier("java")
|
||||
}.apply {
|
||||
parent = kotlinJvm
|
||||
addGetter().apply {
|
||||
addExtensionReceiver(irBuiltIns.kClassClass.starProjectedType)
|
||||
returnType = javaLangClass.defaultType
|
||||
}
|
||||
}.symbol
|
||||
|
||||
val parcelCreateBinderArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createBinderArray", irBuiltIns.arrayClass.typeWith(androidOsIBinder.defaultType)).symbol
|
||||
|
||||
val parcelCreateBinderArrayList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createBinderArrayList", javaUtilArrayList.defaultType).symbol
|
||||
|
||||
val parcelCreateBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createBooleanArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.booleanType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateByteArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createByteArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateCharArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createCharArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.charType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateDoubleArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createDoubleArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.doubleType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateFloatArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createFloatArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.floatType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateIntArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createIntArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateLongArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction(
|
||||
"createLongArray",
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).defaultType
|
||||
).symbol
|
||||
|
||||
val parcelCreateStringArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createStringArray", irBuiltIns.arrayClass.typeWith(irBuiltIns.stringType)).symbol
|
||||
|
||||
val parcelCreateStringArrayList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("createStringArrayList", javaUtilArrayList.defaultType).symbol
|
||||
|
||||
val parcelReadBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readBundle", androidOsBundle.defaultType).apply {
|
||||
addValueParameter("loader", javaLangClassLoader.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelReadByte: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readByte", irBuiltIns.byteType).symbol
|
||||
|
||||
val parcelReadDouble: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readDouble", irBuiltIns.doubleType).symbol
|
||||
|
||||
val parcelReadFileDescriptor: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readFileDescriptor", androidOsParcelFileDescriptor.defaultType).symbol
|
||||
|
||||
val parcelReadFloat: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readFloat", irBuiltIns.floatType).symbol
|
||||
|
||||
val parcelReadInt: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readInt", irBuiltIns.intType).symbol
|
||||
|
||||
val parcelReadLong: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readLong", irBuiltIns.longType).symbol
|
||||
|
||||
val parcelReadParcelable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readParcelable", androidOsParcelable.defaultType).apply {
|
||||
addValueParameter("loader", javaLangClassLoader.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelReadPersistableBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readPersistableBundle", androidOsPersistableBundle.defaultType).apply {
|
||||
addValueParameter("loader", javaLangClassLoader.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelReadSerializable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSerializable", javaIoSerializable.defaultType).symbol
|
||||
|
||||
val parcelReadSize: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSize", androidUtilSize.defaultType).symbol
|
||||
|
||||
val parcelReadSizeF: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSizeF", androidUtilSizeF.defaultType).symbol
|
||||
|
||||
val parcelReadSparseBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readSparseBooleanArray", androidUtilSparseBooleanArray.defaultType).symbol
|
||||
|
||||
val parcelReadString: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readString", irBuiltIns.stringType).symbol
|
||||
|
||||
val parcelReadStrongBinder: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readStrongBinder", androidOsIBinder.defaultType).symbol
|
||||
|
||||
val parcelReadValue: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("readValue", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("loader", javaLangClassLoader.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBinderArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBinderArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.arrayClass.typeWith(androidOsIBinder.defaultType))
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBinderList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBinderList", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", javaUtilList.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBooleanArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.booleanType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeBundle", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidOsBundle.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteByte: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeByte", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.byteType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteByteArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeByteArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("b", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteCharArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeCharArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.charType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteDouble: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeDouble", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.doubleType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteDoubleArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeDoubleArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.doubleType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteFileDescriptor: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeFileDescriptor", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", javaIoFileDescriptor.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteFloat: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeFloat", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.floatType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteFloatArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeFloatArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.floatType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteInt: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeInt", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteIntArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeIntArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteLong: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeLong", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.longType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteLongArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeLongArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteParcelable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeParcelable", irBuiltIns.unitType).apply {
|
||||
addValueParameter("p", androidOsParcelable.defaultType)
|
||||
addValueParameter("parcelableFlags", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val parcelWritePersistableBundle: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writePersistableBundle", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidOsPersistableBundle.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSerializable: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSerializable", irBuiltIns.unitType).apply {
|
||||
addValueParameter("s", javaIoSerializable.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSize: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSize", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidUtilSize.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSizeF: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSizeF", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidUtilSizeF.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteSparseBooleanArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeSparseBooleanArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidUtilSparseBooleanArray.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteString: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeString", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.stringType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteStringArray: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeStringArray", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", irBuiltIns.arrayClass.typeWith(irBuiltIns.stringType))
|
||||
}.symbol
|
||||
|
||||
val parcelWriteStringList: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeStringList", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", javaUtilList.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteStrongBinder: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeStrongBinder", irBuiltIns.unitType).apply {
|
||||
addValueParameter("val", androidOsIBinder.defaultType)
|
||||
}.symbol
|
||||
|
||||
val parcelWriteValue: IrSimpleFunctionSymbol =
|
||||
androidOsParcel.owner.addFunction("writeValue", irBuiltIns.unitType).apply {
|
||||
addValueParameter("v", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val textUtilsWriteToParcel: IrSimpleFunctionSymbol =
|
||||
androidTextTextUtils.owner.addFunction(WRITE_TO_PARCEL_NAME.identifier, irBuiltIns.unitType, isStatic = true).apply {
|
||||
addValueParameter("cs", irBuiltIns.charSequenceClass.defaultType)
|
||||
addValueParameter("p", androidOsParcel.defaultType)
|
||||
addValueParameter("parcelableFlags", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val classGetClassLoader: IrSimpleFunctionSymbol =
|
||||
javaLangClass.owner.addFunction("getClassLoader", javaLangClassLoader.defaultType).symbol
|
||||
|
||||
val arrayListConstructor: IrConstructorSymbol = javaUtilArrayList.owner.addConstructor().apply {
|
||||
addValueParameter("p_0", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val arrayListAdd: IrSimpleFunctionSymbol =
|
||||
javaUtilArrayList.owner.addFunction("add", irBuiltIns.booleanType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashMapConstructor: IrConstructorSymbol =
|
||||
javaUtilLinkedHashMap.owner.addConstructor().apply {
|
||||
addValueParameter("p_0", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashMapPut: IrSimpleFunctionSymbol =
|
||||
javaUtilLinkedHashMap.owner.addFunction("put", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
addValueParameter("p_1", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashSetConstructor: IrConstructorSymbol =
|
||||
javaUtilLinkedHashSet.owner.addConstructor().apply {
|
||||
addValueParameter("p_0", irBuiltIns.intType)
|
||||
}.symbol
|
||||
|
||||
val linkedHashSetAdd: IrSimpleFunctionSymbol =
|
||||
javaUtilLinkedHashSet.owner.addFunction("add", irBuiltIns.booleanType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val treeMapConstructor: IrConstructorSymbol = javaUtilTreeMap.owner.addConstructor().symbol
|
||||
|
||||
val treeMapPut: IrSimpleFunctionSymbol =
|
||||
javaUtilTreeMap.owner.addFunction("put", irBuiltIns.anyNType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
addValueParameter("p_1", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val treeSetConstructor: IrConstructorSymbol = javaUtilTreeSet.owner.addConstructor().symbol
|
||||
|
||||
val treeSetAdd: IrSimpleFunctionSymbol =
|
||||
javaUtilTreeSet.owner.addFunction("add", irBuiltIns.booleanType).apply {
|
||||
addValueParameter("p_0", irBuiltIns.anyNType)
|
||||
}.symbol
|
||||
|
||||
val textUtilsCharSequenceCreator: IrFieldSymbol = androidTextTextUtils.owner.addField {
|
||||
name = Name.identifier("CHAR_SEQUENCE_CREATOR")
|
||||
type = androidOsParcelableCreator.defaultType
|
||||
isStatic = true
|
||||
}.symbol
|
||||
|
||||
val unsafeCoerceIntrinsic: IrSimpleFunctionSymbol =
|
||||
irFactory.buildFun {
|
||||
name = Name.special("<unsafe-coerce>")
|
||||
origin = IrDeclarationOrigin.IR_BUILTINS_STUB
|
||||
}.apply {
|
||||
parent = kotlinJvmInternalPackage
|
||||
val src = addTypeParameter("T", irBuiltIns.anyNType)
|
||||
val dst = addTypeParameter("R", irBuiltIns.anyNType)
|
||||
addValueParameter("v", src.defaultType)
|
||||
returnType = dst.defaultType
|
||||
}.symbol
|
||||
|
||||
private fun createPackage(packageName: String): IrPackageFragment =
|
||||
IrExternalPackageFragmentImpl.createEmptyExternalPackageFragment(
|
||||
moduleFragment.descriptor,
|
||||
FqName(packageName)
|
||||
)
|
||||
|
||||
private fun createClass(
|
||||
irPackage: IrPackageFragment,
|
||||
shortName: String,
|
||||
classKind: ClassKind,
|
||||
classModality: Modality,
|
||||
isValueClass: Boolean = false,
|
||||
): IrClassSymbol = irFactory.buildClass {
|
||||
name = Name.identifier(shortName)
|
||||
kind = classKind
|
||||
modality = classModality
|
||||
isValue = isValueClass
|
||||
}.apply {
|
||||
parent = irPackage
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
}.symbol
|
||||
|
||||
fun createBuilder(
|
||||
symbol: IrSymbol,
|
||||
startOffset: Int = UNDEFINED_OFFSET,
|
||||
endOffset: Int = UNDEFINED_OFFSET
|
||||
) = AndroidIrBuilder(this, symbol, startOffset, endOffset)
|
||||
}
|
||||
+352
@@ -0,0 +1,352 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.psiElement
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.declarations.inlineClassRepresentation
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.RAW_VALUE_ANNOTATION_FQ_NAMES
|
||||
|
||||
class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
|
||||
/**
|
||||
* Resolve the given [irType] to a corresponding [IrParcelSerializer]. This depends on the TypeParcelers which
|
||||
* are currently in [scope], as well as the type of the enclosing Parceleable class [parcelizeType], which is needed
|
||||
* to get a class loader for reflection based serialization. Beyond this, we need to know whether to allow
|
||||
* using read/writeValue for serialization (if [strict] is false). Beyond this, we need to know whether we are
|
||||
* producing parcelers for properties of a Parcelable (if [toplevel] is true), or for a complete Parcelable.
|
||||
*/
|
||||
fun get(
|
||||
irType: IrType,
|
||||
scope: IrParcelerScope?,
|
||||
parcelizeType: IrType,
|
||||
strict: Boolean = false,
|
||||
toplevel: Boolean = false
|
||||
): IrParcelSerializer {
|
||||
fun strict() = strict && !irType.hasAnyAnnotation(RAW_VALUE_ANNOTATION_FQ_NAMES)
|
||||
|
||||
scope.getCustomSerializer(irType)?.let { parceler ->
|
||||
return IrCustomParcelSerializer(parceler)
|
||||
}
|
||||
|
||||
val classifier = irType.erasedUpperBound
|
||||
val classifierFqName = classifier.fqNameWhenAvailable?.asString()
|
||||
when (classifierFqName) {
|
||||
// Built-in parcel serializers
|
||||
"kotlin.String", "java.lang.String" ->
|
||||
return stringSerializer
|
||||
"kotlin.CharSequence", "java.lang.CharSequence" ->
|
||||
return charSequenceSerializer
|
||||
"android.os.Bundle" ->
|
||||
return IrParcelSerializerWithClassLoader(parcelizeType, symbols.parcelReadBundle, symbols.parcelWriteBundle)
|
||||
"android.os.PersistableBundle" ->
|
||||
return IrParcelSerializerWithClassLoader(
|
||||
parcelizeType,
|
||||
symbols.parcelReadPersistableBundle,
|
||||
symbols.parcelWritePersistableBundle
|
||||
)
|
||||
|
||||
// Non-nullable built-in serializers
|
||||
"kotlin.Byte", "java.lang.Byte" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, byteSerializer)
|
||||
"kotlin.Boolean", "java.lang.Boolean" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, booleanSerializer)
|
||||
"kotlin.Char", "java.lang.Character" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, charSerializer)
|
||||
"kotlin.Short", "java.lang.Short" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, shortSerializer)
|
||||
"kotlin.Int", "java.lang.Integer" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, intSerializer)
|
||||
"kotlin.Long", "java.lang.Long" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, longSerializer)
|
||||
"kotlin.Float", "java.lang.Float" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, floatSerializer)
|
||||
"kotlin.Double", "java.lang.Double" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, doubleSerializer)
|
||||
"java.io.FileDescriptor" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, fileDescriptorSerializer)
|
||||
"android.util.Size" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, sizeSerializer)
|
||||
"android.util.SizeF" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, sizeFSerializer)
|
||||
|
||||
// Unsigned primitive types
|
||||
"kotlin.UByte" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, ubyteSerializer)
|
||||
"kotlin.UShort" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, ushortSerializer)
|
||||
"kotlin.UInt" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, uintSerializer)
|
||||
"kotlin.ULong" ->
|
||||
return wrapNullableSerializerIfNeeded(irType, ulongSerializer)
|
||||
|
||||
// Built-in non-parameterized container types.
|
||||
"kotlin.IntArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.intType))
|
||||
return intArraySerializer
|
||||
"kotlin.BooleanArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
|
||||
return booleanArraySerializer
|
||||
"kotlin.ByteArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.byteType))
|
||||
return byteArraySerializer
|
||||
"kotlin.CharArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.charType))
|
||||
return charArraySerializer
|
||||
"kotlin.FloatArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.floatType))
|
||||
return floatArraySerializer
|
||||
"kotlin.DoubleArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.doubleType))
|
||||
return doubleArraySerializer
|
||||
"kotlin.LongArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.longType))
|
||||
return longArraySerializer
|
||||
"android.util.SparseBooleanArray" ->
|
||||
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
|
||||
return sparseBooleanArraySerializer
|
||||
|
||||
// Unsigned array types
|
||||
"kotlin.UByteArray" ->
|
||||
return ubyteArraySerializer
|
||||
"kotlin.UShortArray" ->
|
||||
return wrapNullableSerializerIfNeeded(
|
||||
irType,
|
||||
ushortArraySerializer
|
||||
)
|
||||
"kotlin.UIntArray" ->
|
||||
return uintArraySerializer
|
||||
"kotlin.ULongArray" ->
|
||||
return ulongArraySerializer
|
||||
}
|
||||
|
||||
// Generic container types
|
||||
when (classifierFqName) {
|
||||
// Apart from kotlin.Array and kotlin.ShortArray, these will only be hit if we have a
|
||||
// special parceler for the element type.
|
||||
"kotlin.Array", "kotlin.ShortArray", "kotlin.IntArray",
|
||||
"kotlin.BooleanArray", "kotlin.ByteArray", "kotlin.CharArray",
|
||||
"kotlin.FloatArray", "kotlin.DoubleArray", "kotlin.LongArray" -> {
|
||||
val elementType = irType.getArrayElementType(irBuiltIns)
|
||||
|
||||
if (!scope.hasCustomSerializer(elementType)) {
|
||||
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
|
||||
"java.lang.String", "kotlin.String" ->
|
||||
return stringArraySerializer
|
||||
"android.os.IBinder" ->
|
||||
return iBinderArraySerializer
|
||||
}
|
||||
}
|
||||
|
||||
val arrayType =
|
||||
if (classifier.defaultType.isPrimitiveArray()) classifier.defaultType else irBuiltIns.arrayClass.typeWith(elementType)
|
||||
return wrapNullableSerializerIfNeeded(
|
||||
irType,
|
||||
IrArrayParcelSerializer(arrayType, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
)
|
||||
}
|
||||
|
||||
// This will only be hit if we have a custom serializer for booleans
|
||||
"android.util.SparseBooleanArray" ->
|
||||
return IrSparseArrayParcelSerializer(
|
||||
classifier,
|
||||
irBuiltIns.booleanType,
|
||||
get(irBuiltIns.booleanType, scope, parcelizeType, strict())
|
||||
)
|
||||
"android.util.SparseIntArray" ->
|
||||
return IrSparseArrayParcelSerializer(
|
||||
classifier,
|
||||
irBuiltIns.intType,
|
||||
get(irBuiltIns.intType, scope, parcelizeType, strict())
|
||||
)
|
||||
"android.util.SparseLongArray" ->
|
||||
return IrSparseArrayParcelSerializer(
|
||||
classifier,
|
||||
irBuiltIns.longType,
|
||||
get(irBuiltIns.longType, scope, parcelizeType, strict())
|
||||
)
|
||||
"android.util.SparseArray" -> {
|
||||
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
|
||||
return IrSparseArrayParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
}
|
||||
|
||||
// TODO: More java collections?
|
||||
// TODO: Add tests for all of these types, not just some common ones...
|
||||
// FIXME: Is the support for ArrayDeque missing in the old BE?
|
||||
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List",
|
||||
"kotlin.collections.ArrayList", "java.util.ArrayList",
|
||||
"kotlin.collections.ArrayDeque", "java.util.ArrayDeque",
|
||||
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set",
|
||||
"kotlin.collections.HashSet", "java.util.HashSet",
|
||||
"kotlin.collections.LinkedHashSet", "java.util.LinkedHashSet",
|
||||
"java.util.NavigableSet", "java.util.SortedSet" -> {
|
||||
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
|
||||
if (!scope.hasCustomSerializer(elementType) && classifierFqName in setOf(
|
||||
"kotlin.collections.List", "kotlin.collections.MutableList", "kotlin.collections.ArrayList",
|
||||
"java.util.List", "java.util.ArrayList"
|
||||
)
|
||||
) {
|
||||
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
|
||||
"android.os.IBinder" ->
|
||||
return iBinderListSerializer
|
||||
"kotlin.String", "java.lang.String" ->
|
||||
return stringListSerializer
|
||||
}
|
||||
}
|
||||
return wrapNullableSerializerIfNeeded(
|
||||
irType,
|
||||
IrListParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
)
|
||||
}
|
||||
|
||||
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map",
|
||||
"kotlin.collections.HashMap", "java.util.HashMap",
|
||||
"kotlin.collections.LinkedHashMap", "java.util.LinkedHashMap",
|
||||
"java.util.SortedMap", "java.util.NavigableMap", "java.util.TreeMap",
|
||||
"java.util.concurrent.ConcurrentHashMap" -> {
|
||||
val keyType = (irType as IrSimpleType).arguments[0].upperBound(irBuiltIns)
|
||||
val valueType = irType.arguments[1].upperBound(irBuiltIns)
|
||||
val parceler =
|
||||
IrMapParcelSerializer(
|
||||
classifier,
|
||||
keyType,
|
||||
valueType,
|
||||
get(keyType, scope, parcelizeType, strict()),
|
||||
get(valueType, scope, parcelizeType, strict())
|
||||
)
|
||||
return wrapNullableSerializerIfNeeded(irType, parceler)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic parcelable types
|
||||
when {
|
||||
classifier.isSubclassOfFqName("android.os.Parcelable")
|
||||
// Avoid infinite loops when deriving parcelers for enum or object classes.
|
||||
&& !(toplevel && (classifier.isObject || classifier.isEnumClass)) -> {
|
||||
// We try to use writeToParcel/createFromParcel directly whenever possible, but there are some caveats.
|
||||
//
|
||||
// According to the JLS, changing a class from final to non-final is a binary compatible change, hence we
|
||||
// cannot use the writeToParcel/createFromParcel methods directly when serializing classes from external
|
||||
// dependencies. This issue was originally reported in KT-20029.
|
||||
//
|
||||
// Conversely, we can and should apply this optimization to all classes in the current module which
|
||||
// implement Parcelable (KT-20030). There are two cases to consider. If the class is annotated with
|
||||
// @Parcelize, we will create the corresponding methods/fields ourselves, before we generate the code
|
||||
// for writeToParcel/createFromParcel. For Java classes (or compiled Kotlin classes annotated with
|
||||
// @Parcelize), we'll have a field in the class itself. Finally, with Parcelable instances which were
|
||||
// manually implemented in Kotlin, we'll instead have an @JvmField property getter in the companion object.
|
||||
return if (classifier.modality == Modality.FINAL && classifier.psiElement != null
|
||||
&& (classifier.isParcelize || classifier.hasCreatorField)
|
||||
) {
|
||||
wrapNullableSerializerIfNeeded(irType, IrEfficientParcelableParcelSerializer(classifier))
|
||||
} else {
|
||||
// In all other cases, we have to use the generic methods in Parcel, which use reflection internally.
|
||||
IrGenericParcelableParcelSerializer(parcelizeType)
|
||||
}
|
||||
}
|
||||
|
||||
classifier.isSubclassOfFqName("android.os.IBinder") ->
|
||||
return iBinderSerializer
|
||||
|
||||
classifier.isObject ->
|
||||
return IrObjectParcelSerializer(classifier)
|
||||
|
||||
classifier.isEnumClass ->
|
||||
return wrapNullableSerializerIfNeeded(irType, IrEnumParcelSerializer(classifier))
|
||||
|
||||
classifier.isSubclassOfFqName("java.io.Serializable")
|
||||
// Functions and Continuations are always serializable.
|
||||
|| irType.isFunctionTypeOrSubtype() || irType.isSuspendFunctionTypeOrSubtype() ->
|
||||
return serializableSerializer
|
||||
|
||||
strict() ->
|
||||
throw IllegalArgumentException("Illegal type, could not find a specific serializer for ${irType.render()}")
|
||||
|
||||
else ->
|
||||
return IrParcelSerializerWithClassLoader(parcelizeType, symbols.parcelReadValue, symbols.parcelWriteValue)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapNullableSerializerIfNeeded(irType: IrType, serializer: IrParcelSerializer) =
|
||||
if (irType.isNullable()) IrNullAwareParcelSerializer(serializer) else serializer
|
||||
|
||||
private val irBuiltIns: IrBuiltIns
|
||||
get() = symbols.irBuiltIns
|
||||
|
||||
private val stringArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArray, symbols.parcelWriteStringArray)
|
||||
private val stringListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArrayList, symbols.parcelWriteStringList)
|
||||
private val iBinderSerializer = IrSimpleParcelSerializer(symbols.parcelReadStrongBinder, symbols.parcelWriteStrongBinder)
|
||||
private val iBinderArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArray, symbols.parcelWriteBinderArray)
|
||||
private val iBinderListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArrayList, symbols.parcelWriteBinderList)
|
||||
private val serializableSerializer = IrSimpleParcelSerializer(symbols.parcelReadSerializable, symbols.parcelWriteSerializable)
|
||||
private val stringSerializer = IrSimpleParcelSerializer(symbols.parcelReadString, symbols.parcelWriteString)
|
||||
private val byteSerializer = IrSimpleParcelSerializer(symbols.parcelReadByte, symbols.parcelWriteByte)
|
||||
private val intSerializer = IrSimpleParcelSerializer(symbols.parcelReadInt, symbols.parcelWriteInt)
|
||||
private val longSerializer = IrSimpleParcelSerializer(symbols.parcelReadLong, symbols.parcelWriteLong)
|
||||
private val floatSerializer = IrSimpleParcelSerializer(symbols.parcelReadFloat, symbols.parcelWriteFloat)
|
||||
private val doubleSerializer = IrSimpleParcelSerializer(symbols.parcelReadDouble, symbols.parcelWriteDouble)
|
||||
private val intArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateIntArray, symbols.parcelWriteIntArray)
|
||||
private val booleanArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBooleanArray, symbols.parcelWriteBooleanArray)
|
||||
private val byteArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateByteArray, symbols.parcelWriteByteArray)
|
||||
private val charArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateCharArray, symbols.parcelWriteCharArray)
|
||||
private val doubleArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateDoubleArray, symbols.parcelWriteDoubleArray)
|
||||
private val floatArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateFloatArray, symbols.parcelWriteFloatArray)
|
||||
private val longArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateLongArray, symbols.parcelWriteLongArray)
|
||||
|
||||
// Primitive types without dedicated read/write methods need an additional cast.
|
||||
private val booleanSerializer = IrWrappedIntParcelSerializer(irBuiltIns.booleanType)
|
||||
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
|
||||
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
|
||||
private val shortArraySerializer = IrArrayParcelSerializer(
|
||||
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.shortType).defaultType,
|
||||
irBuiltIns.shortType,
|
||||
shortSerializer
|
||||
)
|
||||
|
||||
// Unsigned primitive types
|
||||
private val ubyteSerializer = IrUnsafeCoerceWrappedSerializer(byteSerializer, symbols.kotlinUByte.defaultType, irBuiltIns.byteType)
|
||||
private val ushortSerializer = IrUnsafeCoerceWrappedSerializer(shortSerializer, symbols.kotlinUShort.defaultType, irBuiltIns.shortType)
|
||||
private val uintSerializer = IrUnsafeCoerceWrappedSerializer(intSerializer, symbols.kotlinUInt.defaultType, irBuiltIns.intType)
|
||||
private val ulongSerializer = IrUnsafeCoerceWrappedSerializer(longSerializer, symbols.kotlinULong.defaultType, irBuiltIns.longType)
|
||||
|
||||
// Unsigned array types
|
||||
private val ubyteArraySerializer = IrUnsafeCoerceWrappedSerializer(
|
||||
byteArraySerializer,
|
||||
symbols.kotlinUByteArray.owner.defaultType,
|
||||
symbols.kotlinUByteArray.owner.inlineClassRepresentation!!.underlyingType
|
||||
)
|
||||
|
||||
private val ushortArraySerializer = IrUnsafeCoerceWrappedSerializer(
|
||||
shortArraySerializer,
|
||||
symbols.kotlinUShortArray.owner.defaultType,
|
||||
symbols.kotlinUShortArray.owner.inlineClassRepresentation!!.underlyingType
|
||||
)
|
||||
|
||||
private val uintArraySerializer = IrUnsafeCoerceWrappedSerializer(
|
||||
intArraySerializer,
|
||||
symbols.kotlinUIntArray.owner.defaultType,
|
||||
symbols.kotlinUIntArray.owner.inlineClassRepresentation!!.underlyingType
|
||||
)
|
||||
|
||||
private val ulongArraySerializer = IrUnsafeCoerceWrappedSerializer(
|
||||
longArraySerializer,
|
||||
symbols.kotlinULongArray.owner.defaultType,
|
||||
symbols.kotlinULongArray.owner.inlineClassRepresentation!!.underlyingType
|
||||
)
|
||||
|
||||
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
|
||||
|
||||
// TODO The old backend uses the hidden "read/writeRawFileDescriptor" methods.
|
||||
private val fileDescriptorSerializer = IrSimpleParcelSerializer(symbols.parcelReadFileDescriptor, symbols.parcelWriteFileDescriptor)
|
||||
|
||||
private val sizeSerializer = IrSimpleParcelSerializer(symbols.parcelReadSize, symbols.parcelWriteSize)
|
||||
private val sizeFSerializer = IrSimpleParcelSerializer(symbols.parcelReadSizeF, symbols.parcelWriteSizeF)
|
||||
private val sparseBooleanArraySerializer =
|
||||
IrSimpleParcelSerializer(symbols.parcelReadSparseBooleanArray, symbols.parcelWriteSparseBooleanArray)
|
||||
}
|
||||
+553
@@ -0,0 +1,553 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isJvmInterface
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrConstructorSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
|
||||
interface IrParcelSerializer {
|
||||
fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression
|
||||
fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression
|
||||
}
|
||||
|
||||
fun AndroidIrBuilder.readParcelWith(serializer: IrParcelSerializer, parcel: IrValueDeclaration): IrExpression {
|
||||
return with(serializer) { readParcel(parcel) }
|
||||
}
|
||||
|
||||
fun AndroidIrBuilder.writeParcelWith(
|
||||
serializer: IrParcelSerializer,
|
||||
parcel: IrValueDeclaration,
|
||||
flags: IrValueDeclaration,
|
||||
value: IrExpression
|
||||
): IrExpression {
|
||||
return with(serializer) { writeParcel(parcel, flags, value) }
|
||||
}
|
||||
|
||||
// Creates a serializer from a pair of parcel methods of the form reader()T and writer(T)V.
|
||||
class IrSimpleParcelSerializer(private val reader: IrSimpleFunctionSymbol, private val writer: IrSimpleFunctionSymbol) :
|
||||
IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irCall(reader).apply { dispatchReceiver = irGet(parcel) }
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irCall(writer).apply {
|
||||
dispatchReceiver = irGet(parcel)
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize a value of the primitive [parcelType] by coercion to int.
|
||||
class IrWrappedIntParcelSerializer(private val parcelType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return if (parcelType.isBoolean()) {
|
||||
irNotEquals(parcelReadInt(irGet(parcel)), irInt(0))
|
||||
} else {
|
||||
val conversion = context.irBuiltIns.intClass.functions.first { function ->
|
||||
function.owner.name.asString() == "to${parcelType.getClass()!!.name}"
|
||||
}
|
||||
irCall(conversion).apply {
|
||||
dispatchReceiver = parcelReadInt(irGet(parcel))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
parcelWriteInt(
|
||||
irGet(parcel),
|
||||
if (parcelType.isBoolean()) {
|
||||
irIfThenElse(context.irBuiltIns.intType, value, irInt(1), irInt(0))
|
||||
} else {
|
||||
val conversion = parcelType.classOrNull!!.functions.first { function ->
|
||||
function.owner.name.asString() == "toInt"
|
||||
}
|
||||
irCall(conversion).apply { dispatchReceiver = value }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
class IrUnsafeCoerceWrappedSerializer(
|
||||
private val serializer: IrParcelSerializer,
|
||||
private val wrappedType: IrType,
|
||||
private val underlyingType: IrType,
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return unsafeCoerce(readParcelWith(serializer, parcel), underlyingType, wrappedType)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return writeParcelWith(serializer, parcel, flags, unsafeCoerce(value, wrappedType, underlyingType))
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps a non-null aware parceler to handle nullable types.
|
||||
class IrNullAwareParcelSerializer(private val serializer: IrParcelSerializer) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val nonNullResult = readParcelWith(serializer, parcel)
|
||||
return irIfThenElse(
|
||||
nonNullResult.type.makeNullable(),
|
||||
irEquals(parcelReadInt(irGet(parcel)), irInt(0)),
|
||||
irNull(),
|
||||
nonNullResult
|
||||
)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irLetS(value) { irValueSymbol ->
|
||||
irIfNull(
|
||||
context.irBuiltIns.unitType,
|
||||
irGet(irValueSymbol.owner),
|
||||
parcelWriteInt(irGet(parcel), irInt(0)),
|
||||
irBlock {
|
||||
+parcelWriteInt(irGet(parcel), irInt(1))
|
||||
+writeParcelWith(serializer, parcel, flags, irGet(irValueSymbol.owner))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for object classes. We avoid empty parcels by writing a dummy value. Not null-safe.
|
||||
class IrObjectParcelSerializer(private val objectClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression
|
||||
// Avoid empty parcels
|
||||
{
|
||||
return irBlock {
|
||||
+parcelReadInt(irGet(parcel))
|
||||
+irGetObject(objectClass.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteInt(irGet(parcel), irInt(1))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for classes with a default constructor. We avoid empty parcels by writing a dummy value. Not null-safe.
|
||||
class IrNoParameterClassParcelSerializer(private val irClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val defaultConstructor = irClass.primaryConstructor!!
|
||||
return irBlock {
|
||||
+parcelReadInt(irGet(parcel))
|
||||
+irCall(defaultConstructor)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteInt(irGet(parcel), irInt(1))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for enum classes. Not null-safe.
|
||||
class IrEnumParcelSerializer(enumClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irCall(enumValueOf).apply {
|
||||
putValueArgument(0, parcelReadString(irGet(parcel)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteString(irGet(parcel), irCall(enumName).apply {
|
||||
dispatchReceiver = value
|
||||
})
|
||||
}
|
||||
|
||||
private val enumValueOf: IrFunctionSymbol =
|
||||
enumClass.functions.single { function ->
|
||||
function.name.asString() == "valueOf" && function.dispatchReceiverParameter == null
|
||||
&& function.extensionReceiverParameter == null && function.valueParameters.size == 1
|
||||
&& function.valueParameters.single().type.isString()
|
||||
}.symbol
|
||||
|
||||
private val enumName: IrFunctionSymbol = enumClass.getPropertyGetter("name")!!
|
||||
}
|
||||
|
||||
// Parcel serializer for the java CharSequence interface.
|
||||
class IrCharSequenceParcelSerializer : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelableCreatorCreateFromParcel(getTextUtilsCharSequenceCreator(), irGet(parcel))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return textUtilsWriteToParcel(value, irGet(parcel), irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for Parcelables in the same module, which accesses the writeToParcel/createFromParcel methods without reflection.
|
||||
class IrEfficientParcelableParcelSerializer(private val irClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelableCreatorCreateFromParcel(getParcelableCreator(irClass), irGet(parcel))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelableWriteToParcel(irClass, value, irGet(parcel), irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for Parcelables using reflection.
|
||||
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
|
||||
class IrGenericParcelableParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelReadParcelable(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelWriteParcelable(irGet(parcel), value, irGet(flags))
|
||||
}
|
||||
}
|
||||
|
||||
// Creates a serializer from a pair of parcel methods of the form reader(ClassLoader)T and writer(T)V.
|
||||
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
|
||||
class IrParcelSerializerWithClassLoader(
|
||||
private val parcelizeType: IrType,
|
||||
private val reader: IrSimpleFunctionSymbol,
|
||||
private val writer: IrSimpleFunctionSymbol
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irCall(reader).apply {
|
||||
dispatchReceiver = irGet(parcel)
|
||||
putValueArgument(0, classGetClassLoader(javaClassReference(parcelizeType)))
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irCall(writer).apply {
|
||||
dispatchReceiver = irGet(parcel)
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer using a custom Parceler object.
|
||||
class IrCustomParcelSerializer(private val parcelerObject: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return parcelerCreate(parcelerObject, parcel)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return parcelerWrite(parcelerObject, parcel, flags, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for array types. This handles both primitive array types (for ShortArray and for primitive arrays using custom element
|
||||
// parcelers) as well as boxed arrays.
|
||||
class IrArrayParcelSerializer(
|
||||
private val arrayType: IrType,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
private fun AndroidIrBuilder.newArray(size: IrExpression): IrExpression {
|
||||
val arrayConstructor: IrFunctionSymbol = if (arrayType.isBoxedArray) {
|
||||
context.irBuiltIns.arrayOfNulls
|
||||
} else {
|
||||
arrayType.classOrNull!!.constructors.single { it.owner.valueParameters.size == 1 }
|
||||
}
|
||||
|
||||
return irCall(arrayConstructor, arrayType).apply {
|
||||
if (typeArgumentsCount != 0)
|
||||
putTypeArgument(0, elementType)
|
||||
putValueArgument(0, size)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val arraySize = irTemporary(parcelReadInt(irGet(parcel)))
|
||||
val arrayTemporary = irTemporary(newArray(irGet(arraySize)))
|
||||
forUntil(irGet(arraySize)) { index ->
|
||||
val setter = arrayType.classOrNull!!.getSimpleFunction("set")!!
|
||||
+irCall(setter).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
putValueArgument(1, readParcelWith(elementSerializer, parcel))
|
||||
}
|
||||
}
|
||||
+irGet(arrayTemporary)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irBlock {
|
||||
val arrayTemporary = irTemporary(value)
|
||||
val arraySizeSymbol = arrayType.classOrNull!!.getPropertyGetter("size")!!
|
||||
val arraySize = irTemporary(irCall(arraySizeSymbol).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
})
|
||||
|
||||
+parcelWriteInt(irGet(parcel), irGet(arraySize))
|
||||
|
||||
forUntil(irGet(arraySize)) { index ->
|
||||
val getter = context.irBuiltIns.arrayClass.getSimpleFunction("get")!!
|
||||
val element = irCall(getter, elementType).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
}
|
||||
+writeParcelWith(elementSerializer, parcel, flags, element)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for android SparseArrays. Note that this also needs to handle BooleanSparseArray, in case of a custom element parceler.
|
||||
class IrSparseArrayParcelSerializer(
|
||||
private val sparseArrayClass: IrClass,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val remainingSizeTemporary = irTemporary(parcelReadInt(irGet(parcel)), isMutable = true)
|
||||
|
||||
val sparseArrayConstructor = sparseArrayClass.constructors.first { irConstructor ->
|
||||
irConstructor.valueParameters.size == 1 && irConstructor.valueParameters.single().type.isInt()
|
||||
}
|
||||
|
||||
val constructorCall = if (sparseArrayClass.typeParameters.isEmpty())
|
||||
irCall(sparseArrayConstructor)
|
||||
else
|
||||
irCallConstructor(sparseArrayConstructor.symbol, listOf(elementType))
|
||||
|
||||
val arrayTemporary = irTemporary(constructorCall.apply {
|
||||
putValueArgument(0, irGet(remainingSizeTemporary))
|
||||
})
|
||||
|
||||
+irWhile().apply {
|
||||
condition = irNotEquals(irGet(remainingSizeTemporary), irInt(0))
|
||||
body = irBlock {
|
||||
val sparseArrayPut = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "put" && function.valueParameters.size == 2
|
||||
}
|
||||
+irCall(sparseArrayPut).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, parcelReadInt(irGet(parcel)))
|
||||
putValueArgument(1, readParcelWith(elementSerializer, parcel))
|
||||
}
|
||||
|
||||
val dec = context.irBuiltIns.intClass.getSimpleFunction("dec")!!
|
||||
+irSet(remainingSizeTemporary.symbol, irCall(dec).apply {
|
||||
dispatchReceiver = irGet(remainingSizeTemporary)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+irGet(arrayTemporary)
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
return irBlock {
|
||||
val sizeFunction = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "size" && function.valueParameters.isEmpty()
|
||||
}
|
||||
val keyAtFunction = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "keyAt" && function.valueParameters.size == 1
|
||||
}
|
||||
val valueAtFunction = sparseArrayClass.functions.first { function ->
|
||||
function.name.asString() == "valueAt" && function.valueParameters.size == 1
|
||||
}
|
||||
|
||||
val arrayTemporary = irTemporary(value)
|
||||
val sizeTemporary = irTemporary(irCall(sizeFunction).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
})
|
||||
|
||||
+parcelWriteInt(irGet(parcel), irGet(sizeTemporary))
|
||||
|
||||
forUntil(irGet(sizeTemporary)) { index ->
|
||||
+parcelWriteInt(irGet(parcel), irCall(keyAtFunction).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
})
|
||||
|
||||
+writeParcelWith(elementSerializer, parcel, flags, irCall(valueAtFunction.symbol, elementType).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for all lists supported by Parcelize. List interfaces use hard-coded default implementations for deserialization.
|
||||
// List maps to ArrayList, Set maps to LinkedHashSet, NavigableSet and SortedSet map to TreeSet.
|
||||
class IrListParcelSerializer(
|
||||
private val irClass: IrClass,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
val sizeFunction = irClass.getPropertyGetter("size")!!
|
||||
val iteratorFunction = irClass.getMethodWithoutArguments("iterator")
|
||||
val iteratorClass = iteratorFunction.returnType.erasedUpperBound
|
||||
val iteratorHasNext = iteratorClass.getMethodWithoutArguments("hasNext")
|
||||
val iteratorNext = iteratorClass.getMethodWithoutArguments("next")
|
||||
|
||||
return irBlock {
|
||||
val list = irTemporary(value)
|
||||
+parcelWriteInt(irGet(parcel), irCall(sizeFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
})
|
||||
val iterator = irTemporary(irCall(iteratorFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
})
|
||||
+irWhile().apply {
|
||||
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
|
||||
body = writeParcelWith(elementSerializer, parcel, flags, irCall(iteratorNext.symbol, elementType).apply {
|
||||
dispatchReceiver = irGet(iterator)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun listSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
|
||||
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
|
||||
// the the default constructor if none exist.
|
||||
if (!irClass.isJvmInterface) {
|
||||
val constructor = irClass.constructors.find { constructor ->
|
||||
constructor.valueParameters.size == 1 && constructor.valueParameters.single().type.isInt()
|
||||
} ?: irClass.constructors.first { constructor -> constructor.valueParameters.isEmpty() }
|
||||
|
||||
val add = irClass.functions.first { function ->
|
||||
function.name.asString() == "add" && function.valueParameters.size == 1
|
||||
}
|
||||
|
||||
return constructor.symbol to add.symbol
|
||||
}
|
||||
|
||||
return when (irClass.fqNameWhenAvailable?.asString()) {
|
||||
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List" ->
|
||||
symbols.arrayListConstructor to symbols.arrayListAdd
|
||||
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set" ->
|
||||
symbols.linkedHashSetConstructor to symbols.linkedHashSetAdd
|
||||
"java.util.NavigableSet", "java.util.SortedSet" ->
|
||||
symbols.treeSetConstructor to symbols.treeSetAdd
|
||||
else -> error("Unknown list interface type: ${irClass.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val (constructorSymbol, addSymbol) = listSymbols(androidSymbols)
|
||||
val sizeTemporary = irTemporary(parcelReadInt(irGet(parcel)))
|
||||
val list = irTemporary(irCall(constructorSymbol).apply {
|
||||
if (constructorSymbol.owner.valueParameters.isNotEmpty())
|
||||
putValueArgument(0, irGet(sizeTemporary))
|
||||
})
|
||||
forUntil(irGet(sizeTemporary)) {
|
||||
+irCall(addSymbol).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
putValueArgument(0, readParcelWith(elementSerializer, parcel))
|
||||
}
|
||||
}
|
||||
+irGet(list)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parcel serializer for all maps supported by Parcelize. Map interfaces use hard-coded default implementations for deserialization.
|
||||
// Map uses LinkedHashMap, while NavigableMap and SortedMap use to TreeMap.
|
||||
class IrMapParcelSerializer(
|
||||
private val irClass: IrClass,
|
||||
private val keyType: IrType,
|
||||
private val valueType: IrType,
|
||||
private val keySerializer: IrParcelSerializer,
|
||||
private val valueSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
val sizeFunction = irClass.getPropertyGetter("size")!!
|
||||
val entriesFunction = irClass.getPropertyGetter("entries")!!
|
||||
val entrySetClass = entriesFunction.owner.returnType.erasedUpperBound
|
||||
val iteratorFunction = entrySetClass.getMethodWithoutArguments("iterator")
|
||||
val iteratorClass = iteratorFunction.returnType.erasedUpperBound
|
||||
val iteratorHasNext = iteratorClass.getMethodWithoutArguments("hasNext")
|
||||
val iteratorNext = iteratorClass.getMethodWithoutArguments("next")
|
||||
val elementClass =
|
||||
(entriesFunction.owner.returnType as IrSimpleType).arguments.single().upperBound(context.irBuiltIns).erasedUpperBound
|
||||
val elementKey = elementClass.getPropertyGetter("key")!!
|
||||
val elementValue = elementClass.getPropertyGetter("value")!!
|
||||
|
||||
return irBlock {
|
||||
val list = irTemporary(value)
|
||||
+parcelWriteInt(irGet(parcel), irCall(sizeFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
})
|
||||
val iterator = irTemporary(irCall(iteratorFunction).apply {
|
||||
dispatchReceiver = irCall(entriesFunction).apply {
|
||||
dispatchReceiver = irGet(list)
|
||||
}
|
||||
})
|
||||
+irWhile().apply {
|
||||
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
|
||||
body = irBlock {
|
||||
val element = irTemporary(irCall(iteratorNext).apply {
|
||||
dispatchReceiver = irGet(iterator)
|
||||
})
|
||||
+writeParcelWith(keySerializer, parcel, flags, irCall(elementKey, keyType).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
+writeParcelWith(valueSerializer, parcel, flags, irCall(elementValue, valueType).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun mapSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
|
||||
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
|
||||
// the the default constructor if none exist.
|
||||
if (!irClass.isJvmInterface) {
|
||||
val constructor = irClass.constructors.find { constructor ->
|
||||
constructor.valueParameters.size == 1 && constructor.valueParameters.single().type.isInt()
|
||||
} ?: irClass.constructors.find { constructor ->
|
||||
constructor.valueParameters.isEmpty()
|
||||
}!!
|
||||
|
||||
val put = irClass.functions.first { function ->
|
||||
function.name.asString() == "put" && function.valueParameters.size == 2
|
||||
}
|
||||
|
||||
return constructor.symbol to put.symbol
|
||||
}
|
||||
|
||||
return when (irClass.fqNameWhenAvailable?.asString()) {
|
||||
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map" ->
|
||||
symbols.linkedHashMapConstructor to symbols.linkedHashMapPut
|
||||
"java.util.SortedMap", "java.util.NavigableMap" ->
|
||||
symbols.treeMapConstructor to symbols.treeMapPut
|
||||
else -> error("Unknown map interface type: ${irClass.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
return irBlock {
|
||||
val (constructorSymbol, putSymbol) = mapSymbols(androidSymbols)
|
||||
val sizeTemporary = irTemporary(parcelReadInt(irGet(parcel)))
|
||||
val map = irTemporary(irCall(constructorSymbol).apply {
|
||||
if (constructorSymbol.owner.valueParameters.isNotEmpty())
|
||||
putValueArgument(0, irGet(sizeTemporary))
|
||||
})
|
||||
forUntil(irGet(sizeTemporary)) {
|
||||
+irCall(putSymbol).apply {
|
||||
dispatchReceiver = irGet(map)
|
||||
putValueArgument(0, readParcelWith(keySerializer, parcel))
|
||||
putValueArgument(1, readParcelWith(valueSerializer, parcel))
|
||||
}
|
||||
}
|
||||
+irGet(map)
|
||||
}
|
||||
}
|
||||
}
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.ir.declarations.IrAnnotationContainer
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.types.IrSimpleType
|
||||
import org.jetbrains.kotlin.ir.types.IrType
|
||||
import org.jetbrains.kotlin.ir.types.getClass
|
||||
import org.jetbrains.kotlin.ir.types.typeOrNull
|
||||
import org.jetbrains.kotlin.ir.util.constructedClass
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.TYPE_PARCELER_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_WITH_FQ_NAMES
|
||||
|
||||
// Keep track of all custom parcelers which are currently in scope.
|
||||
// Note that custom parcelers are resolved in *reverse* lexical order.
|
||||
class IrParcelerScope(private val parent: IrParcelerScope? = null) {
|
||||
private val typeParcelers = mutableMapOf<IrType, IrClass>()
|
||||
|
||||
fun add(type: IrType, parceler: IrClass) {
|
||||
typeParcelers.putIfAbsent(type, parceler)
|
||||
}
|
||||
|
||||
fun get(type: IrType): IrClass? =
|
||||
parent?.get(type) ?: typeParcelers[type]
|
||||
}
|
||||
|
||||
fun IrParcelerScope?.getCustomSerializer(irType: IrType): IrClass? {
|
||||
return irType.getAnyAnnotation(WRITE_WITH_FQ_NAMES)?.let { writeWith ->
|
||||
(writeWith.type as IrSimpleType).arguments.single().typeOrNull!!.getClass()!!
|
||||
} ?: this?.get(irType)
|
||||
}
|
||||
|
||||
fun IrParcelerScope?.hasCustomSerializer(irType: IrType): Boolean {
|
||||
return getCustomSerializer(irType) != null
|
||||
}
|
||||
|
||||
fun IrAnnotationContainer.getParcelerScope(parent: IrParcelerScope? = null): IrParcelerScope? {
|
||||
val typeParcelerAnnotations = annotations.filterTo(mutableListOf()) {
|
||||
it.symbol.owner.constructedClass.fqNameWhenAvailable in TYPE_PARCELER_FQ_NAMES
|
||||
}
|
||||
|
||||
if (typeParcelerAnnotations.isEmpty())
|
||||
return parent
|
||||
|
||||
val scope = IrParcelerScope(parent)
|
||||
|
||||
for (annotation in typeParcelerAnnotations) {
|
||||
val (mappedType, parcelerType) = (annotation.type as IrSimpleType).arguments.map { it.typeOrNull!! }
|
||||
scope.add(mappedType, parcelerType.getClass()!!)
|
||||
}
|
||||
|
||||
return scope
|
||||
}
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.fir.backend.IrPluginDeclarationOrigin
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.util.render
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent
|
||||
import org.jetbrains.kotlin.parcelize.fir.FirParcelizePluginKey
|
||||
|
||||
class ParcelizeFirIrTransformer(
|
||||
context: IrPluginContext,
|
||||
androidSymbols: AndroidSymbols
|
||||
) : ParcelizeIrTransformerBase(context, androidSymbols) {
|
||||
|
||||
fun transform(moduleFragment: IrModuleFragment) {
|
||||
moduleFragment.accept(this, null)
|
||||
deferredOperations.forEach { it() }
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) = element.acceptChildren(this, null)
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.acceptChildren(this, null)
|
||||
|
||||
// Sealed classes can be annotated with `@Parcelize`, but that only implies that we
|
||||
// should process their immediate subclasses.
|
||||
if (!declaration.isParcelize || declaration.modality == Modality.SEALED)
|
||||
return
|
||||
|
||||
val parcelableProperties = declaration.parcelableProperties
|
||||
|
||||
// If the companion extends Parceler, it can override parts of the generated implementation.
|
||||
val parcelerObject = declaration.companionObject()?.takeIf {
|
||||
it.isSubclassOfFqName(PARCELER_FQN.asString())
|
||||
}
|
||||
|
||||
for (function in declaration.functions) {
|
||||
val origin = function.origin
|
||||
if (origin !is IrPluginDeclarationOrigin || origin.pluginKey != FirParcelizePluginKey) continue
|
||||
when (function.name.identifier) {
|
||||
ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS.methodName -> {
|
||||
function.generateDescribeContentsBody(parcelableProperties)
|
||||
}
|
||||
ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL.methodName -> {
|
||||
function.apply {
|
||||
val receiverParameter = dispatchReceiverParameter!!
|
||||
val (parcelParameter, flagsParameter) = function.valueParameters
|
||||
|
||||
// We need to defer the construction of the writer, since it may refer to the [writeToParcel] methods in other
|
||||
// @Parcelize classes in the current module, which might not be constructed yet at this point.
|
||||
defer {
|
||||
generateWriteToParcelBody(
|
||||
declaration,
|
||||
parcelerObject,
|
||||
parcelableProperties,
|
||||
receiverParameter,
|
||||
parcelParameter,
|
||||
flagsParameter
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> error("Generated declaration with unknown name: ${function.render()}")
|
||||
}
|
||||
}
|
||||
|
||||
generateCreator(declaration, parcelerObject, parcelableProperties)
|
||||
}
|
||||
}
|
||||
|
||||
+207
@@ -0,0 +1,207 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.IrStatement
|
||||
import org.jetbrains.kotlin.ir.ObsoleteDescriptorBasedAPI
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addFunction
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.addValueParameter
|
||||
import org.jetbrains.kotlin.ir.declarations.DescriptorMetadataSource
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.IrFunctionReference
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrCallImpl
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrFunctionReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.companionObject
|
||||
import org.jetbrains.kotlin.ir.util.copyTypeAndValueArgumentsFrom
|
||||
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
|
||||
import org.jetbrains.kotlin.ir.util.functions
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementTransformerVoid
|
||||
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.DESCRIBE_CONTENTS_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.FLAGS_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELABLE_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeSyntheticComponent
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
@OptIn(ObsoleteDescriptorBasedAPI::class)
|
||||
class ParcelizeIrTransformer(
|
||||
context: IrPluginContext,
|
||||
androidSymbols: AndroidSymbols
|
||||
) : ParcelizeIrTransformerBase(context, androidSymbols) {
|
||||
private val symbolMap = mutableMapOf<IrSimpleFunctionSymbol, IrSimpleFunctionSymbol>()
|
||||
|
||||
fun transform(moduleFragment: IrModuleFragment) {
|
||||
moduleFragment.accept(this, null)
|
||||
deferredOperations.forEach { it() }
|
||||
|
||||
// Remap broken stubs, which psi2ir generates for the synthetic descriptors coming from the ParcelizeResolveExtension.
|
||||
// Replace the `parcelableCreator` intrinsic with a direct field access.
|
||||
moduleFragment.transformChildrenVoid(object : IrElementTransformerVoid() {
|
||||
override fun visitCall(expression: IrCall): IrExpression {
|
||||
// Handle the `parcelableCreator` intrinsic
|
||||
val callee = expression.symbol.owner
|
||||
if (
|
||||
callee.dispatchReceiverParameter == null
|
||||
&& callee.extensionReceiverParameter == null
|
||||
&& callee.valueParameters.isEmpty()
|
||||
&& callee.isInline
|
||||
&& callee.fqNameWhenAvailable?.asString() == "kotlinx.parcelize.ParcelableCreatorKt.parcelableCreator"
|
||||
&& callee.typeParameters.singleOrNull()?.let {
|
||||
it.isReified && it.superTypes.singleOrNull()?.classFqName == PARCELABLE_FQN
|
||||
} == true
|
||||
) {
|
||||
expression.getTypeArgument(0)?.getClass()?.let { parcelableClass ->
|
||||
androidSymbols.createBuilder(expression.symbol).apply {
|
||||
return getParcelableCreator(parcelableClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remap calls to `describeContents` and `writeToParcel`
|
||||
val remappedSymbol = symbolMap[expression.symbol]
|
||||
?: return super.visitCall(expression)
|
||||
return IrCallImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type, remappedSymbol,
|
||||
expression.typeArgumentsCount, expression.valueArgumentsCount, expression.origin,
|
||||
expression.superQualifierSymbol
|
||||
).apply {
|
||||
copyTypeAndValueArgumentsFrom(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitFunctionReference(expression: IrFunctionReference): IrExpression {
|
||||
val remappedSymbol = symbolMap[expression.symbol]
|
||||
val remappedReflectionTarget = expression.reflectionTarget?.let { symbolMap[it] }
|
||||
if (remappedSymbol == null && remappedReflectionTarget == null)
|
||||
return super.visitFunctionReference(expression)
|
||||
|
||||
return IrFunctionReferenceImpl(
|
||||
expression.startOffset, expression.endOffset, expression.type, remappedSymbol ?: expression.symbol,
|
||||
expression.typeArgumentsCount, expression.valueArgumentsCount, remappedReflectionTarget,
|
||||
expression.origin
|
||||
).apply {
|
||||
copyTypeAndValueArgumentsFrom(expression)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visitSimpleFunction(declaration: IrSimpleFunction): IrStatement {
|
||||
// Remap overridden symbols, otherwise the code might break in BridgeLowering
|
||||
declaration.overriddenSymbols = declaration.overriddenSymbols.map { symbol ->
|
||||
symbolMap[symbol] ?: symbol
|
||||
}
|
||||
return super.visitSimpleFunction(declaration)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun visitElement(element: IrElement) = element.acceptChildren(this, null)
|
||||
|
||||
override fun visitClass(declaration: IrClass) {
|
||||
declaration.acceptChildren(this, null)
|
||||
|
||||
// Sealed classes can be annotated with `@Parcelize`, but that only implies that we
|
||||
// should process their immediate subclasses.
|
||||
if (!declaration.isParcelize || declaration.modality == Modality.SEALED)
|
||||
return
|
||||
|
||||
val parcelableProperties = declaration.parcelableProperties
|
||||
|
||||
// If the companion extends Parceler, it can override parts of the generated implementation.
|
||||
val parcelerObject = declaration.companionObject()?.takeIf {
|
||||
it.isSubclassOfFqName(PARCELER_FQN.asString())
|
||||
}
|
||||
|
||||
if (declaration.descriptor.hasSyntheticDescribeContents()) {
|
||||
val describeContents = declaration.addOverride(
|
||||
PARCELABLE_FQN,
|
||||
DESCRIBE_CONTENTS_NAME.identifier,
|
||||
context.irBuiltIns.intType,
|
||||
modality = Modality.OPEN
|
||||
).apply {
|
||||
generateDescribeContentsBody(parcelableProperties)
|
||||
|
||||
metadata = DescriptorMetadataSource.Function(
|
||||
declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS)!!
|
||||
)
|
||||
}
|
||||
|
||||
declaration.functions.find {
|
||||
it.descriptor.safeAs<ParcelizeSyntheticComponent>()?.componentKind == ParcelizeSyntheticComponent.ComponentKind.DESCRIBE_CONTENTS
|
||||
}?.let { stub ->
|
||||
symbolMap[stub.symbol] = describeContents.symbol
|
||||
declaration.declarations.remove(stub)
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.descriptor.hasSyntheticWriteToParcel()) {
|
||||
val writeToParcel = declaration.addOverride(
|
||||
PARCELABLE_FQN,
|
||||
WRITE_TO_PARCEL_NAME.identifier,
|
||||
context.irBuiltIns.unitType,
|
||||
modality = Modality.OPEN
|
||||
).apply {
|
||||
val receiverParameter = dispatchReceiverParameter!!
|
||||
val parcelParameter = addValueParameter("out", androidSymbols.androidOsParcel.defaultType)
|
||||
val flagsParameter = addValueParameter(FLAGS_NAME, context.irBuiltIns.intType)
|
||||
|
||||
// We need to defer the construction of the writer, since it may refer to the [writeToParcel] methods in other
|
||||
// @Parcelize classes in the current module, which might not be constructed yet at this point.
|
||||
defer {
|
||||
generateWriteToParcelBody(
|
||||
declaration,
|
||||
parcelerObject,
|
||||
parcelableProperties,
|
||||
receiverParameter,
|
||||
parcelParameter,
|
||||
flagsParameter
|
||||
)
|
||||
}
|
||||
|
||||
metadata = DescriptorMetadataSource.Function(
|
||||
declaration.descriptor.findFunction(ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL)!!
|
||||
)
|
||||
}
|
||||
|
||||
declaration.functions.find {
|
||||
it.descriptor.safeAs<ParcelizeSyntheticComponent>()?.componentKind == ParcelizeSyntheticComponent.ComponentKind.WRITE_TO_PARCEL
|
||||
}?.let { stub ->
|
||||
symbolMap[stub.symbol] = writeToParcel.symbol
|
||||
declaration.declarations.remove(stub)
|
||||
}
|
||||
}
|
||||
|
||||
if (!declaration.descriptor.hasCreatorField()) {
|
||||
generateCreator(declaration, parcelerObject, parcelableProperties)
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrClass.addOverride(
|
||||
baseFqName: FqName,
|
||||
name: String,
|
||||
returnType: IrType,
|
||||
modality: Modality = Modality.FINAL
|
||||
): IrSimpleFunction = addFunction(name, returnType, modality).apply {
|
||||
overriddenSymbols = superTypes.mapNotNull { superType ->
|
||||
superType.classOrNull?.owner?.takeIf { superClass -> superClass.isSubclassOfFqName(baseFqName.asString()) }
|
||||
}.flatMap { superClass ->
|
||||
superClass.functions.filter { function ->
|
||||
function.name.asString() == name && function.overridesFunctionIn(baseFqName)
|
||||
}.map { it.symbol }.toList()
|
||||
}
|
||||
}
|
||||
}
|
||||
+208
@@ -0,0 +1,208 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
|
||||
import org.jetbrains.kotlin.backend.common.lower.DeclarationIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.IGNORED_ON_PARCEL_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase
|
||||
|
||||
abstract class ParcelizeIrTransformerBase(
|
||||
protected val context: IrPluginContext,
|
||||
protected val androidSymbols: AndroidSymbols
|
||||
) : ParcelizeExtensionBase, IrElementVisitorVoid {
|
||||
private val irFactory: IrFactory = IrFactoryImpl
|
||||
|
||||
protected val deferredOperations = mutableListOf<() -> Unit>()
|
||||
protected fun defer(block: () -> Unit) = deferredOperations.add(block)
|
||||
|
||||
protected fun IrSimpleFunction.generateDescribeContentsBody(parcelableProperties: List<ParcelableProperty?>) {
|
||||
val flags = if (parcelableProperties.any { it != null && it.field.type.containsFileDescriptors }) 1 else 0
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irInt(flags))
|
||||
}
|
||||
}
|
||||
|
||||
protected fun IrSimpleFunction.generateWriteToParcelBody(
|
||||
irClass: IrClass,
|
||||
parcelerObject: IrClass?,
|
||||
parcelableProperties: List<ParcelableProperty?>,
|
||||
receiverParameter: IrValueParameter,
|
||||
parcelParameter: IrValueParameter,
|
||||
flagsParameter: IrValueParameter
|
||||
) {
|
||||
body = androidSymbols.createBuilder(symbol).run {
|
||||
irBlockBody {
|
||||
when {
|
||||
parcelerObject != null ->
|
||||
+parcelerWrite(parcelerObject, parcelParameter, flagsParameter, irGet(receiverParameter))
|
||||
|
||||
parcelableProperties.isNotEmpty() ->
|
||||
for (property in parcelableProperties) {
|
||||
if (property != null) {
|
||||
+writeParcelWith(
|
||||
property.parceler,
|
||||
parcelParameter,
|
||||
flagsParameter,
|
||||
irGetField(irGet(receiverParameter), property.field)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
+writeParcelWith(
|
||||
irClass.classParceler,
|
||||
parcelParameter,
|
||||
flagsParameter,
|
||||
irGet(receiverParameter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected fun generateCreator(declaration: IrClass, parcelerObject: IrClass?, parcelableProperties: List<ParcelableProperty?>) {
|
||||
// Since the `CREATOR` object cannot refer to the type parameters of the parcelable class we use a star projected type
|
||||
val declarationType = declaration.symbol.starProjectedType
|
||||
val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declarationType)
|
||||
|
||||
declaration.addField {
|
||||
name = CREATOR_NAME
|
||||
type = creatorType
|
||||
isStatic = true
|
||||
isFinal = true
|
||||
}.apply {
|
||||
val irField = this
|
||||
val creatorClass = irFactory.buildClass {
|
||||
name = Name.identifier("Creator")
|
||||
visibility = DescriptorVisibilities.LOCAL
|
||||
}.apply {
|
||||
parent = irField
|
||||
superTypes = listOf(creatorType)
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
|
||||
addConstructor {
|
||||
isPrimary = true
|
||||
}.apply {
|
||||
body = context.createIrBuilder(symbol).irBlockBody {
|
||||
+irDelegatingConstructorCall(context.irBuiltIns.anyClass.owner.constructors.single())
|
||||
}
|
||||
}
|
||||
|
||||
val arrayType = context.irBuiltIns.arrayClass.typeWith(declarationType.makeNullable())
|
||||
addFunction(NEW_ARRAY_NAME.identifier, arrayType).apply {
|
||||
overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!)
|
||||
val sizeParameter = addValueParameter("size", context.irBuiltIns.intType)
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
irExprBody(
|
||||
parcelerNewArray(parcelerObject, sizeParameter)
|
||||
?: irCall(context.irBuiltIns.arrayOfNulls, arrayType).apply {
|
||||
putTypeArgument(0, arrayType)
|
||||
putValueArgument(0, irGet(sizeParameter))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
addFunction(CREATE_FROM_PARCEL_NAME.identifier, declarationType).apply {
|
||||
overriddenSymbols = listOf(androidSymbols.androidOsParcelableCreator.getSimpleFunction(name.asString())!!)
|
||||
val parcelParameter = addValueParameter("parcel", androidSymbols.androidOsParcel.defaultType)
|
||||
|
||||
// We need to defer the construction of the create method, since it may refer to the [Parcelable.Creator]
|
||||
// instances in other @Parcelize classes in the current module, which may not exist yet.
|
||||
defer {
|
||||
body = androidSymbols.createBuilder(symbol).run {
|
||||
irExprBody(
|
||||
when {
|
||||
parcelerObject != null ->
|
||||
parcelerCreate(parcelerObject, parcelParameter)
|
||||
|
||||
parcelableProperties.isNotEmpty() ->
|
||||
irCall(declaration.primaryConstructor!!).apply {
|
||||
for ((index, property) in parcelableProperties.withIndex()) {
|
||||
if (property != null) {
|
||||
putValueArgument(index, readParcelWith(property.parceler, parcelParameter))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
readParcelWith(declaration.classParceler, parcelParameter)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializer = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irBlock {
|
||||
+creatorClass
|
||||
+irCall(creatorClass.primaryConstructor!!)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val IrClass.classParceler: IrParcelSerializer
|
||||
get() = if (kind == ClassKind.CLASS) {
|
||||
IrNoParameterClassParcelSerializer(this)
|
||||
} else {
|
||||
serializerFactory.get(defaultType, parcelizeType = defaultType, strict = true, toplevel = true, scope = getParcelerScope())
|
||||
}
|
||||
|
||||
protected class ParcelableProperty(val field: IrField, parcelerThunk: () -> IrParcelSerializer) {
|
||||
val parceler by lazy(parcelerThunk)
|
||||
}
|
||||
|
||||
private val serializerFactory = IrParcelSerializerFactory(androidSymbols)
|
||||
|
||||
protected val IrClass.parcelableProperties: List<ParcelableProperty?>
|
||||
get() {
|
||||
if (kind != ClassKind.CLASS) return emptyList()
|
||||
|
||||
val constructor = primaryConstructor ?: return emptyList()
|
||||
val topLevelScope = getParcelerScope()
|
||||
|
||||
return constructor.valueParameters.map { parameter ->
|
||||
val property = properties.firstOrNull { it.name == parameter.name }
|
||||
if (property == null || property.hasAnyAnnotation(IGNORED_ON_PARCEL_FQ_NAMES)) {
|
||||
null
|
||||
} else {
|
||||
val localScope = property.getParcelerScope(topLevelScope)
|
||||
ParcelableProperty(property.backingField!!) {
|
||||
serializerFactory.get(parameter.type, parcelizeType = defaultType, scope = localScope)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// *Heuristic* to determine if a Parcelable contains file descriptors.
|
||||
private val IrType.containsFileDescriptors: Boolean
|
||||
get() = erasedUpperBound.fqNameWhenAvailable == ParcelizeExtensionBase.FILE_DESCRIPTOR_FQNAME ||
|
||||
(this as? IrSimpleType)?.arguments?.any { argument ->
|
||||
argument.typeOrNull?.containsFileDescriptors == true
|
||||
} == true
|
||||
|
||||
private fun IrPluginContext.createIrBuilder(symbol: IrSymbol): DeclarationIrBuilder {
|
||||
return DeclarationIrBuilder(this, symbol, symbol.owner.startOffset, symbol.owner.endOffset)
|
||||
}
|
||||
}
|
||||
+234
@@ -0,0 +1,234 @@
|
||||
/*
|
||||
* 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.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.DescriptorVisibilities
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrConstructorCall
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.expressions.impl.IrClassReferenceImpl
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSimpleFunctionSymbol
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrFieldSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.NEW_ARRAY_NAME
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELABLE_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELER_FQN
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.PARCELIZE_CLASS_FQ_NAMES
|
||||
import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME
|
||||
import org.jetbrains.kotlin.parcelize.serializers.ParcelizeExtensionBase
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
|
||||
// true if the class should be processed by the parcelize plugin
|
||||
val IrClass.isParcelize: Boolean
|
||||
get() = kind in ParcelizeExtensionBase.ALLOWED_CLASS_KINDS &&
|
||||
(hasAnyAnnotation(PARCELIZE_CLASS_FQ_NAMES) || superTypes.any { superType ->
|
||||
superType.classOrNull?.owner?.let {
|
||||
it.modality == Modality.SEALED && it.hasAnyAnnotation(PARCELIZE_CLASS_FQ_NAMES)
|
||||
} == true
|
||||
})
|
||||
|
||||
// Finds the getter for a pre-existing CREATOR field on the class companion, which is used for manual Parcelable implementations in Kotlin.
|
||||
val IrClass.creatorGetter: IrSimpleFunctionSymbol?
|
||||
get() = companionObject()?.getPropertyGetter(CREATOR_NAME.asString())?.takeIf {
|
||||
it.owner.correspondingPropertySymbol?.owner?.backingField?.hasAnnotation(FqName("kotlin.jvm.JvmField")) == true
|
||||
}
|
||||
|
||||
// true if the class has a static CREATOR field
|
||||
val IrClass.hasCreatorField: Boolean
|
||||
get() = fields.any { field -> field.name == CREATOR_NAME } || creatorGetter != null
|
||||
|
||||
// object P : Parceler<T> { fun T.write(parcel: Parcel, flags: Int) ...}
|
||||
fun IrBuilderWithScope.parcelerWrite(
|
||||
parceler: IrClass,
|
||||
parcel: IrValueDeclaration,
|
||||
flags: IrValueDeclaration,
|
||||
value: IrExpression,
|
||||
) = irCall(parceler.parcelerSymbolByName("write")!!).apply {
|
||||
dispatchReceiver = irGetObject(parceler.symbol)
|
||||
extensionReceiver = value
|
||||
putValueArgument(0, irGet(parcel))
|
||||
putValueArgument(1, irGet(flags))
|
||||
}
|
||||
|
||||
// object P : Parceler<T> { fun create(parcel: Parcel): T }
|
||||
fun IrBuilderWithScope.parcelerCreate(parceler: IrClass, parcel: IrValueDeclaration): IrExpression =
|
||||
irCall(parceler.parcelerSymbolByName("create")!!).apply {
|
||||
dispatchReceiver = irGetObject(parceler.symbol)
|
||||
putValueArgument(0, irGet(parcel))
|
||||
}
|
||||
|
||||
// object P: Parceler<T> { fun newArray(size: Int): Array<T> }
|
||||
fun IrBuilderWithScope.parcelerNewArray(parceler: IrClass?, size: IrValueDeclaration): IrExpression? =
|
||||
parceler?.parcelerSymbolByName(NEW_ARRAY_NAME.identifier)?.takeIf {
|
||||
// The `newArray` method in `kotlinx.parcelize.Parceler` is stubbed out and we
|
||||
// have to produce a new implementation, unless the user overrides it.
|
||||
!it.owner.isFakeOverride || it.owner.resolveFakeOverride()?.parentClassOrNull?.fqNameWhenAvailable != PARCELER_FQN
|
||||
}?.let { newArraySymbol ->
|
||||
irCall(newArraySymbol).apply {
|
||||
dispatchReceiver = irGetObject(parceler.symbol)
|
||||
putValueArgument(0, irGet(size))
|
||||
}
|
||||
}
|
||||
|
||||
// class Parcelable { fun writeToParcel(parcel: Parcel, flags: Int) ...}
|
||||
fun IrBuilderWithScope.parcelableWriteToParcel(
|
||||
parcelableClass: IrClass,
|
||||
parcelable: IrExpression,
|
||||
parcel: IrExpression,
|
||||
flags: IrExpression
|
||||
): IrExpression {
|
||||
val writeToParcel = parcelableClass.functions.first { function ->
|
||||
function.name == WRITE_TO_PARCEL_NAME && function.overridesFunctionIn(PARCELABLE_FQN)
|
||||
}
|
||||
|
||||
return irCall(writeToParcel).apply {
|
||||
dispatchReceiver = parcelable
|
||||
putValueArgument(0, parcel)
|
||||
putValueArgument(1, flags)
|
||||
}
|
||||
}
|
||||
|
||||
// class C : Parcelable.Creator<T> { fun createFromParcel(parcel: Parcel): T ...}
|
||||
fun IrBuilderWithScope.parcelableCreatorCreateFromParcel(creator: IrExpression, parcel: IrExpression): IrExpression {
|
||||
val createFromParcel = creator.type.getClass()!!.functions.first { function ->
|
||||
function.name == CREATE_FROM_PARCEL_NAME && function.overridesFunctionIn(CREATOR_FQN)
|
||||
}
|
||||
|
||||
return irCall(createFromParcel).apply {
|
||||
dispatchReceiver = creator
|
||||
putValueArgument(0, parcel)
|
||||
}
|
||||
}
|
||||
|
||||
// Construct an expression to access the parcelable creator field in the given class.
|
||||
fun AndroidIrBuilder.getParcelableCreator(irClass: IrClass): IrExpression {
|
||||
// For classes annotated with `Parcelize` in the same module we can
|
||||
// use the creator field directly, since we add it ourselves.
|
||||
irClass.fields.find { it.name == CREATOR_NAME }?.let { creatorField ->
|
||||
return irGetField(null, creatorField)
|
||||
}
|
||||
|
||||
// For parcelable classes which do not use `Parcelize`, the creator field
|
||||
// will be present as a `JvmField` on the companion object.
|
||||
irClass.creatorGetter?.let { getter ->
|
||||
return irCall(getter).apply {
|
||||
dispatchReceiver = irGetObject(irClass.companionObject()!!.symbol)
|
||||
}
|
||||
}
|
||||
|
||||
// For classes in different modules, the creator field may not exist,
|
||||
// since static fields are not present in the Kotlin metadata.
|
||||
// As a workaround we create a synthetic field here together with a
|
||||
// field access.
|
||||
val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(irClass.symbol.starProjectedType)
|
||||
val creatorField = irClass.factory.createField(
|
||||
UNDEFINED_OFFSET, UNDEFINED_OFFSET, IrDeclarationOrigin.IR_EXTERNAL_DECLARATION_STUB,
|
||||
IrFieldSymbolImpl(), CREATOR_NAME, creatorType, DescriptorVisibilities.PUBLIC,
|
||||
isFinal = true, isExternal = false, isStatic = true,
|
||||
).also { it.parent = irClass }
|
||||
|
||||
return irGetField(null, creatorField)
|
||||
}
|
||||
|
||||
// Find a named function declaration which overrides the corresponding function in [Parceler].
|
||||
// This is more reliable than trying to match the functions signature ourselves, since the frontend
|
||||
// has already done the work.
|
||||
private fun IrClass.parcelerSymbolByName(name: String): IrSimpleFunctionSymbol? =
|
||||
functions.firstOrNull { function ->
|
||||
function.name.asString() == name && function.overridesFunctionIn(PARCELER_FQN)
|
||||
}?.symbol
|
||||
|
||||
fun IrSimpleFunction.overridesFunctionIn(fqName: FqName): Boolean =
|
||||
parentClassOrNull?.fqNameWhenAvailable == fqName || allOverridden().any { it.parentClassOrNull?.fqNameWhenAvailable == fqName }
|
||||
|
||||
private fun IrBuilderWithScope.kClassReference(classType: IrType): IrClassReferenceImpl =
|
||||
IrClassReferenceImpl(
|
||||
startOffset, endOffset, context.irBuiltIns.kClassClass.starProjectedType, context.irBuiltIns.kClassClass, classType
|
||||
)
|
||||
|
||||
private fun AndroidIrBuilder.kClassToJavaClass(kClassReference: IrExpression): IrCall =
|
||||
irGet(androidSymbols.javaLangClass.starProjectedType, null, androidSymbols.kotlinKClassJava.owner.getter!!.symbol).apply {
|
||||
extensionReceiver = kClassReference
|
||||
}
|
||||
|
||||
// Produce a static reference to the java class of the given type.
|
||||
fun AndroidIrBuilder.javaClassReference(classType: IrType): IrCall = kClassToJavaClass(kClassReference(classType))
|
||||
|
||||
fun IrClass.isSubclassOfFqName(fqName: String): Boolean =
|
||||
fqNameWhenAvailable?.asString() == fqName || superTypes.any { it.erasedUpperBound.isSubclassOfFqName(fqName) }
|
||||
|
||||
inline fun IrBlockBuilder.forUntil(upperBound: IrExpression, loopBody: IrBlockBuilder.(IrValueDeclaration) -> Unit) {
|
||||
val indexTemporary = irTemporary(irInt(0), isMutable = true)
|
||||
+irWhile().apply {
|
||||
condition = irNotEquals(irGet(indexTemporary), upperBound)
|
||||
body = irBlock {
|
||||
loopBody(indexTemporary)
|
||||
val inc = context.irBuiltIns.intClass.getSimpleFunction("inc")!!
|
||||
+irSet(indexTemporary.symbol, irCall(inc).apply {
|
||||
dispatchReceiver = irGet(indexTemporary)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun IrTypeArgument.upperBound(builtIns: IrBuiltIns): IrType =
|
||||
when (this) {
|
||||
is IrStarProjection -> builtIns.anyNType
|
||||
is IrTypeProjection -> {
|
||||
if (variance == Variance.OUT_VARIANCE || variance == Variance.INVARIANT)
|
||||
type
|
||||
else
|
||||
builtIns.anyNType
|
||||
}
|
||||
else -> error("Unknown type argument: ${render()}")
|
||||
}
|
||||
|
||||
private fun IrClass.getSimpleFunction(name: String): IrSimpleFunctionSymbol? =
|
||||
findDeclaration<IrSimpleFunction> { it.name.asString() == name }?.symbol
|
||||
|
||||
// This is a version of getPropertyGetter which does not throw when applied to broken lazy classes, such as java.util.HashMap,
|
||||
// which contains two "size" properties with different visibilities.
|
||||
fun IrClass.getPropertyGetter(name: String): IrSimpleFunctionSymbol? =
|
||||
declarations.filterIsInstance<IrProperty>().firstOrNull { it.name.asString() == name && it.getter != null }?.getter?.symbol
|
||||
?: getSimpleFunction("<get-$name>")
|
||||
|
||||
fun IrClass.getMethodWithoutArguments(name: String): IrSimpleFunction =
|
||||
functions.first { function ->
|
||||
function.name.asString() == name && function.dispatchReceiverParameter != null
|
||||
&& function.extensionReceiverParameter == null && function.valueParameters.isEmpty()
|
||||
}
|
||||
|
||||
internal fun IrAnnotationContainer.hasAnyAnnotation(fqNames: List<FqName>): Boolean {
|
||||
for (fqName in fqNames) {
|
||||
if (hasAnnotation(fqName)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
internal fun IrAnnotationContainer.getAnyAnnotation(fqNames: List<FqName>): IrConstructorCall? {
|
||||
for (fqName in fqNames) {
|
||||
val annotation = getAnnotation(fqName)
|
||||
if (annotation != null) {
|
||||
return annotation
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
Reference in New Issue
Block a user