Parcelize: Implement support for the JVM IR backend.
This commit is contained in:
committed by
Alexander Udalov
parent
7cad2be329
commit
779133e71e
@@ -18,6 +18,7 @@ dependencies {
|
||||
compileOnly(project(":compiler:frontend.java"))
|
||||
compileOnly(project(":compiler:backend"))
|
||||
compileOnly(project(":compiler:ir.backend.common"))
|
||||
compileOnly(project(":compiler:backend.jvm"))
|
||||
compileOnly(project(":kotlin-android-extensions-runtime"))
|
||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||
compileOnly(intellijDep()) { includeJars("asm-all", rootProject = rootProject) }
|
||||
@@ -25,6 +26,7 @@ dependencies {
|
||||
testCompile(project(":compiler:util"))
|
||||
testCompile(project(":compiler:backend"))
|
||||
testCompile(project(":compiler:ir.backend.common"))
|
||||
testCompile(project(":compiler:backend.jvm"))
|
||||
testCompile(project(":compiler:cli"))
|
||||
testCompile(project(":kotlin-android-extensions-runtime"))
|
||||
testCompile(projectTests(":compiler:tests-common"))
|
||||
|
||||
+1
-5
@@ -9,6 +9,7 @@ import kotlinx.android.parcel.TypeParceler
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension.Companion.createMethod
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.*
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelableExtensionBase.Companion.FILE_DESCRIPTOR_FQNAME
|
||||
import org.jetbrains.kotlin.builtins.KotlinBuiltIns
|
||||
import org.jetbrains.kotlin.codegen.*
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
@@ -44,11 +45,6 @@ import org.jetbrains.org.objectweb.asm.Type
|
||||
import java.io.FileDescriptor
|
||||
|
||||
open class ParcelableCodegenExtension : ParcelableExtensionBase, ExpressionCodegenExtension {
|
||||
|
||||
companion object {
|
||||
private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
||||
}
|
||||
|
||||
@Deprecated(
|
||||
"@Parcelize is now available in non-experimental setups as well.",
|
||||
replaceWith = ReplaceWith("true"),
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.android.parcel
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.ir.AndroidSymbols
|
||||
import org.jetbrains.kotlin.android.parcel.ir.ParcelableIrTransformer
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.extensions.PureIrGenerationExtension
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
|
||||
class ParcelableIrGeneratorExtension : PureIrGenerationExtension {
|
||||
override fun generate(moduleFragment: IrModuleFragment, context: CommonBackendContext) {
|
||||
ParcelableIrTransformer(context, AndroidSymbols(context, moduleFragment)).transform(moduleFragment)
|
||||
}
|
||||
}
|
||||
+89
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
// This file is autogenerated based on android.jar, do not edit it directly.
|
||||
package org.jetbrains.kotlin.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.expressions.IrExpression
|
||||
import org.jetbrains.kotlin.ir.symbols.IrSymbol
|
||||
|
||||
// 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 =
|
||||
irCall(androidSymbols.parcelReadInt).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
|
||||
fun parcelReadParcelable(receiver: IrExpression, loader: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.parcelReadParcelable).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, loader)
|
||||
}
|
||||
|
||||
fun parcelReadString(receiver: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.parcelReadString).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
|
||||
fun parcelReadValue(receiver: IrExpression, loader: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.parcelReadValue).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, loader)
|
||||
}
|
||||
|
||||
fun parcelWriteInt(receiver: IrExpression, value: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.parcelWriteInt).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
|
||||
fun parcelWriteParcelable(
|
||||
receiver: IrExpression,
|
||||
p: IrExpression,
|
||||
parcelableFlags: IrExpression
|
||||
): IrExpression = irCall(androidSymbols.parcelWriteParcelable).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, p)
|
||||
putValueArgument(1, parcelableFlags)
|
||||
}
|
||||
|
||||
fun parcelWriteString(receiver: IrExpression, value: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.parcelWriteString).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
|
||||
fun parcelWriteValue(receiver: IrExpression, v: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.parcelWriteValue).apply {
|
||||
dispatchReceiver = receiver
|
||||
putValueArgument(0, v)
|
||||
}
|
||||
|
||||
fun textUtilsWriteToParcel(
|
||||
cs: IrExpression,
|
||||
p: IrExpression,
|
||||
parcelableFlags: IrExpression
|
||||
): IrExpression = irCall(androidSymbols.textUtilsWriteToParcel).apply {
|
||||
putValueArgument(0, cs)
|
||||
putValueArgument(1, p)
|
||||
putValueArgument(2, parcelableFlags)
|
||||
}
|
||||
|
||||
fun classGetClassLoader(receiver: IrExpression): IrExpression =
|
||||
irCall(androidSymbols.classGetClassLoader).apply {
|
||||
dispatchReceiver = receiver
|
||||
}
|
||||
|
||||
fun getTextUtilsCharSequenceCreator(): IrExpression =
|
||||
irGetField(null, androidSymbols.textUtilsCharSequenceCreator.owner)
|
||||
}
|
||||
+477
@@ -0,0 +1,477 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.
|
||||
*/
|
||||
// This file was autogenerated based on android.jar, do not edit it directly.
|
||||
package org.jetbrains.kotlin.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.Symbols
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.impl.EmptyPackageFragmentDescriptor
|
||||
import org.jetbrains.kotlin.ir.UNDEFINED_OFFSET
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
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.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.symbols.*
|
||||
import org.jetbrains.kotlin.ir.symbols.impl.IrExternalPackageFragmentSymbolImpl
|
||||
import org.jetbrains.kotlin.ir.types.defaultType
|
||||
import org.jetbrains.kotlin.ir.types.makeNullable
|
||||
import org.jetbrains.kotlin.ir.types.starProjectedType
|
||||
import org.jetbrains.kotlin.ir.types.typeWith
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.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(
|
||||
context: CommonBackendContext,
|
||||
private val moduleFragment: IrModuleFragment
|
||||
) {
|
||||
val irBuiltIns: IrBuiltIns = context.irBuiltIns
|
||||
|
||||
val irSymbols: Symbols<CommonBackendContext> = context.ir.symbols
|
||||
|
||||
private val androidOs: IrPackageFragment = createPackage("android.os")
|
||||
|
||||
private val androidText: IrPackageFragment = createPackage("android.text")
|
||||
|
||||
private val androidUtil: IrPackageFragment = createPackage("android.util")
|
||||
|
||||
private val javaIo: IrPackageFragment = createPackage("java.io")
|
||||
|
||||
private val javaLang: IrPackageFragment = createPackage("java.lang")
|
||||
|
||||
private val javaUtil: IrPackageFragment = createPackage("java.util")
|
||||
|
||||
private val kotlinJvm: IrPackageFragment = createPackage("kotlin.jvm")
|
||||
|
||||
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 androidOsParcelableCreator: IrClassSymbol = buildClass {
|
||||
name = Name.identifier("Creator")
|
||||
kind = ClassKind.INTERFACE
|
||||
modality = Modality.ABSTRACT
|
||||
}.apply {
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
val t = addTypeParameter("T", context.irBuiltIns.anyNType)
|
||||
parent = androidOsParcelable.owner
|
||||
|
||||
addFunction("createFromParcel", t.defaultType, Modality.ABSTRACT).apply {
|
||||
addValueParameter("source", androidOsParcel.defaultType)
|
||||
}
|
||||
|
||||
addFunction(
|
||||
"newArray", irBuiltIns.arrayClass.typeWith(t.defaultType.makeNullable()),
|
||||
Modality.ABSTRACT
|
||||
).apply {
|
||||
addValueParameter("size", context.irBuiltIns.intType)
|
||||
}
|
||||
}.symbol
|
||||
|
||||
val kotlinKClassJava: IrPropertySymbol = 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).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).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("writeToParcel", irBuiltIns.unitType, isStatic = true).apply {
|
||||
addValueParameter("cs", irSymbols.charSequence.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
|
||||
|
||||
private fun createPackage(packageName: String): IrPackageFragment =
|
||||
IrExternalPackageFragmentImpl(
|
||||
IrExternalPackageFragmentSymbolImpl(
|
||||
EmptyPackageFragmentDescriptor(
|
||||
moduleFragment.descriptor,
|
||||
FqName(packageName)
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private fun createClass(
|
||||
irPackage: IrPackageFragment,
|
||||
shortName: String,
|
||||
classKind: ClassKind,
|
||||
classModality: Modality
|
||||
): IrClassSymbol = buildClass {
|
||||
name = Name.identifier(shortName)
|
||||
kind = classKind
|
||||
modality = classModality
|
||||
}.apply {
|
||||
parent = irPackage
|
||||
createImplicitParameterDeclarationWithWrappedDescriptor()
|
||||
}.symbol
|
||||
|
||||
fun createBuilder(
|
||||
symbol: IrSymbol,
|
||||
startOffset: Int = UNDEFINED_OFFSET,
|
||||
endOffset: Int = UNDEFINED_OFFSET
|
||||
) = AndroidIrBuilder(this, symbol, startOffset, endOffset)
|
||||
}
|
||||
+285
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.RAWVALUE_ANNOTATION_FQNAME
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.getArrayElementType
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.resolve.source.PsiSourceElement
|
||||
|
||||
class IrParcelSerializerFactory(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.hasAnnotation(RAWVALUE_ANNOTATION_FQNAME)
|
||||
|
||||
scope.getCustomSerializer(irType)?.let { parceler ->
|
||||
return IrCustomParcelSerializer(parceler)
|
||||
}
|
||||
|
||||
// TODO inline classes
|
||||
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 bundleSerializer
|
||||
"android.os.PersistableBundle" ->
|
||||
return persistableBundleSerializer
|
||||
|
||||
// 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)
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// 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 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, 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,
|
||||
get(keyType, scope, parcelizeType, strict()),
|
||||
get(valueType, scope, parcelizeType, strict())
|
||||
)
|
||||
return wrapNullableSerializerIfNeeded(irType, parceler)
|
||||
}
|
||||
}
|
||||
|
||||
// Generic parceleable 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.descriptor.source is PsiSourceElement
|
||||
&& (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") ->
|
||||
return serializableSerializer
|
||||
|
||||
strict() ->
|
||||
throw IllegalArgumentException("Illegal type, could not find a specific serializer for ${irType.render()}")
|
||||
|
||||
else ->
|
||||
return IrGenericValueParcelSerializer(parcelizeType)
|
||||
}
|
||||
}
|
||||
|
||||
private fun wrapNullableSerializerIfNeeded(irType: IrType, serializer: IrParcelSerializer) =
|
||||
if (irType.isNullable()) IrNullAwareParcelSerializer(serializer) else serializer
|
||||
|
||||
private val irBuiltIns: IrBuiltIns = 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 = IrWrappedPrimitiveParcelSerializer(irBuiltIns.booleanType, intSerializer)
|
||||
private val shortSerializer = IrWrappedPrimitiveParcelSerializer(irBuiltIns.shortType, intSerializer)
|
||||
private val charSerializer = IrWrappedPrimitiveParcelSerializer(irBuiltIns.charType, intSerializer)
|
||||
|
||||
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 bundleSerializer = IrSimpleParcelSerializer(symbols.parcelReadBundle, symbols.parcelWriteBundle)
|
||||
private val persistableBundleSerializer =
|
||||
IrSimpleParcelSerializer(symbols.parcelReadPersistableBundle, symbols.parcelWritePersistableBundle)
|
||||
private val sparseBooleanArraySerializer =
|
||||
IrSimpleParcelSerializer(symbols.parcelReadSparseBooleanArray, symbols.parcelWriteSparseBooleanArray)
|
||||
}
|
||||
+505
@@ -0,0 +1,505 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelableExtensionBase.Companion.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.backend.jvm.codegen.isJvmInterface
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.isBoxedArray
|
||||
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 =
|
||||
with(serializer) { readParcel(parcel) }
|
||||
|
||||
fun AndroidIrBuilder.writeParcelWith(
|
||||
serializer: IrParcelSerializer,
|
||||
parcel: IrValueDeclaration,
|
||||
flags: IrValueDeclaration,
|
||||
value: IrExpression
|
||||
): IrExpression =
|
||||
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 =
|
||||
irCall(reader).apply { dispatchReceiver = irGet(parcel) }
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
irCall(writer).apply {
|
||||
dispatchReceiver = irGet(parcel)
|
||||
putValueArgument(0, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize a value of the primitive type [parcelType] by wrapping the given [serializer] with casts.
|
||||
class IrWrappedPrimitiveParcelSerializer(private val parcelType: IrType, private val serializer: IrParcelSerializer) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val deserializedPrimitive = readParcelWith(serializer, parcel)
|
||||
return if (parcelType.isBoolean()) {
|
||||
irIfThenElse(
|
||||
parcelType,
|
||||
irNotEquals(deserializedPrimitive, irInt(0)),
|
||||
irInt(1),
|
||||
irInt(0)
|
||||
)
|
||||
} else {
|
||||
val conversion = deserializedPrimitive.type.getClass()!!.functions.first { function ->
|
||||
function.name.asString() == "to${parcelType.getClass()!!.name}"
|
||||
}
|
||||
irCall(conversion).apply {
|
||||
dispatchReceiver = deserializedPrimitive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun IrBuilderWithScope.irCastIfNeeded(expression: IrExpression, irType: IrType): IrExpression =
|
||||
if (expression.type == irType) expression else irAs(expression, irType)
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
writeParcelWith(serializer, parcel, flags, irCastIfNeeded(value, parcelType))
|
||||
}
|
||||
|
||||
// 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 =
|
||||
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(val objectClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression =
|
||||
// Avoid empty parcels
|
||||
irBlock {
|
||||
+parcelReadInt(irGet(parcel))
|
||||
+irGetObject(objectClass.symbol)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
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(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 =
|
||||
parcelWriteInt(irGet(parcel), irInt(1))
|
||||
}
|
||||
|
||||
// Parcel serializer for enum classes. Not null-safe.
|
||||
class IrEnumParcelSerializer(val enumClass: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression =
|
||||
irCall(enumValueOf).apply {
|
||||
putValueArgument(0, parcelReadString(irGet(parcel)))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
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 =
|
||||
parcelableCreatorCreateFromParcel(getTextUtilsCharSequenceCreator(), irGet(parcel))
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
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 {
|
||||
val creator: IrExpression = irClass.fields.find { it.name == CREATOR_NAME }?.let { creatorField ->
|
||||
irGetField(null, creatorField)
|
||||
} ?: irCall(irClass.creatorGetter!!).apply {
|
||||
dispatchReceiver = irGetObject((irClass.companionObject()!! as IrClass).symbol)
|
||||
}
|
||||
|
||||
return parcelableCreatorCreateFromParcel(creator, irGet(parcel))
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
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 =
|
||||
parcelReadParcelable(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
parcelWriteParcelable(irGet(parcel), value, irGet(flags))
|
||||
}
|
||||
|
||||
// Fallback parcel serializer for unknown types using Parcel.readValue/writeValue.
|
||||
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
|
||||
class IrGenericValueParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression =
|
||||
parcelReadValue(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
parcelWriteValue(irGet(parcel), value)
|
||||
}
|
||||
|
||||
// Parcel serializer using a custom Parceler object.
|
||||
class IrCustomParcelSerializer(private val parcelerObject: IrClass) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression =
|
||||
parcelerCreate(parcelerObject, parcel)
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
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.
|
||||
// TODO: Unsigned array types
|
||||
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)
|
||||
androidSymbols.irSymbols.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 =
|
||||
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 =
|
||||
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).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 =
|
||||
irBlock {
|
||||
val remainingSizeTemporary = irTemporaryVar(parcelReadInt(irGet(parcel)))
|
||||
|
||||
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")!!
|
||||
+irSetVar(remainingSizeTemporary.symbol, irCall(dec).apply {
|
||||
dispatchReceiver = irGet(remainingSizeTemporary)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
+irGet(arrayTemporary)
|
||||
}
|
||||
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression =
|
||||
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).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 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).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 capactiy 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 =
|
||||
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 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).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
+writeParcelWith(valueSerializer, parcel, flags, irCall(elementValue).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 capactiy 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 =
|
||||
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)
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableAnnotationChecker
|
||||
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.ir.util.getAnnotation
|
||||
|
||||
// Keep track of all custom parcelers which are currently in scope.
|
||||
// Note that custom parcelers are resolved in *reverse* lexical order.
|
||||
class IrParcelerScope(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? =
|
||||
irType.getAnnotation(ParcelableAnnotationChecker.WRITE_WITH_FQNAME)?.let { writeWith ->
|
||||
(writeWith.type as IrSimpleType).arguments.single().typeOrNull!!.getClass()!!
|
||||
} ?: this?.get(irType)
|
||||
|
||||
fun IrParcelerScope?.hasCustomSerializer(irType: IrType): Boolean =
|
||||
getCustomSerializer(irType) != null
|
||||
|
||||
fun IrAnnotationContainer.getParcelerScope(parent: IrParcelerScope? = null): IrParcelerScope? {
|
||||
val typeParcelerAnnotations = annotations.filterTo(mutableListOf()) {
|
||||
it.symbol.owner.constructedClass.fqNameWhenAvailable == ParcelableAnnotationChecker.TYPE_PARCELER_FQNAME
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
+241
@@ -0,0 +1,241 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.ANDROID_PARCELABLE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.android.parcel.PARCELER_FQNAME
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelableExtensionBase
|
||||
import org.jetbrains.kotlin.backend.common.CommonBackendContext
|
||||
import org.jetbrains.kotlin.backend.common.ir.createImplicitParameterDeclarationWithWrappedDescriptor
|
||||
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.descriptors.Modality
|
||||
import org.jetbrains.kotlin.descriptors.Visibilities
|
||||
import org.jetbrains.kotlin.ir.IrElement
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.builders.declarations.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrField
|
||||
import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
class ParcelableIrTransformer(private val context: CommonBackendContext, private val androidSymbols: AndroidSymbols) :
|
||||
ParcelableExtensionBase, IrElementVisitorVoid {
|
||||
private val serializerFactory = IrParcelSerializerFactory(androidSymbols)
|
||||
|
||||
private val deferredOperations = mutableListOf<() -> Unit>()
|
||||
private fun defer(block: () -> Unit) = deferredOperations.add(block)
|
||||
|
||||
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)
|
||||
if (!declaration.isParcelize)
|
||||
return
|
||||
|
||||
val parcelableProperties = declaration.parcelableProperties
|
||||
|
||||
// If the companion extends Parceler, it can override parts of the generated implementation.
|
||||
val parcelerObject = declaration.companionObject()?.safeAs<IrClass>()?.takeIf {
|
||||
it.isSubclassOfFqName(PARCELER_FQNAME.asString())
|
||||
}
|
||||
|
||||
if (declaration.descriptor.hasSyntheticDescribeContents()) {
|
||||
declaration.addOverride(
|
||||
ANDROID_PARCELABLE_CLASS_FQNAME,
|
||||
"describeContents",
|
||||
context.irBuiltIns.intType,
|
||||
modality = Modality.OPEN
|
||||
).apply {
|
||||
val flags = if (parcelableProperties.any { it.field.type.containsFileDescriptors }) 1 else 0
|
||||
body = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irInt(flags))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (declaration.descriptor.hasSyntheticWriteToParcel()) {
|
||||
declaration.addOverride(
|
||||
ANDROID_PARCELABLE_CLASS_FQNAME,
|
||||
"writeToParcel",
|
||||
context.irBuiltIns.unitType,
|
||||
modality = Modality.OPEN
|
||||
).apply {
|
||||
val receiverParameter = dispatchReceiverParameter!!
|
||||
val parcelParameter = addValueParameter("out", androidSymbols.androidOsParcel.defaultType)
|
||||
val flagsParameter = addValueParameter("flags", 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 {
|
||||
body = androidSymbols.createBuilder(symbol).run {
|
||||
irBlockBody {
|
||||
when {
|
||||
parcelerObject != null ->
|
||||
+parcelerWrite(parcelerObject, parcelParameter, flagsParameter, irGet(receiverParameter))
|
||||
|
||||
parcelableProperties.isNotEmpty() ->
|
||||
for (property in parcelableProperties) {
|
||||
+writeParcelWith(
|
||||
property.parceler,
|
||||
parcelParameter,
|
||||
flagsParameter,
|
||||
irGetField(irGet(receiverParameter), property.field)
|
||||
)
|
||||
}
|
||||
|
||||
else ->
|
||||
+writeParcelWith(
|
||||
declaration.classParceler,
|
||||
parcelParameter,
|
||||
flagsParameter,
|
||||
irGet(receiverParameter)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val creatorType = androidSymbols.androidOsParcelableCreator.typeWith(declaration.defaultType)
|
||||
|
||||
if (!declaration.descriptor.hasCreatorField()) {
|
||||
declaration.addField {
|
||||
name = ParcelableExtensionBase.CREATOR_NAME
|
||||
type = creatorType
|
||||
isStatic = true
|
||||
isFinal = true
|
||||
}.apply {
|
||||
val irField = this
|
||||
val creatorClass = buildClass {
|
||||
name = Name.identifier("Creator")
|
||||
visibility = Visibilities.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(declaration.defaultType.makeNullable())
|
||||
addFunction("newArray", 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(androidSymbols.irSymbols.arrayOfNulls, arrayType).apply {
|
||||
putTypeArgument(0, arrayType)
|
||||
putValueArgument(0, irGet(sizeParameter))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
addFunction("createFromParcel", declaration.defaultType).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()) {
|
||||
putValueArgument(index, readParcelWith(property.parceler, parcelParameter))
|
||||
}
|
||||
}
|
||||
|
||||
else ->
|
||||
readParcelWith(declaration.classParceler, parcelParameter)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
initializer = context.createIrBuilder(symbol).run {
|
||||
irExprBody(irBlock {
|
||||
+creatorClass
|
||||
+irCall(creatorClass.primaryConstructor!!)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
private data class ParcelableProperty(val field: IrField, val parceler: IrParcelSerializer)
|
||||
|
||||
private val IrClass.classParceler: IrParcelSerializer
|
||||
get() = if (kind == ClassKind.CLASS) {
|
||||
IrNoParameterClassParcelSerializer(this)
|
||||
} else {
|
||||
serializerFactory.get(defaultType, parcelizeType = defaultType, strict = true, toplevel = true, scope = getParcelerScope())
|
||||
}
|
||||
|
||||
private 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.first { it.name == parameter.name }
|
||||
val localScope = property.getParcelerScope(toplevelScope)
|
||||
val parceler = serializerFactory.get(parameter.type, parcelizeType = defaultType, strict = true, scope = localScope)
|
||||
ParcelableProperty(property.backingField!!, parceler)
|
||||
}
|
||||
}
|
||||
|
||||
// *Heuristic* to determine if a Parcelable contains file descriptors.
|
||||
private val IrType.containsFileDescriptors: Boolean
|
||||
get() = erasedUpperBound.fqNameWhenAvailable == ParcelableExtensionBase.FILE_DESCRIPTOR_FQNAME ||
|
||||
(this as? IrSimpleType)?.arguments?.any { argument ->
|
||||
argument.typeOrNull?.containsFileDescriptors == true
|
||||
} == true
|
||||
}
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
/*
|
||||
* Copyright 2010-2020 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.android.parcel.ir
|
||||
|
||||
import org.jetbrains.kotlin.android.parcel.ANDROID_PARCELABLE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.android.parcel.ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.android.parcel.PARCELER_FQNAME
|
||||
import org.jetbrains.kotlin.android.parcel.PARCELIZE_CLASS_FQNAME
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelableExtensionBase
|
||||
import org.jetbrains.kotlin.android.parcel.serializers.ParcelableExtensionBase.Companion.CREATOR_NAME
|
||||
import org.jetbrains.kotlin.backend.common.lower.allOverridden
|
||||
import org.jetbrains.kotlin.backend.jvm.ir.erasedUpperBound
|
||||
import org.jetbrains.kotlin.ir.builders.*
|
||||
import org.jetbrains.kotlin.ir.declarations.IrClass
|
||||
import org.jetbrains.kotlin.ir.declarations.IrProperty
|
||||
import org.jetbrains.kotlin.ir.declarations.IrSimpleFunction
|
||||
import org.jetbrains.kotlin.ir.declarations.IrValueDeclaration
|
||||
import org.jetbrains.kotlin.ir.descriptors.IrBuiltIns
|
||||
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.types.*
|
||||
import org.jetbrains.kotlin.ir.util.*
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import org.jetbrains.kotlin.types.Variance
|
||||
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
|
||||
|
||||
// true if the class should be processed by the parcelize plugin
|
||||
val IrClass.isParcelize: Boolean
|
||||
get() = kind in ParcelableExtensionBase.ALLOWED_CLASS_KINDS && hasAnnotation(PARCELIZE_CLASS_FQNAME)
|
||||
|
||||
// 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()?.safeAs<IrClass>()?.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("newArray")?.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.asString() == "writeToParcel" && function.overridesFunctionIn(ANDROID_PARCELABLE_CLASS_FQNAME)
|
||||
}
|
||||
|
||||
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.asString() == "createFromParcel" && function.overridesFunctionIn(ANDROID_PARCELABLE_CREATOR_CLASS_FQNAME)
|
||||
}
|
||||
|
||||
return irCall(createFromParcel).apply {
|
||||
dispatchReceiver = creator
|
||||
putValueArgument(0, parcel)
|
||||
}
|
||||
}
|
||||
|
||||
// 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.isFakeOverride && function.name.asString() == name && function.overridesFunctionIn(PARCELER_FQNAME)
|
||||
}?.symbol
|
||||
|
||||
fun IrSimpleFunction.overridesFunctionIn(fqName: FqName): Boolean =
|
||||
parentClassOrNull?.fqNameWhenAvailable == fqName || allOverridden().any { it.parentClassOrNull?.fqNameWhenAvailable == fqName }
|
||||
|
||||
private fun IrBuilderWithScope.kClassReference(classType: IrType) =
|
||||
IrClassReferenceImpl(
|
||||
startOffset, endOffset, context.irBuiltIns.kClassClass.starProjectedType, context.irBuiltIns.kClassClass, classType
|
||||
)
|
||||
|
||||
private fun AndroidIrBuilder.kClassToJavaClass(kClassReference: IrExpression) =
|
||||
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) =
|
||||
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 = irTemporaryVar(irInt(0))
|
||||
+irWhile().apply {
|
||||
condition = irNotEquals(irGet(indexTemporary), upperBound)
|
||||
body = irBlock {
|
||||
loopBody(indexTemporary)
|
||||
val inc = context.irBuiltIns.intClass.getSimpleFunction("inc")!!
|
||||
+irSetVar(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()
|
||||
}
|
||||
+1
-1
@@ -42,7 +42,7 @@ import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
private val RAWVALUE_ANNOTATION_FQNAME = FqName("kotlinx.android.parcel.RawValue")
|
||||
val RAWVALUE_ANNOTATION_FQNAME = FqName("kotlinx.android.parcel.RawValue")
|
||||
|
||||
internal typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
|
||||
|
||||
|
||||
+7
-2
@@ -5,6 +5,7 @@
|
||||
|
||||
package org.jetbrains.kotlin.android.parcel.serializers
|
||||
|
||||
import kotlinx.android.parcel.TypeParceler
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent
|
||||
import org.jetbrains.kotlin.android.parcel.isParcelize
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
@@ -13,13 +14,17 @@ import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.name.Name
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||
import org.jetbrains.kotlin.name.FqName
|
||||
import java.io.FileDescriptor
|
||||
|
||||
interface ParcelableExtensionBase {
|
||||
|
||||
companion object {
|
||||
private val CREATOR_NAME = Name.identifier("CREATOR")
|
||||
val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
||||
|
||||
private val ALLOWED_CLASS_KINDS = listOf(ClassKind.CLASS, ClassKind.OBJECT, ClassKind.ENUM_CLASS)
|
||||
val CREATOR_NAME = Name.identifier("CREATOR")
|
||||
|
||||
val ALLOWED_CLASS_KINDS = listOf(ClassKind.CLASS, ClassKind.OBJECT, ClassKind.ENUM_CLASS)
|
||||
}
|
||||
|
||||
fun ClassDescriptor.hasCreatorField(): Boolean {
|
||||
|
||||
+3
-4
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.android.synthetic
|
||||
import com.intellij.mock.MockProject
|
||||
import com.intellij.openapi.project.Project
|
||||
import kotlinx.android.extensions.CacheImplementation
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableAnnotationChecker
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableCodegenExtension
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableDeclarationChecker
|
||||
import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension
|
||||
import org.jetbrains.kotlin.android.parcel.*
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidIrExtension
|
||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
|
||||
@@ -33,6 +30,7 @@ import org.jetbrains.kotlin.android.synthetic.res.AndroidVariant
|
||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidLayoutXmlFileManager
|
||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidPackageFragmentProviderExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
||||
import org.jetbrains.kotlin.backend.common.extensions.PureIrGenerationExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ClassBuilderInterceptorExtension
|
||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||
import org.jetbrains.kotlin.compiler.plugin.*
|
||||
@@ -107,6 +105,7 @@ class AndroidComponentRegistrar : ComponentRegistrar {
|
||||
companion object {
|
||||
fun registerParcelExtensions(project: Project) {
|
||||
ExpressionCodegenExtension.registerExtension(project, ParcelableCodegenExtension())
|
||||
PureIrGenerationExtension.registerExtension(project, ParcelableIrGeneratorExtension())
|
||||
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
|
||||
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user