From 897c09bf8dd0799e678ada9c240b5aa5a7b55781 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steven=20Sch=C3=A4fer?= Date: Mon, 20 Sep 2021 15:00:36 +0200 Subject: [PATCH] Parcelize: Use the class loader of the Parcelable class for reading In `readBundle`, `readPersistableBundle`, and `readValue`. --- .../kotlin/parcelize/ir/AndroidIrBuilder.kt | 14 ---- .../kotlin/parcelize/ir/AndroidSymbols.kt | 8 +- .../parcelize/ir/IrParcelSerializerFactory.kt | 18 ++-- .../parcelize/ir/IrParcelSerializers.kt | 18 +++- .../testData/codegen/classLoaderValues.ir.txt | 83 +++++++++++++++++++ .../testData/codegen/classLoaderValues.kt | 15 ++++ .../testData/codegen/classLoaderValues.txt | 79 ++++++++++++++++++ ...ParcelizeBytecodeListingTestGenerated.java | 5 ++ ...rcelizeIrBytecodeListingTestGenerated.java | 5 ++ 9 files changed, 217 insertions(+), 28 deletions(-) create mode 100644 plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.ir.txt create mode 100644 plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.kt create mode 100644 plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.txt diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt index c5eb6d6cd84..cd9064c0204 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidIrBuilder.kt @@ -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) diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt index 9c225cb7407..3cdb96ef1ad 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/AndroidSymbols.kt @@ -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 diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt index 9c6d26601c6..6bebca199cd 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializerFactory.kt @@ -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) } diff --git a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt index ad38769dc81..4a38006ff5e 100644 --- a/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt +++ b/plugins/parcelize/parcelize-compiler/src/org/jetbrains/kotlin/parcelize/ir/IrParcelSerializers.kt @@ -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) + } } } diff --git a/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.ir.txt b/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.ir.txt new file mode 100644 index 00000000000..a5379c1a9b5 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.ir.txt @@ -0,0 +1,83 @@ +public final class A$Creator : java/lang/Object, android/os/Parcelable$Creator { + public void () + + 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, , (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 () + + public void (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) + } +} diff --git a/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.kt b/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.kt new file mode 100644 index 00000000000..1bc50730755 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.kt @@ -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(val a: @RawValue T, val b: Bundle, val c: PersistableBundle) : Parcelable diff --git a/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.txt b/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.txt new file mode 100644 index 00000000000..72b33fb7236 --- /dev/null +++ b/plugins/parcelize/parcelize-compiler/testData/codegen/classLoaderValues.txt @@ -0,0 +1,79 @@ +public final class A$Creator : java/lang/Object, android/os/Parcelable$Creator { + public void () + + 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, , (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 () + + public void (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) + } +} diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeBytecodeListingTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeBytecodeListingTestGenerated.java index b911563ed58..75fce756139 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeBytecodeListingTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeBytecodeListingTestGenerated.java @@ -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"); diff --git a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeIrBytecodeListingTestGenerated.java b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeIrBytecodeListingTestGenerated.java index 60ebcfeff55..682bf5e4a68 100644 --- a/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeIrBytecodeListingTestGenerated.java +++ b/plugins/parcelize/parcelize-compiler/tests/org/jetbrains/kotlin/parcelize/test/ParcelizeIrBytecodeListingTestGenerated.java @@ -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");