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()) {
|
if (properties.isEmpty()) {
|
||||||
val entityType = this@writeWriteToParcel.defaultType
|
val entityType = this@writeWriteToParcel.defaultType
|
||||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
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(1, parcelAsmType)
|
||||||
v.load(0, containerAsmType)
|
v.load(0, containerAsmType)
|
||||||
serializer.writeValue(v)
|
serializer.writeValue(v)
|
||||||
@@ -220,6 +225,10 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
|||||||
codegen: ImplementationBodyCodegen,
|
codegen: ImplementationBodyCodegen,
|
||||||
parcelableClass: ClassDescriptor
|
parcelableClass: ClassDescriptor
|
||||||
): List<PropertyToSerialize> {
|
): List<PropertyToSerialize> {
|
||||||
|
if (parcelableClass.kind != ClassKind.CLASS) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
|
val constructor = parcelableClass.constructors.firstOrNull { it.isPrimary } ?: return emptyList()
|
||||||
|
|
||||||
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
|
val propertiesToSerialize = constructor.valueParameters.mapNotNull { param ->
|
||||||
@@ -271,7 +280,11 @@ open class ParcelableCodegenExtension : ExpressionCodegenExtension {
|
|||||||
if (properties.isEmpty()) {
|
if (properties.isEmpty()) {
|
||||||
val entityType = parcelableClass.defaultType
|
val entityType = parcelableClass.defaultType
|
||||||
val asmType = codegen.state.typeMapper.mapType(entityType)
|
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)
|
v.load(1, parcelAsmType)
|
||||||
serializer.readValue(v)
|
serializer.readValue(v)
|
||||||
} else {
|
} 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.state.KotlinTypeMapper
|
||||||
import org.jetbrains.kotlin.codegen.useTmpVar
|
import org.jetbrains.kotlin.codegen.useTmpVar
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassKind
|
||||||
import org.jetbrains.kotlin.types.KotlinType
|
import org.jetbrains.kotlin.types.KotlinType
|
||||||
import org.jetbrains.org.objectweb.asm.Label
|
import org.jetbrains.org.objectweb.asm.Label
|
||||||
import org.jetbrains.org.objectweb.asm.Type
|
import org.jetbrains.org.objectweb.asm.Type
|
||||||
@@ -56,7 +57,7 @@ internal class TypeParcelerParcelSerializer(
|
|||||||
boxTypeIfNeeded(v) // -> parcel, (boxed)value
|
boxTypeIfNeeded(v) // -> parcel, (boxed)value
|
||||||
|
|
||||||
v.swap() // -> value, parcel
|
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.dupX2() // -> parceler, value, parcel, parceler
|
||||||
v.pop() // -> parceler, value, parcel
|
v.pop() // -> parceler, value, parcel
|
||||||
v.load(2, Type.INT_TYPE) // -> parceler, value, parcel, flags
|
v.load(2, Type.INT_TYPE) // -> parceler, value, parcel, flags
|
||||||
@@ -65,7 +66,7 @@ internal class TypeParcelerParcelSerializer(
|
|||||||
|
|
||||||
override fun readValue(v: InstructionAdapter) {
|
override fun readValue(v: InstructionAdapter) {
|
||||||
// -> parcel
|
// -> parcel
|
||||||
putObjectInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
|
putObjectOrClassInstanceOnStack(parcelerType, parcelerAsmType, typeMapper, v) // -> parcel, parceler
|
||||||
v.swap() // -> parceler, parcel
|
v.swap() // -> parceler, parcel
|
||||||
v.invokeinterface(PARCELER_TYPE.internalName, "create", "(Landroid/os/Parcel;)Ljava/lang/Object;") // -> obj
|
v.invokeinterface(PARCELER_TYPE.internalName, "create", "(Landroid/os/Parcel;)Ljava/lang/Object;") // -> obj
|
||||||
unboxTypeIfNeeded(v)
|
unboxTypeIfNeeded(v)
|
||||||
@@ -475,17 +476,45 @@ internal class ObjectParcelSerializer(
|
|||||||
|
|
||||||
override fun readValue(v: InstructionAdapter) {
|
override fun readValue(v: InstructionAdapter) {
|
||||||
v.pop()
|
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
|
val clazz = type.constructor.declarationDescriptor as? ClassDescriptor
|
||||||
if (clazz != null && clazz.isCompanionObject) {
|
|
||||||
val outerClass = clazz.containingDeclaration as? ClassDescriptor
|
if (clazz != null) {
|
||||||
if (outerClass != null) {
|
if (clazz.isCompanionObject) {
|
||||||
v.getstatic(typeMapper.mapType(outerClass.defaultType).internalName, clazz.name.asString(), asmType.descriptor)
|
val outerClass = clazz.containingDeclaration as? ClassDescriptor
|
||||||
return
|
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 testEnumObject() = doTest("enumObject")
|
||||||
fun testIntArray() = doTest("intArray")
|
fun testIntArray() = doTest("intArray")
|
||||||
fun testOpenParcelize() = doTest("openParcelize")
|
fun testOpenParcelize() = doTest("openParcelize")
|
||||||
|
fun testKt25839() = doTest("kt25839")
|
||||||
}
|
}
|
||||||
|
|
||||||
class ParcelBoxTestWithSerializableLikeExtension : AbstractParcelBoxTest() {
|
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");
|
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")
|
@TestMetadata("listInsideList.kt")
|
||||||
public void testListInsideList() throws Exception {
|
public void testListInsideList() throws Exception {
|
||||||
runTest("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/listInsideList.kt");
|
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