Parcelize, JVM IR: Fix types in nested containers
This commit is contained in:
committed by
Yan Zhulanow
parent
eaa5d08736
commit
8ab6411b93
+6
-4
@@ -171,7 +171,7 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
||||
}
|
||||
return wrapNullableSerializerIfNeeded(
|
||||
irType,
|
||||
IrListParcelSerializer(classifier, get(elementType, scope, parcelizeType, strict()))
|
||||
IrListParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
|
||||
)
|
||||
}
|
||||
|
||||
@@ -185,6 +185,8 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
||||
val parceler =
|
||||
IrMapParcelSerializer(
|
||||
classifier,
|
||||
keyType,
|
||||
valueType,
|
||||
get(keyType, scope, parcelizeType, strict()),
|
||||
get(valueType, scope, parcelizeType, strict())
|
||||
)
|
||||
@@ -265,9 +267,9 @@ class IrParcelSerializerFactory(symbols: AndroidSymbols) {
|
||||
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 booleanSerializer = IrWrappedIntParcelSerializer(irBuiltIns.booleanType)
|
||||
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
|
||||
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
|
||||
|
||||
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
|
||||
|
||||
|
||||
+29
-22
@@ -47,32 +47,33 @@ class IrSimpleParcelSerializer(private val reader: IrSimpleFunctionSymbol, priva
|
||||
}
|
||||
}
|
||||
|
||||
// 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 {
|
||||
// Serialize a value of the primitive [parcelType] by coercion to int.
|
||||
class IrWrappedIntParcelSerializer(private val parcelType: IrType) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
|
||||
val deserializedPrimitive = readParcelWith(serializer, parcel)
|
||||
return if (parcelType.isBoolean()) {
|
||||
irIfThenElse(
|
||||
parcelType,
|
||||
irNotEquals(deserializedPrimitive, irInt(0)),
|
||||
irInt(1),
|
||||
irInt(0)
|
||||
)
|
||||
irNotEquals(parcelReadInt(irGet(parcel)), irInt(0))
|
||||
} else {
|
||||
val conversion = deserializedPrimitive.type.getClass()!!.functions.first { function ->
|
||||
function.name.asString() == "to${parcelType.getClass()!!.name}"
|
||||
val conversion = context.irBuiltIns.intClass.functions.first { function ->
|
||||
function.owner.name.asString() == "to${parcelType.getClass()!!.name}"
|
||||
}
|
||||
irCall(conversion).apply {
|
||||
dispatchReceiver = deserializedPrimitive
|
||||
dispatchReceiver = parcelReadInt(irGet(parcel))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
parcelWriteInt(
|
||||
irGet(parcel),
|
||||
if (parcelType.isBoolean()) {
|
||||
irIfThenElse(context.irBuiltIns.intType, value, irInt(1), irInt(0))
|
||||
} else {
|
||||
val conversion = parcelType.classOrNull!!.functions.first { function ->
|
||||
function.owner.name.asString() == "toInt"
|
||||
}
|
||||
irCall(conversion).apply { dispatchReceiver = value }
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
// Wraps a non-null aware parceler to handle nullable types.
|
||||
@@ -253,7 +254,7 @@ class IrArrayParcelSerializer(
|
||||
|
||||
forUntil(irGet(arraySize)) { index ->
|
||||
val getter = context.irBuiltIns.arrayClass.getSimpleFunction("get")!!
|
||||
val element = irCall(getter).apply {
|
||||
val element = irCall(getter, elementType).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
}
|
||||
@@ -332,7 +333,7 @@ class IrSparseArrayParcelSerializer(
|
||||
putValueArgument(0, irGet(index))
|
||||
})
|
||||
|
||||
+writeParcelWith(elementSerializer, parcel, flags, irCall(valueAtFunction).apply {
|
||||
+writeParcelWith(elementSerializer, parcel, flags, irCall(valueAtFunction.symbol, elementType).apply {
|
||||
dispatchReceiver = irGet(arrayTemporary)
|
||||
putValueArgument(0, irGet(index))
|
||||
})
|
||||
@@ -342,7 +343,11 @@ class IrSparseArrayParcelSerializer(
|
||||
|
||||
// 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 {
|
||||
class IrListParcelSerializer(
|
||||
private val irClass: IrClass,
|
||||
private val elementType: IrType,
|
||||
private val elementSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
override fun AndroidIrBuilder.writeParcel(parcel: IrValueDeclaration, flags: IrValueDeclaration, value: IrExpression): IrExpression {
|
||||
val sizeFunction = irClass.getPropertyGetter("size")!!
|
||||
val iteratorFunction = irClass.getMethodWithoutArguments("iterator")
|
||||
@@ -360,7 +365,7 @@ class IrListParcelSerializer(private val irClass: IrClass, private val elementSe
|
||||
})
|
||||
+irWhile().apply {
|
||||
condition = irCall(iteratorHasNext).apply { dispatchReceiver = irGet(iterator) }
|
||||
body = writeParcelWith(elementSerializer, parcel, flags, irCall(iteratorNext).apply {
|
||||
body = writeParcelWith(elementSerializer, parcel, flags, irCall(iteratorNext.symbol, elementType).apply {
|
||||
dispatchReceiver = irGet(iterator)
|
||||
})
|
||||
}
|
||||
@@ -415,6 +420,8 @@ class IrListParcelSerializer(private val irClass: IrClass, private val elementSe
|
||||
// Map uses LinkedHashMap, while NavigableMap and SortedMap use to TreeMap.
|
||||
class IrMapParcelSerializer(
|
||||
private val irClass: IrClass,
|
||||
private val keyType: IrType,
|
||||
private val valueType: IrType,
|
||||
private val keySerializer: IrParcelSerializer,
|
||||
private val valueSerializer: IrParcelSerializer
|
||||
) : IrParcelSerializer {
|
||||
@@ -447,10 +454,10 @@ class IrMapParcelSerializer(
|
||||
val element = irTemporary(irCall(iteratorNext).apply {
|
||||
dispatchReceiver = irGet(iterator)
|
||||
})
|
||||
+writeParcelWith(keySerializer, parcel, flags, irCall(elementKey).apply {
|
||||
+writeParcelWith(keySerializer, parcel, flags, irCall(elementKey, keyType).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
+writeParcelWith(valueSerializer, parcel, flags, irCall(elementValue).apply {
|
||||
+writeParcelWith(valueSerializer, parcel, flags, irCall(elementValue, valueType).apply {
|
||||
dispatchReceiver = irGet(element)
|
||||
})
|
||||
}
|
||||
|
||||
+20
@@ -194,11 +194,31 @@ public class ParcelBoxTestGenerated extends AbstractParcelBoxTest {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/maps.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedArrays.kt")
|
||||
public void testNestedArrays() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedArrays.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedLists.kt")
|
||||
public void testNestedLists() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedLists.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedMaps.kt")
|
||||
public void testNestedMaps() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedMaps.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedParcelable.kt")
|
||||
public void testNestedParcelable() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedParcelable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedSparseArrays.kt")
|
||||
public void testNestedSparseArrays() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedSparseArrays.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullableTypes.kt")
|
||||
public void testNullableTypes() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nullableTypes.kt");
|
||||
|
||||
+20
@@ -194,11 +194,31 @@ public class ParcelIrBoxTestGenerated extends AbstractParcelIrBoxTest {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/maps.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedArrays.kt")
|
||||
public void testNestedArrays() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedArrays.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedLists.kt")
|
||||
public void testNestedLists() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedLists.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedMaps.kt")
|
||||
public void testNestedMaps() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedMaps.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedParcelable.kt")
|
||||
public void testNestedParcelable() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedParcelable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nestedSparseArrays.kt")
|
||||
public void testNestedSparseArrays() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nestedSparseArrays.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullableTypes.kt")
|
||||
public void testNullableTypes() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/box/nullableTypes.kt");
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: Array<Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Data(arrayOf(arrayOf(0, 1)))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size == 1)
|
||||
assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: List<Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Data(listOf(arrayOf(0, 1)))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size == 1)
|
||||
assert(Arrays.equals(second.data[0], arrayOf(0, 1)))
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: Map<Array<Int>, Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val first = Data(mapOf(arrayOf(0) to arrayOf(1)))
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size == 1)
|
||||
val entry = second.data.entries.single()
|
||||
assert(Arrays.equals(entry.key, arrayOf(0)))
|
||||
assert(Arrays.equals(entry.value, arrayOf(1)))
|
||||
}
|
||||
Vendored
+30
@@ -0,0 +1,30 @@
|
||||
// WITH_RUNTIME
|
||||
// IGNORE_BACKEND: JVM
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
import android.util.SparseArray
|
||||
import java.util.Arrays
|
||||
|
||||
@Parcelize
|
||||
class Data(val data: SparseArray<Array<Int>>) : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
var array = SparseArray<Array<Int>>()
|
||||
array.append(0, arrayOf(0, 1))
|
||||
val first = Data(array)
|
||||
|
||||
first.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
parcel.setDataPosition(0)
|
||||
|
||||
val second = readFromParcel<Data>(parcel)
|
||||
assert(second.data.size() == 1)
|
||||
assert(Arrays.equals(second.data.get(0), arrayOf(0, 1)))
|
||||
}
|
||||
-2
@@ -112,8 +112,6 @@ public final class test/Foo$Creator : java/lang/Object, android/os/Parcelable$Cr
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
NEW
|
||||
DUP
|
||||
GETSTATIC (Companion, Lk/KotlinParcelable$Companion;)
|
||||
POP
|
||||
GETSTATIC (CREATOR, Lk/KotlinParcelable$Creator;)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;)
|
||||
|
||||
plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/listInsideList.ir.txt
Vendored
+1
-2
@@ -44,14 +44,13 @@ public final class Test : java/lang/Object, android/os/Parcelable {
|
||||
IFEQ (L2)
|
||||
ALOAD (4)
|
||||
INVOKEINTERFACE (java/util/Iterator, next, ()Ljava/lang/Object;)
|
||||
CHECKCAST
|
||||
ASTORE (5)
|
||||
ALOAD (1)
|
||||
ALOAD (5)
|
||||
CHECKCAST
|
||||
INVOKEINTERFACE (java/util/List, size, ()I)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
ALOAD (5)
|
||||
CHECKCAST
|
||||
INVOKEINTERFACE (java/util/List, iterator, ()Ljava/util/Iterator;)
|
||||
ASTORE (6)
|
||||
LABEL (L3)
|
||||
|
||||
Vendored
+13
-2
@@ -1,7 +1,7 @@
|
||||
public final class Test$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final java.lang.Object createFromParcel(android.os.Parcel in) {
|
||||
public final Test createFromParcel(android.os.Parcel in) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
@@ -18,7 +18,18 @@ public final class Test$Creator : java/lang/Object, android/os/Parcelable$Creato
|
||||
LABEL (L1)
|
||||
}
|
||||
|
||||
public final java.lang.Object[] newArray(int size)
|
||||
public java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
LINENUMBER (10)
|
||||
ALOAD (0)
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (Test$Creator, createFromParcel, (Landroid/os/Parcel;)LTest;)
|
||||
ARETURN
|
||||
}
|
||||
|
||||
public final Test[] newArray(int size)
|
||||
|
||||
public java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class Test : java/lang/Object, android/os/Parcelable {
|
||||
|
||||
Vendored
+7
-1
@@ -145,8 +145,14 @@ public final class User : java/lang/Object, android/os/Parcelable {
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
GETFIELD (isProUser, Z)
|
||||
IFEQ (L1)
|
||||
ICONST_1
|
||||
GOTO (L2)
|
||||
LABEL (L1)
|
||||
ICONST_0
|
||||
LABEL (L2)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
RETURN
|
||||
LABEL (L1)
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user