diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt index d149278f8b2..3402b779ab5 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/ParcelableCodegenExtension.kt @@ -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 { + 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 { diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializers.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializers.kt index d4dfaeec534..7a39c5b3c74 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializers.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializers.kt @@ -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, "", "()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 + } } } diff --git a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/parcel/ParcelBoxTest.kt b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/parcel/ParcelBoxTest.kt index b0d5aa91599..1c4545ad89e 100644 --- a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/parcel/ParcelBoxTest.kt +++ b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/android/parcel/ParcelBoxTest.kt @@ -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() { diff --git a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java index 62f2bc2fd14..0e03bc11817 100644 --- a/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java +++ b/plugins/android-extensions/android-extensions-compiler/test/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java @@ -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"); diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/box/kt25839.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/box/kt25839.kt new file mode 100644 index 00000000000..39520c1bc4f --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/box/kt25839.kt @@ -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(parcel) + readFromParcel(parcel) +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.kt new file mode 100644 index 00000000000..ab73e342017 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.kt @@ -0,0 +1,10 @@ +// CURIOUS_ABOUT writeToParcel, createFromParcel +// WITH_RUNTIME + +package test + +import kotlinx.android.parcel.* +import android.os.Parcelable + +@Parcelize +class SomeClass : Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.txt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.txt new file mode 100644 index 00000000000..73baf54d41b --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/kt25839.txt @@ -0,0 +1,60 @@ +public final class test/SomeClass$Creator : java/lang/Object, android/os/Parcelable$Creator { + public void () + + 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, , ()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 () + + public void () + + 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) + } +} \ No newline at end of file