diff --git a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializer.kt b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializer.kt index d3f6c135452..024ef4201a2 100644 --- a/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializer.kt +++ b/plugins/android-extensions/android-extensions-compiler/src/org/jetbrains/kotlin/android/parcel/serializers/ParcelSerializer.kt @@ -22,10 +22,13 @@ import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassKind import org.jetbrains.kotlin.descriptors.Modality import org.jetbrains.kotlin.incremental.components.NoLookupLocation +import org.jetbrains.kotlin.load.java.descriptors.JavaClassDescriptor import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe +import org.jetbrains.kotlin.resolve.scopes.MemberScope +import org.jetbrains.kotlin.resolve.source.PsiSourceElement import org.jetbrains.kotlin.synthetic.isVisibleOutside import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils @@ -199,10 +202,17 @@ interface ParcelSerializer { type.isParcelable() -> { val clazz = type.constructor.declarationDescriptor as? ClassDescriptor - if (clazz != null && clazz.modality == Modality.FINAL) { - val creatorVar = clazz.staticScope.getContributedVariables( + if (clazz != null && clazz.modality == Modality.FINAL && clazz.source is PsiSourceElement) { + + fun MemberScope.findCreatorField() = getContributedVariables( Name.identifier("CREATOR"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS).firstOrNull() + val creatorVar = when (clazz) { + is JavaClassDescriptor -> clazz.staticScope.findCreatorField() + else -> clazz.companionObjectDescriptor?.unsubstitutedMemberScope?.findCreatorField() + ?.takeIf { it.annotations.hasAnnotation(FqName(JvmField::class.java.name)) } + } + val creatorAsmType = when { creatorVar != null -> typeMapper.mapTypeSafe(creatorVar.type) clazz.isParcelize -> Type.getObjectType(asmType.internalName + "\$Creator") diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.kt new file mode 100644 index 00000000000..1ae618b5fb1 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.kt @@ -0,0 +1,11 @@ +// CURIOUS_ABOUT writeToParcel, createFromParcel, +// WITH_RUNTIME + +package test + +import kotlinx.android.parcel.* +import android.os.* +import android.accounts.Account + +@Parcelize +class Foo(val kp: Account): Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.txt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.txt new file mode 100644 index 00000000000..40e5e4143bd --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.txt @@ -0,0 +1,56 @@ +public final class test/Foo$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) + NEW + DUP + ALOAD (1) + LDC (Ltest/Foo;) + INVOKEVIRTUAL (java/lang/Class, getClassLoader, ()Ljava/lang/ClassLoader;) + INVOKEVIRTUAL (android/os/Parcel, readParcelable, (Ljava/lang/ClassLoader;)Landroid/os/Parcelable;) + CHECKCAST + INVOKESPECIAL (test/Foo, , (Landroid/accounts/Account;)V) + ARETURN + LABEL (L1) + } + + public final test.Foo[] newArray(int p0) +} + +public final class test/Foo : java/lang/Object, android/os/Parcelable { + public final static test.Foo$Creator CREATOR + + private final android.accounts.Account kp + + static void () { + NEW + DUP + INVOKESPECIAL (test/Foo$Creator, , ()V) + PUTSTATIC (CREATOR, Ltest/Foo$Creator;) + RETURN + } + + public void (android.accounts.Account p0) + + public final int describeContents() + + public final android.accounts.Account getKp() + + public final 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) + GETFIELD (kp, Landroid/accounts/Account;) + ILOAD (2) + INVOKEVIRTUAL (android/os/Parcel, writeParcelable, (Landroid/os/Parcelable;I)V) + RETURN + LABEL (L1) + } +} diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.kt new file mode 100644 index 00000000000..a5b42397877 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.kt @@ -0,0 +1,40 @@ +// CURIOUS_ABOUT writeToParcel, createFromParcel, +// WITH_RUNTIME + +// FILE: KotlinParcelable.kt +package k +import android.os.* + +data class KotlinParcelable(var data: Int): Parcelable { + + override fun describeContents() = 1 + + override fun writeToParcel(dest: Parcel, flags: Int) { + dest.writeInt(data) + } + + companion object { + @JvmField + val CREATOR = Creator() + } + + class Creator : Parcelable.Creator { + override fun createFromParcel(source: Parcel): KotlinParcelable { + val data = source.readInt() + return KotlinParcelable(data) + } + + override fun newArray(size: Int) = arrayOfNulls(size) + } +} + + +// FILE: test.kt +package test + +import kotlinx.android.parcel.* +import android.os.* +import k.* + +@Parcelize +class Foo(val kp: KotlinParcelable): Parcelable \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.txt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.txt new file mode 100644 index 00000000000..a009ad2781a --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.txt @@ -0,0 +1,160 @@ +public final class k/KotlinParcelable$Companion : java/lang/Object { + private void () + + public void (kotlin.jvm.internal.DefaultConstructorMarker p0) +} + +public final class k/KotlinParcelable$Creator : java/lang/Object, android/os/Parcelable$Creator { + public void () + + public k.KotlinParcelable createFromParcel(android.os.Parcel p0) { + LABEL (L0) + ALOAD (1) + LDC (source) + INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V) + LABEL (L1) + LINENUMBER (23) + ALOAD (1) + INVOKEVIRTUAL (android/os/Parcel, readInt, ()I) + ISTORE (2) + LABEL (L2) + LINENUMBER (24) + NEW + DUP + ILOAD (2) + INVOKESPECIAL (k/KotlinParcelable, , (I)V) + ARETURN + LABEL (L3) + } + + public java.lang.Object createFromParcel(android.os.Parcel p0) { + LABEL (L0) + LINENUMBER (21) + ALOAD (0) + ALOAD (1) + INVOKEVIRTUAL (k/KotlinParcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Lk/KotlinParcelable;) + ARETURN + } + + public k.KotlinParcelable[] newArray(int p0) + + public java.lang.Object[] newArray(int p0) +} + +public final class k/KotlinParcelable : java/lang/Object, android/os/Parcelable { + public final static k.KotlinParcelable$Creator CREATOR + + public final static k.KotlinParcelable$Companion Companion + + private int data + + static void () { + NEW + DUP + ACONST_NULL + INVOKESPECIAL (k/KotlinParcelable$Companion, , (Lkotlin/jvm/internal/DefaultConstructorMarker;)V) + PUTSTATIC (Companion, Lk/KotlinParcelable$Companion;) + LABEL (L0) + LINENUMBER (18) + NEW + DUP + INVOKESPECIAL (k/KotlinParcelable$Creator, , ()V) + PUTSTATIC (CREATOR, Lk/KotlinParcelable$Creator;) + RETURN + } + + public void (int p0) + + public final int component1() + + public final k.KotlinParcelable copy(int p0) + + public static k.KotlinParcelable copy$default(k.KotlinParcelable p0, int p1, int p2, java.lang.Object p3) + + public int describeContents() + + public boolean equals(java.lang.Object p0) + + public final int getData() + + public int hashCode() + + public final void setData(int p0) + + public java.lang.String toString() + + public void writeToParcel(android.os.Parcel p0, int p1) { + LABEL (L0) + ALOAD (1) + LDC (dest) + INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V) + LABEL (L1) + LINENUMBER (13) + ALOAD (1) + ALOAD (0) + GETFIELD (data, I) + INVOKEVIRTUAL (android/os/Parcel, writeInt, (I)V) + LABEL (L2) + LINENUMBER (14) + RETURN + LABEL (L3) + } +} + +public final class test/Foo$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) + NEW + DUP + ALOAD (1) + GETSTATIC (CREATOR, Lk/KotlinParcelable$Creator;) + SWAP + INVOKEINTERFACE (android/os/Parcelable$Creator, createFromParcel, (Landroid/os/Parcel;)Ljava/lang/Object;) + CHECKCAST + INVOKESPECIAL (test/Foo, , (Lk/KotlinParcelable;)V) + ARETURN + LABEL (L1) + } + + public final test.Foo[] newArray(int p0) +} + +public final class test/Foo : java/lang/Object, android/os/Parcelable { + public final static test.Foo$Creator CREATOR + + private final k.KotlinParcelable kp + + static void () { + NEW + DUP + INVOKESPECIAL (test/Foo$Creator, , ()V) + PUTSTATIC (CREATOR, Ltest/Foo$Creator;) + RETURN + } + + public void (k.KotlinParcelable p0) + + public final int describeContents() + + public final k.KotlinParcelable getKp() + + public final 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) + GETFIELD (kp, Lk/KotlinParcelable;) + SWAP + LDC (0) + INVOKEINTERFACE (android/os/Parcelable, writeToParcel, (Landroid/os/Parcel;I)V) + RETURN + LABEL (L1) + } +} diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java index d0388a27337..aa3f3aadfd2 100644 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBytecodeListingTestGenerated.java @@ -42,6 +42,18 @@ public class ParcelBytecodeListingTestGenerated extends AbstractParcelBytecodeLi doTest(fileName); } + @TestMetadata("customParcelablesDifferentModule.kt") + public void testCustomParcelablesDifferentModule() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesDifferentModule.kt"); + doTest(fileName); + } + + @TestMetadata("customParcelablesSameModule.kt") + public void testCustomParcelablesSameModule() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customParcelablesSameModule.kt"); + doTest(fileName); + } + @TestMetadata("customSimple.kt") public void testCustomSimple() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.kt");