Parcelize: Fix infinite recursive loop for zero-parameter Parcelable classes (KT-25839)

This commit is contained in:
Yan Zhulanow
2018-06-04 17:40:55 +03:00
parent 475a80b641
commit 8c05769745
7 changed files with 157 additions and 11 deletions
@@ -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 {
@@ -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
}
}
}