Parcelize: Use the class loader of the Parcelable class for reading

In `readBundle`, `readPersistableBundle`, and `readValue`.
This commit is contained in:
Steven Schäfer
2021-09-20 15:00:36 +02:00
committed by Alexander Udalov
parent 08eaac7e56
commit 897c09bf8d
9 changed files with 217 additions and 28 deletions
@@ -37,13 +37,6 @@ class AndroidIrBuilder internal constructor(
}
}
fun parcelReadValue(receiver: IrExpression, loader: IrExpression): IrExpression {
return irCall(androidSymbols.parcelReadValue).apply {
dispatchReceiver = receiver
putValueArgument(0, loader)
}
}
fun parcelWriteInt(receiver: IrExpression, value: IrExpression): IrExpression {
return irCall(androidSymbols.parcelWriteInt).apply {
dispatchReceiver = receiver
@@ -66,13 +59,6 @@ class AndroidIrBuilder internal constructor(
}
}
fun parcelWriteValue(receiver: IrExpression, v: IrExpression): IrExpression {
return irCall(androidSymbols.parcelWriteValue).apply {
dispatchReceiver = receiver
putValueArgument(0, v)
}
}
fun textUtilsWriteToParcel(cs: IrExpression, p: IrExpression, parcelableFlags: IrExpression): IrExpression {
return irCall(androidSymbols.textUtilsWriteToParcel).apply {
putValueArgument(0, cs)
@@ -209,7 +209,9 @@ class AndroidSymbols(
androidOsParcel.owner.addFunction("createStringArrayList", javaUtilArrayList.defaultType).symbol
val parcelReadBundle: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readBundle", androidOsBundle.defaultType).symbol
androidOsParcel.owner.addFunction("readBundle", androidOsBundle.defaultType).apply {
addValueParameter("loader", javaLangClassLoader.defaultType)
}.symbol
val parcelReadByte: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readByte", irBuiltIns.byteType).symbol
@@ -235,7 +237,9 @@ class AndroidSymbols(
}.symbol
val parcelReadPersistableBundle: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readPersistableBundle", androidOsPersistableBundle.defaultType).symbol
androidOsParcel.owner.addFunction("readPersistableBundle", androidOsPersistableBundle.defaultType).apply {
addValueParameter("loader", javaLangClassLoader.defaultType)
}.symbol
val parcelReadSerializable: IrSimpleFunctionSymbol =
androidOsParcel.owner.addFunction("readSerializable", javaIoSerializable.defaultType).symbol
@@ -13,7 +13,7 @@ import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.parcelize.RAW_VALUE_ANNOTATION_FQ_NAMES
class IrParcelSerializerFactory(symbols: AndroidSymbols) {
class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
/**
* Resolve the given [irType] to a corresponding [IrParcelSerializer]. This depends on the TypeParcelers which
* are currently in [scope], as well as the type of the enclosing Parceleable class [parcelizeType], which is needed
@@ -43,9 +43,13 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
"kotlin.CharSequence", "java.lang.CharSequence" ->
return charSequenceSerializer
"android.os.Bundle" ->
return bundleSerializer
return IrParcelSerializerWithClassLoader(parcelizeType, symbols.parcelReadBundle, symbols.parcelWriteBundle)
"android.os.PersistableBundle" ->
return persistableBundleSerializer
return IrParcelSerializerWithClassLoader(
parcelizeType,
symbols.parcelReadPersistableBundle,
symbols.parcelWritePersistableBundle
)
// Non-nullable built-in serializers
"kotlin.Byte", "java.lang.Byte" ->
@@ -251,14 +255,15 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
throw IllegalArgumentException("Illegal type, could not find a specific serializer for ${irType.render()}")
else ->
return IrGenericValueParcelSerializer(parcelizeType)
return IrParcelSerializerWithClassLoader(parcelizeType, symbols.parcelReadValue, symbols.parcelWriteValue)
}
}
private fun wrapNullableSerializerIfNeeded(irType: IrType, serializer: IrParcelSerializer) =
if (irType.isNullable()) IrNullAwareParcelSerializer(serializer) else serializer
private val irBuiltIns: IrBuiltIns = symbols.irBuiltIns
private val irBuiltIns: IrBuiltIns
get() = symbols.irBuiltIns
private val stringArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArray, symbols.parcelWriteStringArray)
private val stringListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArrayList, symbols.parcelWriteStringList)
@@ -298,9 +303,6 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
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)
}
@@ -215,15 +215,25 @@ class IrGenericParcelableParcelSerializer(private val parcelizeType: IrType) : I
}
}
// Fallback parcel serializer for unknown types using Parcel.readValue/writeValue.
// Creates a serializer from a pair of parcel methods of the form reader(ClassLoader)T and writer(T)V.
// This needs a reference to the parcelize type itself in order to find the correct class loader to use, see KT-20027.
class IrGenericValueParcelSerializer(private val parcelizeType: IrType) : IrParcelSerializer {
class IrParcelSerializerWithClassLoader(
private val parcelizeType: IrType,
private val reader: IrSimpleFunctionSymbol,
private val writer: IrSimpleFunctionSymbol
) : IrParcelSerializer {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
return parcelReadValue(irGet(parcel), classGetClassLoader(javaClassReference(parcelizeType)))
return irCall(reader).apply {
dispatchReceiver = irGet(parcel)
putValueArgument(0, classGetClassLoader(javaClassReference(parcelizeType)))
}
}
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
return parcelWriteValue(irGet(parcel), value)
return irCall(writer).apply {
dispatchReceiver = irGet(parcel)
putValueArgument(0, value)
}
}
}
@@ -0,0 +1,83 @@
public final class A$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final A createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
ALOAD (1)
LDC (LA;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readValue, (Ljava/lang/ClassLoader;)Ljava/lang/Object;)
ALOAD (1)
LDC (LA;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readBundle, (Ljava/lang/ClassLoader;)Landroid/os/Bundle;)
ALOAD (1)
LDC (LA;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readPersistableBundle, (Ljava/lang/ClassLoader;)Landroid/os/PersistableBundle;)
INVOKESPECIAL (A, <init>, (Ljava/lang/Object;Landroid/os/Bundle;Landroid/os/PersistableBundle;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (A$Creator, createFromParcel, (Landroid/os/Parcel;)LA;)
ARETURN
LABEL (L1)
}
public final A[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class A : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final java.lang.Object a
private final android.os.Bundle b
private final android.os.PersistableBundle c
static void <clinit>()
public void <init>(java.lang.Object a, android.os.Bundle b, android.os.PersistableBundle c)
public int describeContents()
public final java.lang.Object getA()
public final android.os.Bundle getB()
public final android.os.PersistableBundle getC()
public void writeToParcel(android.os.Parcel out, int flags) {
LABEL (L0)
ALOAD (1)
LDC (out)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (A, a, Ljava/lang/Object;)
INVOKEVIRTUAL (android/os/Parcel, writeValue, (Ljava/lang/Object;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (A, b, Landroid/os/Bundle;)
INVOKEVIRTUAL (android/os/Parcel, writeBundle, (Landroid/os/Bundle;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (A, c, Landroid/os/PersistableBundle;)
INVOKEVIRTUAL (android/os/Parcel, writePersistableBundle, (Landroid/os/PersistableBundle;)V)
RETURN
LABEL (L1)
}
}
@@ -0,0 +1,15 @@
// CURIOUS_ABOUT writeToParcel, createFromParcel
// WITH_RUNTIME
// Test to ensure that we are using the correct class loader
// for generic types and bundles. See:
// - https://issuetracker.google.com/184072801
// - https://issuetracker.google.com/187577721
import kotlinx.parcelize.*
import android.os.Bundle
import android.os.Parcelable
import android.os.PersistableBundle
@Parcelize
class A<T>(val a: @RawValue T, val b: Bundle, val c: PersistableBundle) : Parcelable
@@ -0,0 +1,79 @@
public final class A$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final A createFromParcel(android.os.Parcel in) {
LABEL (L0)
ALOAD (1)
LDC (in)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW
DUP
ALOAD (1)
LDC (Ljava/lang/Object;)
INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;)
INVOKEVIRTUAL (android/os/Parcel, readValue, (Ljava/lang/ClassLoader;)Ljava/lang/Object;)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readBundle, ()Landroid/os/Bundle;)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readBundle, ()Landroid/os/PersistableBundle;)
INVOKESPECIAL (A, <init>, (Ljava/lang/Object;Landroid/os/Bundle;Landroid/os/PersistableBundle;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel p0) {
LABEL (L0)
LINENUMBER (15)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (A$Creator, createFromParcel, (Landroid/os/Parcel;)LA;)
ARETURN
}
public final A[] newArray(int size)
public java.lang.Object[] newArray(int p0)
}
public final class A : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final java.lang.Object a
private final android.os.Bundle b
private final android.os.PersistableBundle c
static void <clinit>()
public void <init>(java.lang.Object a, android.os.Bundle b, android.os.PersistableBundle c)
public int describeContents()
public final java.lang.Object getA()
public final android.os.Bundle getB()
public final android.os.PersistableBundle getC()
public void writeToParcel(android.os.Parcel parcel, int flags) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (A, a, Ljava/lang/Object;)
INVOKEVIRTUAL (android/os/Parcel, writeValue, (Ljava/lang/Object;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (A, b, Landroid/os/Bundle;)
INVOKEVIRTUAL (android/os/Parcel, writeBundle, (Landroid/os/Bundle;)V)
ALOAD (1)
ALOAD (0)
GETFIELD (A, c, Landroid/os/PersistableBundle;)
INVOKEVIRTUAL (android/os/Parcel, writeBundle, (Landroid/os/PersistableBundle;)V)
RETURN
LABEL (L1)
}
}
@@ -30,6 +30,11 @@ public class ParcelizeBytecodeListingTestGenerated extends AbstractParcelizeByte
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/parcelize/parcelize-compiler/testData/codegen"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM, true);
}
@TestMetadata("classLoaderValues.kt")
public void testClassLoaderValues() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.kt");
}
@TestMetadata("customDescribeContents.kt")
public void testCustomDescribeContents() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/customDescribeContents.kt");
@@ -30,6 +30,11 @@ public class ParcelizeIrBytecodeListingTestGenerated extends AbstractParcelizeIr
KtTestUtil.assertAllTestsPresentByMetadataWithExcluded(this.getClass(), new File("plugins/parcelize/parcelize-compiler/testData/codegen"), Pattern.compile("^(.+)\\.kt$"), null, TargetBackend.JVM_IR, true);
}
@TestMetadata("classLoaderValues.kt")
public void testClassLoaderValues() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.kt");
}
@TestMetadata("customDescribeContents.kt")
public void testCustomDescribeContents() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/customDescribeContents.kt");