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:frontend.java"))
|
||||||
compileOnly(project(":compiler:backend"))
|
compileOnly(project(":compiler:backend"))
|
||||||
compileOnly(project(":compiler:ir.backend.common"))
|
compileOnly(project(":compiler:ir.backend.common"))
|
||||||
|
compileOnly(project(":compiler:backend.jvm"))
|
||||||
compileOnly(project(":kotlin-android-extensions-runtime"))
|
compileOnly(project(":kotlin-android-extensions-runtime"))
|
||||||
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
compileOnly(intellijCoreDep()) { includeJars("intellij-core") }
|
||||||
compileOnly(intellijDep()) { includeJars("asm-all", rootProject = rootProject) }
|
compileOnly(intellijDep()) { includeJars("asm-all", rootProject = rootProject) }
|
||||||
@@ -25,6 +26,7 @@ dependencies {
|
|||||||
testCompile(project(":compiler:util"))
|
testCompile(project(":compiler:util"))
|
||||||
testCompile(project(":compiler:backend"))
|
testCompile(project(":compiler:backend"))
|
||||||
testCompile(project(":compiler:ir.backend.common"))
|
testCompile(project(":compiler:ir.backend.common"))
|
||||||
|
testCompile(project(":compiler:backend.jvm"))
|
||||||
testCompile(project(":compiler:cli"))
|
testCompile(project(":compiler:cli"))
|
||||||
testCompile(project(":kotlin-android-extensions-runtime"))
|
testCompile(project(":kotlin-android-extensions-runtime"))
|
||||||
testCompile(projectTests(":compiler:tests-common"))
|
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.ParcelableResolveExtension.Companion.createMethod
|
||||||
import org.jetbrains.kotlin.android.parcel.serializers.*
|
import org.jetbrains.kotlin.android.parcel.serializers.*
|
||||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
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.builtins.KotlinBuiltIns
|
||||||
import org.jetbrains.kotlin.codegen.*
|
import org.jetbrains.kotlin.codegen.*
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||||
@@ -44,11 +45,6 @@ import org.jetbrains.org.objectweb.asm.Type
|
|||||||
import java.io.FileDescriptor
|
import java.io.FileDescriptor
|
||||||
|
|
||||||
open class ParcelableCodegenExtension : ParcelableExtensionBase, ExpressionCodegenExtension {
|
open class ParcelableCodegenExtension : ParcelableExtensionBase, ExpressionCodegenExtension {
|
||||||
|
|
||||||
companion object {
|
|
||||||
private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName)
|
|
||||||
}
|
|
||||||
|
|
||||||
@Deprecated(
|
@Deprecated(
|
||||||
"@Parcelize is now available in non-experimental setups as well.",
|
"@Parcelize is now available in non-experimental setups as well.",
|
||||||
replaceWith = ReplaceWith("true"),
|
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.*
|
||||||
import java.util.concurrent.ConcurrentHashMap
|
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>
|
internal typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
|
||||||
|
|
||||||
|
|||||||
+7
-2
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
package org.jetbrains.kotlin.android.parcel.serializers
|
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.ParcelableSyntheticComponent
|
||||||
import org.jetbrains.kotlin.android.parcel.isParcelize
|
import org.jetbrains.kotlin.android.parcel.isParcelize
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
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.name.Name
|
||||||
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.*
|
||||||
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.SimpleFunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
|
import java.io.FileDescriptor
|
||||||
|
|
||||||
interface ParcelableExtensionBase {
|
interface ParcelableExtensionBase {
|
||||||
|
|
||||||
companion object {
|
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 {
|
fun ClassDescriptor.hasCreatorField(): Boolean {
|
||||||
|
|||||||
+3
-4
@@ -19,10 +19,7 @@ package org.jetbrains.kotlin.android.synthetic
|
|||||||
import com.intellij.mock.MockProject
|
import com.intellij.mock.MockProject
|
||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import kotlinx.android.extensions.CacheImplementation
|
import kotlinx.android.extensions.CacheImplementation
|
||||||
import org.jetbrains.kotlin.android.parcel.ParcelableAnnotationChecker
|
import org.jetbrains.kotlin.android.parcel.*
|
||||||
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.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
|
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidExtensionsExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidIrExtension
|
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidIrExtension
|
||||||
import org.jetbrains.kotlin.android.synthetic.codegen.CliAndroidOnDestroyClassBuilderInterceptorExtension
|
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.CliAndroidLayoutXmlFileManager
|
||||||
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidPackageFragmentProviderExtension
|
import org.jetbrains.kotlin.android.synthetic.res.CliAndroidPackageFragmentProviderExtension
|
||||||
import org.jetbrains.kotlin.backend.common.extensions.IrGenerationExtension
|
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.ClassBuilderInterceptorExtension
|
||||||
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension
|
||||||
import org.jetbrains.kotlin.compiler.plugin.*
|
import org.jetbrains.kotlin.compiler.plugin.*
|
||||||
@@ -107,6 +105,7 @@ class AndroidComponentRegistrar : ComponentRegistrar {
|
|||||||
companion object {
|
companion object {
|
||||||
fun registerParcelExtensions(project: Project) {
|
fun registerParcelExtensions(project: Project) {
|
||||||
ExpressionCodegenExtension.registerExtension(project, ParcelableCodegenExtension())
|
ExpressionCodegenExtension.registerExtension(project, ParcelableCodegenExtension())
|
||||||
|
PureIrGenerationExtension.registerExtension(project, ParcelableIrGeneratorExtension())
|
||||||
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
|
SyntheticResolveExtension.registerExtension(project, ParcelableResolveExtension())
|
||||||
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
|
ClassBuilderInterceptorExtension.registerExtension(project, ParcelableClinitClassBuilderInterceptorExtension())
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user