diff --git a/.idea/dictionaries/yan.xml b/.idea/dictionaries/yan.xml index 750f689a5b3..3c5a40afa14 100644 --- a/.idea/dictionaries/yan.xml +++ b/.idea/dictionaries/yan.xml @@ -1,8 +1,10 @@ + deserializes impls kapt + parceler uast 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 4c9a9dc5326..8316271002f 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 @@ -16,9 +16,12 @@ package org.jetbrains.kotlin.android.parcel +import kotlinx.android.parcel.Parceler import org.jetbrains.kotlin.android.parcel.ParcelableSyntheticComponent.ComponentKind.* import org.jetbrains.kotlin.android.parcel.ParcelableResolveExtension.Companion.createMethod +import org.jetbrains.kotlin.android.parcel.serializers.PARCEL_TYPE import org.jetbrains.kotlin.android.parcel.serializers.ParcelSerializer +import org.jetbrains.kotlin.builtins.KotlinBuiltIns import org.jetbrains.kotlin.codegen.ExpressionCodegen import org.jetbrains.kotlin.codegen.ImplementationBodyCodegen import org.jetbrains.kotlin.codegen.extensions.ExpressionCodegenExtension @@ -33,6 +36,7 @@ import org.jetbrains.kotlin.codegen.OwnerKind import org.jetbrains.kotlin.codegen.context.ClassContext import org.jetbrains.kotlin.codegen.writeSyntheticClassMetadata import org.jetbrains.kotlin.descriptors.impl.ClassDescriptorImpl +import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.incremental.components.NoLookupLocation.* import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.resolve.DescriptorFactory @@ -40,6 +44,7 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe import org.jetbrains.kotlin.resolve.descriptorUtil.module import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.types.KotlinType +import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.Variance import org.jetbrains.org.objectweb.asm.Opcodes.* import org.jetbrains.org.objectweb.asm.Type @@ -48,6 +53,9 @@ import java.io.FileDescriptor class ParcelableCodegenExtension : ExpressionCodegenExtension { private companion object { private val FILE_DESCRIPTOR_FQNAME = FqName(FileDescriptor::class.java.canonicalName) + private val PARCELER_FQNAME = FqName(Parceler::class.java.canonicalName) + + fun KotlinType.isParceler() = constructor.declarationDescriptor?.fqNameSafe == PARCELER_FQNAME } override fun generateClassSyntheticParts(codegen: ImplementationBodyCodegen) { @@ -60,32 +68,54 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension { val parcelClassType = ParcelableResolveExtension.resolveParcelClassType(parcelableClass.module) val parcelAsmType = codegen.typeMapper.mapType(parcelClassType) + val parcelerObject = parcelableClass.companionObjectDescriptor?.takeIf { + TypeUtils.getAllSupertypes(it.defaultType).any { it.isParceler() } + } + with (parcelableClass) { writeDescribeContentsFunction(codegen, propertiesToSerialize) - writeWriteToParcel(codegen, propertiesToSerialize, parcelAsmType) + writeWriteToParcel(codegen, propertiesToSerialize, parcelAsmType, parcelerObject) } writeCreatorAccessField(codegen, parcelableClass) - writeCreatorClass(codegen, parcelableClass, parcelClassType, parcelAsmType, propertiesToSerialize) + writeCreatorClass(codegen, parcelableClass, parcelClassType, parcelAsmType, parcelerObject, propertiesToSerialize) + } + + private fun getCompanionClassType(containerAsmType: Type, parcelerObject: ClassDescriptor): Pair { + val shortName = parcelerObject.name + return Pair(Type.getObjectType(containerAsmType.internalName + "\$$shortName"), shortName.asString()) } private fun ClassDescriptor.writeWriteToParcel( codegen: ImplementationBodyCodegen, properties: List>, - parcelAsmType: Type + parcelAsmType: Type, + parcelerObject: ClassDescriptor? ): Unit? { val containerAsmType = codegen.typeMapper.mapType(this.defaultType) return findFunction(WRITE_TO_PARCEL)?.write(codegen) { - for ((fieldName, type) in properties) { - val asmType = codegen.typeMapper.mapType(type) + if (parcelerObject != null) { + val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject) - v.load(1, parcelAsmType) + v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor) v.load(0, containerAsmType) - v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor) + v.load(1, PARCEL_TYPE) + v.load(2, Type.INT_TYPE) + v.invokevirtual(companionAsmType.internalName, "write", + "(${containerAsmType.descriptor}${PARCEL_TYPE.descriptor}I)V", false) + } + else { + for ((fieldName, type) in properties) { + val asmType = codegen.typeMapper.mapType(type) - val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper) - serializer.writeValue(v) + v.load(1, parcelAsmType) + v.load(0, containerAsmType) + v.getfield(containerAsmType.internalName, fieldName, asmType.descriptor) + + val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper) + serializer.writeValue(v) + } } v.areturn(Type.VOID_TYPE) @@ -135,26 +165,38 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension { creatorClass: ClassDescriptorImpl, parcelClassType: KotlinType, parcelAsmType: Type, + parcelerObject: ClassDescriptor?, properties: List> ) { val containerAsmType = codegen.typeMapper.mapType(parcelableClass) createMethod(creatorClass, CREATE_FROM_PARCEL, parcelableClass.defaultType, "in" to parcelClassType).write(codegen) { - v.anew(containerAsmType) - v.dup() + if (parcelerObject != null) { + val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject) - val asmConstructorParameters = StringBuilder() + v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor) + v.load(1, PARCEL_TYPE) + v.invokevirtual(companionAsmType.internalName, "create", + "(${PARCEL_TYPE.descriptor})${containerAsmType.descriptor}", false) + } + else { + v.anew(containerAsmType) + v.dup() - for ((_, type) in properties) { - val asmType = codegen.typeMapper.mapType(type) - asmConstructorParameters.append(asmType.descriptor) + val asmConstructorParameters = StringBuilder() - val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper) - v.load(1, parcelAsmType) - serializer.readValue(v) + for ((_, type) in properties) { + val asmType = codegen.typeMapper.mapType(type) + asmConstructorParameters.append(asmType.descriptor) + + val serializer = ParcelSerializer.get(type, asmType, codegen.typeMapper) + v.load(1, parcelAsmType) + serializer.readValue(v) + } + + v.invokespecial(containerAsmType.internalName, "", "($asmConstructorParameters)V", false) } - v.invokespecial(containerAsmType.internalName, "", "($asmConstructorParameters)V", false) v.areturn(containerAsmType) } } @@ -171,6 +213,7 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension { parcelableClass: ClassDescriptor, parcelClassType: KotlinType, parcelAsmType: Type, + parcelerObject: ClassDescriptor?, properties: List> ) { val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType) @@ -201,8 +244,8 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension { writeSyntheticClassMetadata(classBuilderForCreator, codegen.state) writeCreatorConstructor(codegenForCreator, creatorClass, creatorAsmType) - writeNewArrayMethod(codegenForCreator, parcelableClass, creatorClass) - writeCreateFromParcel(codegenForCreator, parcelableClass, creatorClass, parcelClassType, parcelAsmType, properties) + writeNewArrayMethod(codegenForCreator, parcelableClass, creatorClass, parcelerObject) + writeCreateFromParcel(codegenForCreator, parcelableClass, creatorClass, parcelClassType, parcelAsmType, parcelerObject, properties) classBuilderForCreator.done() } @@ -221,7 +264,8 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension { private fun writeNewArrayMethod( codegen: ImplementationBodyCodegen, parcelableClass: ClassDescriptor, - creatorClass: ClassDescriptorImpl + creatorClass: ClassDescriptorImpl, + parcelerObject: ClassDescriptor? ) { val builtIns = parcelableClass.builtIns val parcelableAsmType = codegen.typeMapper.mapType(parcelableClass) @@ -230,6 +274,29 @@ class ParcelableCodegenExtension : ExpressionCodegenExtension { builtIns.getArrayType(Variance.INVARIANT, parcelableClass.defaultType), "size" to builtIns.intType ).write(codegen) { + if (parcelerObject != null) { + val newArrayMethod = parcelerObject.unsubstitutedMemberScope + .getContributedFunctions(Name.identifier("newArray"), NoLookupLocation.WHEN_GET_ALL_DESCRIPTORS) + .filter { + it.typeParameters.isEmpty() + && it.kind == CallableMemberDescriptor.Kind.DECLARATION + && (it.valueParameters.size == 1 && KotlinBuiltIns.isInt(it.valueParameters[0].type)) + && !((it.containingDeclaration as? ClassDescriptor)?.defaultType?.isParceler() ?: true) + }.firstOrNull() + + if (newArrayMethod != null) { + val containerAsmType = codegen.typeMapper.mapType(parcelableClass.defaultType) + val (companionAsmType, companionFieldName) = getCompanionClassType(containerAsmType, parcelerObject) + + v.getstatic(containerAsmType.internalName, companionFieldName, companionAsmType.descriptor) + v.load(1, Type.INT_TYPE) + v.invokevirtual(companionAsmType.internalName, "newArray", "(I)[${containerAsmType.descriptor}", false) + v.areturn(Type.getType("[L$parcelableAsmType;")) + + return@write + } + } + v.load(1, Type.INT_TYPE) v.newarray(parcelableAsmType) v.areturn(Type.getType("[L$parcelableAsmType;")) 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 582d692ea42..2258010dd01 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 @@ -20,7 +20,7 @@ import org.jetbrains.org.objectweb.asm.Label import org.jetbrains.org.objectweb.asm.Type import org.jetbrains.org.objectweb.asm.commons.InstructionAdapter -private val PARCEL_TYPE = Type.getObjectType("android/os/Parcel") +internal val PARCEL_TYPE = Type.getObjectType("android/os/Parcel") internal object GenericParcelSerializer : ParcelSerializer { override val asmType: Type = Type.getObjectType("java/lang/Object") diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/box/customSimple.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/box/customSimple.kt new file mode 100644 index 00000000000..608a2debde8 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/box/customSimple.kt @@ -0,0 +1,34 @@ +// WITH_RUNTIME + +@file:JvmName("TestKt") +package test + +import kotlinx.android.parcel.* +import android.os.Parcel +import android.os.Parcelable + +@MagicParcel +data class User(val firstName: String, val secondName: String, val age: Int) : Parcelable { + private companion object : Parceler { + override fun User.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeString(secondName) + } + + override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), 0) + } +} + +fun box() = parcelTest { parcel -> + val user = User("John", "Smith", 20) + user.writeToParcel(parcel, 0) + + val bytes = parcel.marshall() + parcel.unmarshall(bytes, 0, bytes.size) + + val user2 = readFromParcel(parcel) + + assert(user.firstName == user2.firstName) + assert(user.secondName == user2.secondName) + assert(user2.age == 0) +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.kt new file mode 100644 index 00000000000..40a6d955eed --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.kt @@ -0,0 +1,18 @@ +// CURIOUS_ABOUT writeToParcel, createFromParcel, newArray + +import kotlinx.android.parcel.* +import android.os.Parcel +import android.os.Parcelable + +@MagicParcel +class User(val firstName: String, val lastName: String, val age: Int) : Parcelable { + private companion object : Parceler { + override fun User.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeString(lastName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), parcel.readInt()) + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.txt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.txt new file mode 100644 index 00000000000..ab2f97e0099 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.txt @@ -0,0 +1,96 @@ +public static class User$CREATOR : java/lang/Object, android/os/Parcelable$Creator { + public void () + + public final User createFromParcel(android.os.Parcel p0) { + LABEL (L0) + ALOAD (1) + LDC (in) + INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkParameterIsNotNull, (Ljava/lang/Object;Ljava/lang/String;)V) + GETSTATIC (Companion, LUser$Companion;) + ALOAD (1) + INVOKEVIRTUAL (User$Companion, create, (Landroid/os/Parcel;)LUser;) + ARETURN + LABEL (L1) + } + + public final User[] newArray(int p0) { + LABEL (L0) + ILOAD (1) + ANEWARRAY + ARETURN + LABEL (L1) + } +} + +final class User$Companion : java/lang/Object, kotlinx/android/parcel/Parceler { + private void () + + public void (kotlin.jvm.internal.DefaultConstructorMarker p0) + + public User create(android.os.Parcel p0) + + public android.os.Parcelable create(android.os.Parcel p0) + + public User[] newArray(int p0) { + LABEL (L0) + LINENUMBER (9) + ALOAD (0) + ILOAD (1) + INVOKESTATIC (kotlinx/android/parcel/Parceler$DefaultImpls, newArray, (Lkotlinx/android/parcel/Parceler;I)[Landroid/os/Parcelable;) + CHECKCAST + ARETURN + LABEL (L1) + } + + public android.os.Parcelable[] newArray(int p0) { + LABEL (L0) + LINENUMBER (9) + ALOAD (0) + ILOAD (1) + INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;) + CHECKCAST + ARETURN + } + + public void write(User p0, android.os.Parcel p1, int p2) + + public void write(android.os.Parcelable p0, android.os.Parcel p1, int p2) +} + +public final class User : java/lang/Object, android/os/Parcelable { + public final static User$CREATOR CREATOR + + public final static User$Companion Companion + + private final int age + + private final java.lang.String firstName + + private final java.lang.String lastName + + static void () + + public void (java.lang.String p0, java.lang.String p1, int p2) + + public final int describeContents() + + public final int getAge() + + public final java.lang.String getFirstName() + + public final java.lang.String getLastName() + + 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) + GETSTATIC (Companion, LUser$Companion;) + ALOAD (0) + ALOAD (1) + ILOAD (2) + INVOKEVIRTUAL (User$Companion, write, (LUser;Landroid/os/Parcel;I)V) + RETURN + LABEL (L1) + } +} diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.kt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.kt new file mode 100644 index 00000000000..81f56cb4e62 --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.kt @@ -0,0 +1,20 @@ +// CURIOUS_ABOUT newArray + +import kotlinx.android.parcel.* +import android.os.Parcel +import android.os.Parcelable + +@MagicParcel +class User(val firstName: String, val lastName: String, val age: Int) : Parcelable { + private companion object : Parceler { + override fun User.write(parcel: Parcel, flags: Int) { + parcel.writeString(firstName) + parcel.writeString(lastName) + parcel.writeInt(age) + } + + override fun create(parcel: Parcel) = User(parcel.readString(), parcel.readString(), parcel.readInt()) + + override fun newArray(size: Int) = arrayOfNulls(size) as Array + } +} \ No newline at end of file diff --git a/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.txt b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.txt new file mode 100644 index 00000000000..1d2b1c795df --- /dev/null +++ b/plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.txt @@ -0,0 +1,73 @@ +public static class User$CREATOR : java/lang/Object, android/os/Parcelable$Creator { + public void () + + public final User createFromParcel(android.os.Parcel p0) + + public final User[] newArray(int p0) { + LABEL (L0) + GETSTATIC (Companion, LUser$Companion;) + ILOAD (1) + INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;) + ARETURN + LABEL (L1) + } +} + +final class User$Companion : java/lang/Object, kotlinx/android/parcel/Parceler { + private void () + + public void (kotlin.jvm.internal.DefaultConstructorMarker p0) + + public User create(android.os.Parcel p0) + + public android.os.Parcelable create(android.os.Parcel p0) + + public User[] newArray(int p0) { + LABEL (L0) + LINENUMBER (18) + ILOAD (1) + ANEWARRAY + ARETURN + LABEL (L1) + } + + public android.os.Parcelable[] newArray(int p0) { + LABEL (L0) + LINENUMBER (9) + ALOAD (0) + ILOAD (1) + INVOKEVIRTUAL (User$Companion, newArray, (I)[LUser;) + CHECKCAST + ARETURN + } + + public void write(User p0, android.os.Parcel p1, int p2) + + public void write(android.os.Parcelable p0, android.os.Parcel p1, int p2) +} + +public final class User : java/lang/Object, android/os/Parcelable { + public final static User$CREATOR CREATOR + + public final static User$Companion Companion + + private final int age + + private final java.lang.String firstName + + private final java.lang.String lastName + + static void () + + public void (java.lang.String p0, java.lang.String p1, int p2) + + public final int describeContents() + + public final int getAge() + + public final java.lang.String getFirstName() + + public final java.lang.String getLastName() + + public final void writeToParcel(android.os.Parcel p0, int p1) +} diff --git a/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/parcel/Parceler.kt b/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/parcel/Parceler.kt new file mode 100644 index 00000000000..0684dc5c039 --- /dev/null +++ b/plugins/android-extensions/android-extensions-runtime/src/kotlinx/android/parcel/Parceler.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +@file:Suppress("unused") +package kotlinx.android.parcel + +import android.os.Parcel +import android.os.Parcelable + +interface Parceler { + /** + * Writes the [P] instance state to the [parcel]. + */ + fun P.write(parcel: Parcel, flags: Int) + + /** + * Reads the [P] instance state from the [parcel], constructs the new [P] instance and returns it. + */ + fun create(parcel: Parcel): P + + /** + * Returns a new [Array]

with the given array [size]. + */ + fun newArray(size: Int): Array

{ + throw NotImplementedError("Generated by Android Extensions automatically") + } +} \ No newline at end of file diff --git a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBoxTest.kt b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBoxTest.kt index e6407693834..dc75da1b3dc 100644 --- a/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBoxTest.kt +++ b/plugins/plugins-tests/tests/org/jetbrains/kotlin/android/parcel/ParcelBoxTest.kt @@ -42,4 +42,5 @@ class ParcelBoxTest : AbstractParcelBoxTest() { @Test fun sparseBooleanArray() = doTest("sparseBooleanArray") @Test fun bundle() = doTest("bundle") @Test fun sparseArrays() = doTest("sparseArrays") + @Test fun customSimple() = doTest("customSimple") } \ No newline at end of file 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 e012660435e..095ab919b77 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("customSimple.kt") + public void testCustomSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimple.kt"); + doTest(fileName); + } + + @TestMetadata("customSimpleWithNewArray.kt") + public void testCustomSimpleWithNewArray() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/customSimpleWithNewArray.kt"); + doTest(fileName); + } + @TestMetadata("listInsideList.kt") public void testListInsideList() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("plugins/android-extensions/android-extensions-compiler/testData/parcel/codegen/listInsideList.kt");