Parcelize: Fix infinite recursive loop for zero-parameter Parcelable classes (KT-25839)
This commit is contained in:
+15
-2
@@ -168,7 +168,12 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
if (properties.isEmpty()) {
|
||||
val entityType = this@writeWriteToParcel.defaultType
|
||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
||||
val serializer = ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
val serializer = if (this@writeWriteToParcel.kind == ClassKind.CLASS) {
|
||||
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
|
||||
} else {
|
||||
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
}
|
||||
|
||||
v.load(1, parcelAsmType)
|
||||
v.load(0, containerAsmType)
|
||||
serializer.writeValue(v)
|
||||
@@ -220,6 +225,10 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
codegen: ImplementationBodyCodegen,
|
||||
parcelableClass: ClassDescriptor
|
||||
): List<PropertyToSerialize> {
|
||||
if (parcelableClass.kind != ClassKind.CLASS) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
|
||||
|
||||
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
|
||||
@@ -271,7 +280,11 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
||||
if (properties.isEmpty()) {
|
||||
val entityType = parcelableClass.defaultType
|
||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
||||
val serializer = ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
val serializer = if (parcelableClass.kind == ClassKind.CLASS) {
|
||||
NullAwareParcelSerializerWrapper(ZeroParameterClassSerializer(asmType, entityType))
|
||||
} else {
|
||||
ParcelSerializer.get(entityType, asmType, globalContext, strict = true)
|
||||
}
|
||||
v.load(1, parcelAsmType)
|
||||
serializer.readValue(v)
|
||||
} else {
|
||||
|
||||
+38
-9
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.codegen.FrameMap
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
|
||||
import org.jetbrains.kotlin.codegen.useTmpVar
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||
import org.jetbrains.kotlin.types.KotlinType
|
||||
import org.jetbrains.org.objectweb.asm.Label
|
||||
import org.jetbrains.org.objectweb.asm.Type
|
||||
@@ -56,7 +57,7 @@ internal class TypeParcelerParcelSerializer(
|
||||
boxTypeIfNeeded(v) // -> parcel, (boxed)value
|
||||
|
||||
v.swap() // -> value, parcel
|
||||
putObjectInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> value, parcel, parceler
|
||||
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> value, parcel, parceler
|
||||
v.dupX2() // -> parceler, value, parcel, parceler
|
||||
v.pop() // -> parceler, value, parcel
|
||||
v.load(2, Type.INT_TYPE) // -> parceler, value, parcel, flags
|
||||
@@ -65,7 +66,7 @@ internal class TypeParcelerParcelSerializer(
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
// -> parcel
|
||||
putObjectInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
|
||||
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
|
||||
v.swap() // -> parceler, parcel
|
||||
v.invokeinterface(PARCELER_TYPE.internalName, "create", "(Landroid/os/Parcel;)Ljava/lang/Object;") // -> obj
|
||||
unboxTypeIfNeeded(v)
|
||||
@@ -475,17 +476,45 @@ internal class ObjectParcelSerializer(
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.pop()
|
||||
putObjectInstanceOnStack(type, asmType, typeMapper, v)
|
||||
putObjectOrClassInstanceOnStack(type, asmType, typeMapper, v)
|
||||
}
|
||||
}
|
||||
|
||||
private fun putObjectInstanceOnStack(type: KotlinType, asmType: Type, typeMapper: KotlinTypeMapper, v: InstructionAdapter) {
|
||||
internal class ZeroParameterClassSerializer(
|
||||
override val asmType: Type,
|
||||
type: KotlinType
|
||||
) : ParcelSerializer {
|
||||
val clazz = type.constructor.declarationDescriptor as ClassDescriptor
|
||||
|
||||
init {
|
||||
assert(clazz.kind == ClassKind.CLASS)
|
||||
}
|
||||
|
||||
override fun writeValue(v: InstructionAdapter) {
|
||||
v.pop2()
|
||||
}
|
||||
|
||||
override fun readValue(v: InstructionAdapter) {
|
||||
v.pop()
|
||||
|
||||
val constructor = clazz.unsubstitutedPrimaryConstructor
|
||||
assert(constructor == null || constructor.valueParameters.isEmpty())
|
||||
v.anew(asmType)
|
||||
v.dup()
|
||||
v.invokespecial(asmType.internalName, "<init>", "()V", false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun putObjectOrClassInstanceOnStack(type: KotlinType, asmType: Type, typeMapper: KotlinTypeMapper, v: InstructionAdapter) {
|
||||
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||
if (clazz != null && clazz.isCompanionObject) {
|
||||
val outerClass = clazz.containingDeclaration as? ClassDescriptor
|
||||
if (outerClass != null) {
|
||||
v.getstatic(typeMapper.mapType(outerClass.defaultType).internalName, clazz.name.asString(), asmType.descriptor)
|
||||
return
|
||||
|
||||
if (clazz != null) {
|
||||
if (clazz.isCompanionObject) {
|
||||
val outerClass = clazz.containingDeclaration as? ClassDescriptor
|
||||
if (outerClass != null) {
|
||||
v.getstatic(typeMapper.mapType(outerClass.defaultType).internalName, clazz.name.asString(), asmType.descriptor)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
@@ -60,6 +60,7 @@ class ParcelBoxTest : AbstractParcelBoxTest() {
|
||||
fun testEnumObject() = doTest("enumObject")
|
||||
fun testIntArray() = doTest("intArray")
|
||||
fun testOpenParcelize() = doTest("openParcelize")
|
||||
fun testKt25839() = doTest("kt25839")
|
||||
}
|
||||
|
||||
class ParcelBoxTestWithSerializableLikeExtension : AbstractParcelBoxTest() {
|
||||
|
||||
+5
@@ -69,6 +69,11 @@ public class ParcelBytecodeListingTestGenerated extends AbstractParcelBytecodeLi
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/IBinderIInterface.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("kt25839.kt")
|
||||
public void testKt25839() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("listInsideList.kt")
|
||||
public void testListInsideList() throws Exception {
|
||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/listInsideList.kt");
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
@file:JvmName("TestKt")
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User : Parcelable
|
||||
|
||||
@Parcelize
|
||||
class User2() : Parcelable
|
||||
|
||||
fun box() = parcelTest { parcel ->
|
||||
val user = User()
|
||||
val user2 = User2()
|
||||
|
||||
user.writeToParcel(parcel, 0)
|
||||
user2.writeToParcel(parcel, 0)
|
||||
|
||||
val bytes = parcel.marshall()
|
||||
parcel.unmarshall(bytes, 0, bytes.size)
|
||||
|
||||
readFromParcel<User>(parcel)
|
||||
readFromParcel<User2>(parcel)
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// CURIOUS_ABOUT writeToParcel, createFromParcel
|
||||
// WITH_RUNTIME
|
||||
|
||||
package test
|
||||
|
||||
import kotlinx.android.parcel.*
|
||||
import android.os.Parcelable
|
||||
|
||||
@Parcelize
|
||||
class SomeClass : Parcelable
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
public final class test/SomeClass$Creator : java/lang/Object, android/os/Parcelable$Creator {
|
||||
public void <init>()
|
||||
|
||||
public final java.lang.Object createFromParcel(android.os.Parcel p0) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (in)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
LABEL (L1)
|
||||
NEW
|
||||
DUP
|
||||
ALOAD (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, readInt, ()I)
|
||||
IFEQ (L2)
|
||||
NEW
|
||||
DUP
|
||||
INVOKESPECIAL (test/SomeClass, <init>, ()V)
|
||||
GOTO (L3)
|
||||
LABEL (L2)
|
||||
ACONST_NULL
|
||||
LABEL (L3)
|
||||
ARETURN
|
||||
LABEL (L4)
|
||||
}
|
||||
|
||||
public final java.lang.Object[] newArray(int p0)
|
||||
}
|
||||
|
||||
public final class test/SomeClass : java/lang/Object, android/os/Parcelable {
|
||||
public final static android.os.Parcelable$Creator CREATOR
|
||||
|
||||
static void <clinit>()
|
||||
|
||||
public void <init>()
|
||||
|
||||
public int describeContents()
|
||||
|
||||
public void writeToParcel(android.os.Parcel p0, int p1) {
|
||||
LABEL (L0)
|
||||
ALOAD (1)
|
||||
LDC (parcel)
|
||||
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V)
|
||||
ALOAD (1)
|
||||
ALOAD (0)
|
||||
DUP
|
||||
IFNULL (L1)
|
||||
ALOAD (1)
|
||||
LDC (1)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
POP2
|
||||
GOTO (L2)
|
||||
LABEL (L1)
|
||||
POP
|
||||
LDC (0)
|
||||
INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V)
|
||||
LABEL (L2)
|
||||
RETURN
|
||||
LABEL (L3)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user