Parcelize: Support unsigned array types

See https://issuetracker.google.com/200774823
This commit is contained in:
Steven Schäfer
2021-09-21 14:47:48 +02:00
committed by Alexander Udalov
parent 2501013012
commit cb46a56815
11 changed files with 490 additions and 2 deletions
@@ -19,6 +19,7 @@ import org.jetbrains.kotlin.ir.declarations.impl.IrExternalPackageFragmentImpl
import org.jetbrains.kotlin.ir.declarations.impl.IrFactoryImpl
import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
@@ -123,6 +124,38 @@ class AndroidSymbols(
owner.inlineClassRepresentation = InlineClassRepresentation(Name.identifier("data"), irBuiltIns.longType as IrSimpleType)
}
val kotlinUByteArray: IrClassSymbol =
createClass(kotlin, "UByteArray", ClassKind.CLASS, Modality.FINAL, true).apply {
owner.inlineClassRepresentation = InlineClassRepresentation(
Name.identifier("storage"),
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.byteType).owner.defaultType
)
}
val kotlinUShortArray: IrClassSymbol =
createClass(kotlin, "UShortArray", ClassKind.CLASS, Modality.FINAL, true).apply {
owner.inlineClassRepresentation = InlineClassRepresentation(
Name.identifier("storage"),
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.shortType).owner.defaultType
)
}
val kotlinUIntArray: IrClassSymbol =
createClass(kotlin, "UIntArray", ClassKind.CLASS, Modality.FINAL, true).apply {
owner.inlineClassRepresentation = InlineClassRepresentation(
Name.identifier("storage"),
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.intType).owner.defaultType
)
}
val kotlinULongArray: IrClassSymbol =
createClass(kotlin, "ULongArray", ClassKind.CLASS, Modality.FINAL, true).apply {
owner.inlineClassRepresentation = InlineClassRepresentation(
Name.identifier("storage"),
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.longType).owner.defaultType
)
}
val androidOsParcelableCreator: IrClassSymbol = irFactory.buildClass {
name = Name.identifier("Creator")
kind = ClassKind.INTERFACE
@@ -110,6 +110,19 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
"android.util.SparseBooleanArray" ->
if (!scope.hasCustomSerializer(irBuiltIns.booleanType))
return sparseBooleanArraySerializer
// Unsigned array types
"kotlin.UByteArray" ->
return ubyteArraySerializer
"kotlin.UShortArray" ->
return wrapNullableSerializerIfNeeded(
irType,
ushortArraySerializer
)
"kotlin.UIntArray" ->
return uintArraySerializer
"kotlin.ULongArray" ->
return ulongArraySerializer
}
// Generic container types
@@ -289,6 +302,11 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
private val booleanSerializer = IrWrappedIntParcelSerializer(irBuiltIns.booleanType)
private val shortSerializer = IrWrappedIntParcelSerializer(irBuiltIns.shortType)
private val charSerializer = IrWrappedIntParcelSerializer(irBuiltIns.charType)
private val shortArraySerializer = IrArrayParcelSerializer(
irBuiltIns.primitiveArrayForType.getValue(irBuiltIns.shortType).defaultType,
irBuiltIns.shortType,
shortSerializer
)
// Unsigned primitive types
private val ubyteSerializer = IrUnsafeCoerceWrappedSerializer(byteSerializer, symbols.kotlinUByte.defaultType, irBuiltIns.byteType)
@@ -296,6 +314,31 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
private val uintSerializer = IrUnsafeCoerceWrappedSerializer(intSerializer, symbols.kotlinUInt.defaultType, irBuiltIns.intType)
private val ulongSerializer = IrUnsafeCoerceWrappedSerializer(longSerializer, symbols.kotlinULong.defaultType, irBuiltIns.longType)
// Unsigned array types
private val ubyteArraySerializer = IrUnsafeCoerceWrappedSerializer(
byteArraySerializer,
symbols.kotlinUByteArray.owner.defaultType,
symbols.kotlinUByteArray.owner.inlineClassRepresentation!!.underlyingType
)
private val ushortArraySerializer = IrUnsafeCoerceWrappedSerializer(
shortArraySerializer,
symbols.kotlinUShortArray.owner.defaultType,
symbols.kotlinUShortArray.owner.inlineClassRepresentation!!.underlyingType
)
private val uintArraySerializer = IrUnsafeCoerceWrappedSerializer(
intArraySerializer,
symbols.kotlinUIntArray.owner.defaultType,
symbols.kotlinUIntArray.owner.inlineClassRepresentation!!.underlyingType
)
private val ulongArraySerializer = IrUnsafeCoerceWrappedSerializer(
longArraySerializer,
symbols.kotlinULongArray.owner.defaultType,
symbols.kotlinULongArray.owner.inlineClassRepresentation!!.underlyingType
)
private val charSequenceSerializer = IrCharSequenceParcelSerializer()
// TODO The old backend uses the hidden "read/writeRawFileDescriptor" methods.
@@ -243,7 +243,6 @@ class IrCustomParcelSerializer(private val parcelerObject: IrClass) : IrParcelSe
// 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,
@@ -428,7 +428,8 @@ interface ParcelSerializer {
}
private fun Type.isUnsigned(): Boolean = when (descriptor) {
"Lkotlin/UByte;", "Lkotlin/UShort;", "Lkotlin/UInt;", "Lkotlin/ULong;" -> true
"Lkotlin/UByte;", "Lkotlin/UShort;", "Lkotlin/UInt;", "Lkotlin/ULong;",
"Lkotlin/UByteArray;", "Lkotlin/UShortArray;", "Lkotlin/UIntArray;", "Lkotlin/ULongArray;" -> true
else -> false
}
@@ -0,0 +1,79 @@
// WITH_RUNTIME
// IGNORE_BACKEND: JVM
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.Arrays
@Parcelize
data class Test(
val a: UByteArray,
val b: UShortArray,
val c: UIntArray,
val d: ULongArray,
val e: UByteArray?,
val f: UShortArray?,
val g: UIntArray?,
val h: ULongArray?
) : Parcelable {
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Test) return false
if (!a.contentEquals(other.a)) return false
if (!b.contentEquals(other.b)) return false
if (!c.contentEquals(other.c)) return false
if (!d.contentEquals(other.d)) return false
if (!e.contentEquals(other.e)) return false
if (!f.contentEquals(other.f)) return false
if (!g.contentEquals(other.g)) return false
if (!h.contentEquals(other.h)) return false
return true
}
override fun hashCode(): Int {
return 0
}
}
fun box() = parcelTest { parcel ->
val first = Test(
a = ubyteArrayOf(1U, 2U, 3U),
b = ushortArrayOf(2U, 3U, 4U),
c = uintArrayOf(3U, 4U, 5U),
d = ulongArrayOf(4U, 5U, 6U),
e = ubyteArrayOf(UByte.MAX_VALUE, UByte.MIN_VALUE),
f = ushortArrayOf(UShort.MAX_VALUE, UShort.MIN_VALUE),
g = uintArrayOf(UInt.MAX_VALUE, UInt.MIN_VALUE),
h = ulongArrayOf(ULong.MAX_VALUE, ULong.MIN_VALUE),
)
val second = Test(
a = ubyteArrayOf(),
b = ushortArrayOf(),
c = uintArrayOf(),
d = ulongArrayOf(),
e = null,
f = null,
g = null,
h = null,
)
first.writeToParcel(parcel, 0)
second.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val creator = parcelableCreator<Test>()
val first2 = creator.createFromParcel(parcel)
val second2 = creator.createFromParcel(parcel)
assert(first == first2)
assert(second == second2)
}
@@ -0,0 +1,295 @@
public final class Test$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final Test createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createByteArray, ()[B)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
ISTORE (2)
ILOAD (2)
NEWARRAY (9)
ASTORE (3)
ICONST_0
ISTORE (4)
ASTORE (5)
LABEL (L1)
ILOAD (4)
ILOAD (2)
IF_ICMPEQ (L2)
ALOAD (3)
ILOAD (4)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
I2S
SASTORE
ILOAD (4)
ICONST_1
IADD
ISTORE (4)
GOTO (L1)
LABEL (L2)
ALOAD (5)
ALOAD (3)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createIntArray, ()[I)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createLongArray, ()[J)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createByteArray, ()[B)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
IFNE (L3)
ACONST_NULL
GOTO (L4)
LABEL (L3)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
ISTORE (2)
ILOAD (2)
NEWARRAY (9)
ASTORE (3)
ICONST_0
ISTORE (4)
ASTORE (9)
ASTORE (8)
ASTORE (7)
ASTORE (6)
ASTORE (5)
LABEL (L5)
ILOAD (4)
ILOAD (2)
IF_ICMPEQ (L6)
ALOAD (3)
ILOAD (4)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
I2S
SASTORE
ILOAD (4)
ICONST_1
IADD
ISTORE (4)
GOTO (L5)
LABEL (L6)
ALOAD (5)
ALOAD (6)
ALOAD (7)
ALOAD (8)
ALOAD (9)
ALOAD (3)
LABEL (L4)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createIntArray, ()[I)
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createLongArray, ()[J)
ACONST_NULL
ASTORE (10)
ASTORE (11)
ASTORE (12)
ASTORE (13)
ASTORE (14)
ASTORE (15)
ASTORE (16)
ASTORE (17)
ASTORE (18)
NEW
DUP
ALOAD (18)
ALOAD (17)
ALOAD (16)
ALOAD (15)
ALOAD (14)
ALOAD (13)
ALOAD (12)
ALOAD (11)
ALOAD (10)
INVOKESPECIAL (Test, <init>, ([B[S[I[J[B[S[I[JLkotlin/jvm/internal/DefaultConstructorMarker;)V)
ARETURN
LABEL (L7)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (Test$Creator, createFromParcel, (Landroid/os/Parcel;)LTest;)
ARETURN
LABEL (L1)
}
public final Test[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class Test : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final byte[] a
private final short[] b
private final int[] c
private final long[] d
private final byte[] e
private final short[] f
private final int[] g
private final long[] h
static void <clinit>()
private void <init>(byte[] a, short[] b, int[] c, long[] d, byte[] e, short[] f, int[] g, long[] h)
public void <init>(byte[] a, short[] b, int[] c, long[] d, byte[] e, short[] f, int[] g, long[] h, kotlin.jvm.internal.DefaultConstructorMarker $constructor_marker)
public final byte[] component1-TcUX1vc()
public final short[] component2-amswpOA()
public final int[] component3--hP7Qyg()
public final long[] component4-Y2RjT0g()
public final byte[] component5--5HJl4c()
public final short[] component6-mR6EDK0()
public final int[] component7-9a2mAYU()
public final long[] component8-2g2roS4()
public final Test copy-gFjYMWk(byte[] a, short[] b, int[] c, long[] d, byte[] e, short[] f, int[] g, long[] h)
public static Test copy-gFjYMWk$default(Test p0, byte[] p1, short[] p2, int[] p3, long[] p4, byte[] p5, short[] p6, int[] p7, long[] p8, int p9, java.lang.Object p10)
public int describeContents()
public boolean equals(java.lang.Object other)
public final byte[] getA-TcUX1vc()
public final short[] getB-amswpOA()
public final int[] getC--hP7Qyg()
public final long[] getD-Y2RjT0g()
public final byte[] getE--5HJl4c()
public final short[] getF-mR6EDK0()
public final int[] getG-9a2mAYU()
public final long[] getH-2g2roS4()
public int hashCode()
public java.lang.String toString()
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 (Test, a, [B)
INVOKEVIRTUAL (android/os/Parcel, writeByteArray, ([B)V)
ALOAD (0)
GETFIELD (Test, b, [S)
ASTORE (3)
ALOAD (3)
ARRAYLENGTH
ISTORE (4)
ALOAD (1)
ILOAD (4)
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
ICONST_0
ISTORE (5)
LABEL (L1)
ILOAD (5)
ILOAD (4)
IF_ICMPEQ (L2)
ALOAD (1)
ALOAD (3)
ILOAD (5)
SALOAD
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
ILOAD (5)
ICONST_1
IADD
ISTORE (5)
GOTO (L1)
LABEL (L2)
ALOAD (1)
ALOAD (0)
GETFIELD (Test, c, [I)
INVOKEVIRTUAL (android/os/Parcel, writeIntArray, ([I)V)
ALOAD (1)
ALOAD (0)
GETFIELD (Test, d, [J)
INVOKEVIRTUAL (android/os/Parcel, writeLongArray, ([J)V)
ALOAD (1)
ALOAD (0)
GETFIELD (Test, e, [B)
INVOKEVIRTUAL (android/os/Parcel, writeByteArray, ([B)V)
ALOAD (0)
GETFIELD (Test, f, [S)
ASTORE (3)
ALOAD (3)
IFNONNULL (L3)
ALOAD (1)
ICONST_0
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
GOTO (L4)
LABEL (L3)
ALOAD (1)
ICONST_1
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
ALOAD (3)
ASTORE (4)
ALOAD (4)
ARRAYLENGTH
ISTORE (5)
ALOAD (1)
ILOAD (5)
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
ICONST_0
ISTORE (6)
LABEL (L5)
ILOAD (6)
ILOAD (5)
IF_ICMPEQ (L4)
ALOAD (1)
ALOAD (4)
ILOAD (6)
SALOAD
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
ILOAD (6)
ICONST_1
IADD
ISTORE (6)
GOTO (L5)
LABEL (L4)
ALOAD (1)
ALOAD (0)
GETFIELD (Test, g, [I)
INVOKEVIRTUAL (android/os/Parcel, writeIntArray, ([I)V)
ALOAD (1)
ALOAD (0)
GETFIELD (Test, h, [J)
INVOKEVIRTUAL (android/os/Parcel, writeLongArray, ([J)V)
RETURN
LABEL (L6)
}
}
@@ -0,0 +1,18 @@
// CURIOUS_ABOUT writeToParcel, createFromParcel
// WITH_RUNTIME
// IGNORE_BACKEND: JVM
import kotlinx.parcelize.*
import android.os.Parcelable
@Parcelize
data class Test(
val a: UByteArray,
val b: UShortArray,
val c: UIntArray,
val d: ULongArray,
val e: UByteArray?,
val f: UShortArray?,
val g: UIntArray?,
val h: ULongArray?,
) : Parcelable
@@ -380,6 +380,11 @@ public class ParcelizeBoxTestGenerated extends AbstractParcelizeBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/typeParameters.kt");
}
@TestMetadata("unsignedArrays.kt")
public void testUnsignedArrays() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/unsignedArrays.kt");
}
@TestMetadata("valueClassWrapper.kt")
public void testValueClassWrapper() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/valueClassWrapper.kt");
@@ -139,4 +139,9 @@ public class ParcelizeBytecodeListingTestGenerated extends AbstractParcelizeByte
public void testSize() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/size.kt");
}
@TestMetadata("unsignedPrimitiveArrays.kt")
public void testUnsignedPrimitiveArrays() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/unsignedPrimitiveArrays.kt");
}
}
@@ -380,6 +380,11 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/typeParameters.kt");
}
@TestMetadata("unsignedArrays.kt")
public void testUnsignedArrays() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/unsignedArrays.kt");
}
@TestMetadata("valueClassWrapper.kt")
public void testValueClassWrapper() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/valueClassWrapper.kt");
@@ -139,4 +139,9 @@ public class ParcelizeIrBytecodeListingTestGenerated extends AbstractParcelizeIr
public void testSize() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/size.kt");
}
@TestMetadata("unsignedPrimitiveArrays.kt")
public void testUnsignedPrimitiveArrays() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/unsignedPrimitiveArrays.kt");
}
}