Support kotlinx.collections.immutable in kotlin-parcelize plugin

#KT-57685 Fixed

Co-authored-by: Ilya Gulya <ilyagulya@gmail.com>
This commit is contained in:
Ilya Gulya
2023-10-02 23:29:43 +00:00
committed by Space Team
parent e8853fd40b
commit f6b2c642c2
20 changed files with 416 additions and 54 deletions
@@ -65,6 +65,7 @@ dependencies {
parcelizeRuntimeForTests(project(":plugins:parcelize:parcelize-runtime")) { isTransitive = false }
parcelizeRuntimeForTests(project(":kotlin-android-extensions-runtime")) { isTransitive = false }
parcelizeRuntimeForTests(commonDependency("org.jetbrains.kotlinx:kotlinx-collections-immutable-jvm")) { isTransitive = false }
layoutLib("org.jetbrains.intellij.deps.android.tools:layoutlib:26.5.0") { isTransitive = false }
layoutLibApi("com.android.tools.layoutlib:layoutlib-api:26.5.0") { isTransitive = false }
@@ -5,6 +5,7 @@
package org.jetbrains.kotlin.parcelize
import org.jetbrains.kotlin.backend.common.extensions.IrPluginContext
import org.jetbrains.kotlin.backend.common.ir.addExtensionReceiver
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.InlineClassRepresentation
@@ -22,6 +23,7 @@ import org.jetbrains.kotlin.ir.symbols.*
import org.jetbrains.kotlin.ir.types.*
import org.jetbrains.kotlin.ir.util.createImplicitParameterDeclarationWithWrappedDescriptor
import org.jetbrains.kotlin.ir.util.defaultType
import org.jetbrains.kotlin.name.CallableId
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.parcelize.ParcelizeNames.CREATE_FROM_PARCEL_NAME
@@ -32,10 +34,11 @@ import org.jetbrains.kotlin.parcelize.ParcelizeNames.WRITE_TO_PARCEL_NAME
// hence contain just enough information to produce correct JVM bytecode for *calls*. In particular, we omit generic types and
// supertypes, which are not needed to produce correct bytecode.
class AndroidSymbols(
val irBuiltIns: IrBuiltIns,
private val moduleFragment: IrModuleFragment
private val pluginContext: IrPluginContext,
private val moduleFragment: IrModuleFragment,
) {
private val irFactory: IrFactory = IrFactoryImpl
val irBuiltIns: IrBuiltIns = pluginContext.irBuiltIns
private val javaIo: IrPackageFragment = createPackage("java.io")
private val javaLang: IrPackageFragment = createPackage("java.lang")
@@ -499,6 +502,36 @@ class AndroidSymbols(
isStatic = true
}.symbol
private val kotlinxCollectionsImmutable = FqName(kotlinxImmutable())
private val kotlinCollections = FqName("kotlin.collections")
private val kotlinIterable: FqName = kotlinCollections.child(Name.identifier("Iterable"))
private val kotlinMap: FqName = kotlinCollections.child(Name.identifier("Map"))
private fun findKotlinxImmutableCollectionExtensionFunction(
receiver: FqName,
functionName: String,
): IrSimpleFunctionSymbol {
val callableId = CallableId(kotlinxCollectionsImmutable, Name.identifier(functionName))
return pluginContext.referenceFunctions(callableId)
.firstOrNull {
it.owner.extensionReceiverParameter?.type?.classFqName == receiver &&
it.owner.valueParameters.isEmpty()
}
?: error("Function from kotlinx.collections.immutable is not found on classpath: $callableId")
}
val kotlinIterableToPersistentListExtension: IrSimpleFunctionSymbol by lazy {
findKotlinxImmutableCollectionExtensionFunction(kotlinIterable, "toPersistentList")
}
val kotlinIterableToPersistentSetExtension: IrSimpleFunctionSymbol by lazy {
findKotlinxImmutableCollectionExtensionFunction(kotlinIterable, "toPersistentSet")
}
val kotlinMapToPersistentMapExtension: IrSimpleFunctionSymbol by lazy {
findKotlinxImmutableCollectionExtensionFunction(kotlinMap, "toPersistentMap")
}
val unsafeCoerceIntrinsic: IrSimpleFunctionSymbol =
irFactory.buildFun {
name = Name.special("<unsafe-coerce>")
@@ -15,6 +15,35 @@ import org.jetbrains.kotlin.ir.util.*
import org.jetbrains.kotlin.parcelize.ParcelizeNames.RAW_VALUE_ANNOTATION_FQ_NAMES
class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
private val supportedBySimpleListSerializer = setOf(
"kotlin.collections.List", "kotlin.collections.MutableList", "kotlin.collections.ArrayList",
"java.util.List", "java.util.ArrayList",
*BuiltinParcelableTypes.IMMUTABLE_LIST_FQNAMES.toTypedArray()
)
// TODO: More java collections?
// TODO: Add tests for all of these types, not just some common ones...
private val supportedByListSerializer = setOf(
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List",
"kotlin.collections.ArrayList", "java.util.ArrayList",
"kotlin.collections.ArrayDeque", "java.util.ArrayDeque",
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set",
"kotlin.collections.HashSet", "java.util.HashSet",
"kotlin.collections.LinkedHashSet", "java.util.LinkedHashSet",
"java.util.NavigableSet", "java.util.SortedSet",
*BuiltinParcelableTypes.IMMUTABLE_LIST_FQNAMES.toTypedArray(),
*BuiltinParcelableTypes.IMMUTABLE_SET_FQNAMES.toTypedArray(),
)
private val supportedByMapSerializer = setOf(
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map",
"kotlin.collections.HashMap", "java.util.HashMap",
"kotlin.collections.LinkedHashMap", "java.util.LinkedHashMap",
"java.util.SortedMap", "java.util.NavigableMap", "java.util.TreeMap",
"java.util.concurrent.ConcurrentHashMap",
*BuiltinParcelableTypes.IMMUTABLE_MAP_FQNAMES.toTypedArray(),
)
/**
* Resolve the given [irType] to a corresponding [IrParcelSerializer]. This depends on the TypeParcelers which
* are currently in [scope], as well as the type of the enclosing Parceleable class [parcelizeType], which is needed
@@ -192,43 +221,56 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
)
}
// TODO: More java collections?
// TODO: Add tests for all of these types, not just some common ones...
// FIXME: Is the support for ArrayDeque missing in the old BE?
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List",
"kotlin.collections.ArrayList", "java.util.ArrayList",
"kotlin.collections.ArrayDeque", "java.util.ArrayDeque",
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set",
"kotlin.collections.HashSet", "java.util.HashSet",
"kotlin.collections.LinkedHashSet", "java.util.LinkedHashSet",
"java.util.NavigableSet", "java.util.SortedSet" -> {
in supportedByListSerializer -> {
val elementType = (irType as IrSimpleType).arguments.single().upperBound(irBuiltIns)
if (!scope.hasCustomSerializer(elementType) && classifierFqName in setOf(
"kotlin.collections.List", "kotlin.collections.MutableList", "kotlin.collections.ArrayList",
"java.util.List", "java.util.ArrayList"
)
if (!scope.hasCustomSerializer(elementType) &&
classifierFqName in supportedBySimpleListSerializer
) {
when (elementType.erasedUpperBound.fqNameWhenAvailable?.asString()) {
"android.os.IBinder" ->
return iBinderListSerializer
"kotlin.String", "java.lang.String" ->
return stringListSerializer
val elementTypeAsString = elementType.erasedUpperBound.fqNameWhenAvailable?.asString()
val simpleSerializer =
if (classifierFqName in BuiltinParcelableTypes.IMMUTABLE_LIST_FQNAMES) {
when (elementTypeAsString) {
"android.os.IBinder" -> iBinderPersistentListSerializer
"kotlin.String", "java.lang.String" -> stringPersistentListSerializer
else -> null
}
} else {
when (elementTypeAsString) {
"android.os.IBinder" -> iBinderListSerializer
"kotlin.String", "java.lang.String" -> stringListSerializer
else -> null
}
}
if (simpleSerializer != null) {
return simpleSerializer
}
}
val listSerializer = IrListParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
val actualSerializer =
when (classifierFqName) {
in BuiltinParcelableTypes.IMMUTABLE_LIST_FQNAMES -> IrExtensionFunctionOnReadCallingSerializer(
delegated = listSerializer,
converterExtensionFunction = symbols.kotlinIterableToPersistentListExtension
)
in BuiltinParcelableTypes.IMMUTABLE_SET_FQNAMES -> IrExtensionFunctionOnReadCallingSerializer(
delegated = listSerializer,
converterExtensionFunction = symbols.kotlinIterableToPersistentSetExtension
)
else -> listSerializer
}
return wrapNullableSerializerIfNeeded(
irType,
IrListParcelSerializer(classifier, elementType, get(elementType, scope, parcelizeType, strict()))
actualSerializer
)
}
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map",
"kotlin.collections.HashMap", "java.util.HashMap",
"kotlin.collections.LinkedHashMap", "java.util.LinkedHashMap",
"java.util.SortedMap", "java.util.NavigableMap", "java.util.TreeMap",
"java.util.concurrent.ConcurrentHashMap" -> {
in supportedByMapSerializer -> {
val keyType = (irType as IrSimpleType).arguments[0].upperBound(irBuiltIns)
val valueType = irType.arguments[1].upperBound(irBuiltIns)
val parceler =
val mapSerializer =
IrMapParcelSerializer(
classifier,
keyType,
@@ -236,7 +278,17 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
get(keyType, scope, parcelizeType, strict()),
get(valueType, scope, parcelizeType, strict())
)
return wrapNullableSerializerIfNeeded(irType, parceler)
val actualSerializer =
if (classifierFqName in BuiltinParcelableTypes.IMMUTABLE_MAP_FQNAMES) {
IrExtensionFunctionOnReadCallingSerializer(
mapSerializer,
symbols.kotlinMapToPersistentMapExtension
)
} else {
mapSerializer
}
return wrapNullableSerializerIfNeeded(irType, actualSerializer)
}
}
@@ -297,9 +349,21 @@ class IrParcelSerializerFactory(private val symbols: AndroidSymbols) {
private val stringArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArray, symbols.parcelWriteStringArray)
private val stringListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateStringArrayList, symbols.parcelWriteStringList)
private val stringPersistentListSerializer by lazy {
IrExtensionFunctionOnReadCallingSerializer(
delegated = stringListSerializer,
converterExtensionFunction = symbols.kotlinIterableToPersistentListExtension,
)
}
private val iBinderSerializer = IrSimpleParcelSerializer(symbols.parcelReadStrongBinder, symbols.parcelWriteStrongBinder)
private val iBinderArraySerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArray, symbols.parcelWriteBinderArray)
private val iBinderListSerializer = IrSimpleParcelSerializer(symbols.parcelCreateBinderArrayList, symbols.parcelWriteBinderList)
private val iBinderPersistentListSerializer by lazy {
IrExtensionFunctionOnReadCallingSerializer(
delegated = iBinderListSerializer,
converterExtensionFunction = symbols.kotlinIterableToPersistentListExtension,
)
}
private val serializableSerializer = IrSimpleParcelSerializer(symbols.parcelReadSerializable, symbols.parcelWriteSerializable)
private val stringSerializer = IrSimpleParcelSerializer(symbols.parcelReadString, symbols.parcelWriteString)
private val byteSerializer = IrSimpleParcelSerializer(symbols.parcelReadByte, symbols.parcelWriteByte)
@@ -35,6 +35,20 @@ fun AndroidIrBuilder.writeParcelWith(
return with(serializer) { writeParcel(parcel, flags, value) }
}
class IrExtensionFunctionOnReadCallingSerializer(
private val delegated: IrParcelSerializer,
private val converterExtensionFunction: IrSimpleFunctionSymbol
) : IrParcelSerializer by delegated {
override fun AndroidIrBuilder.readParcel(parcel: IrValueDeclaration): IrExpression {
val delegatedResult = with(delegated) {
readParcel(parcel)
}
return irCall(converterExtensionFunction).apply {
extensionReceiver = delegatedResult
}
}
}
// Creates a serializer from a pair of parcel methods of the form reader()T and writer(T)V.
class IrSimpleParcelSerializer(private val reader: IrSimpleFunctionSymbol, private val writer: IrSimpleFunctionSymbol) :
IrParcelSerializer {
@@ -412,7 +426,12 @@ class IrListParcelSerializer(
}
}
private fun listSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
data class ListSymbols(
val constructor: IrConstructorSymbol,
val function: IrSimpleFunctionSymbol,
)
private fun listSymbols(symbols: AndroidSymbols): ListSymbols {
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
// the the default constructor if none exist.
if (!irClass.isJvmInterface) {
@@ -424,16 +443,31 @@ class IrListParcelSerializer(
function.name.asString() == "add" && function.valueParameters.size == 1
}
return constructor.symbol to add.symbol
return ListSymbols(
constructor = constructor.symbol,
function = add.symbol
)
}
return when (irClass.fqNameWhenAvailable?.asString()) {
"kotlin.collections.MutableList", "kotlin.collections.List", "java.util.List" ->
symbols.arrayListConstructor to symbols.arrayListAdd
"kotlin.collections.MutableSet", "kotlin.collections.Set", "java.util.Set" ->
symbols.linkedHashSetConstructor to symbols.linkedHashSetAdd
"java.util.NavigableSet", "java.util.SortedSet" ->
symbols.treeSetConstructor to symbols.treeSetAdd
"kotlin.collections.MutableList",
"kotlin.collections.List",
"java.util.List",
in BuiltinParcelableTypes.IMMUTABLE_LIST_FQNAMES -> ListSymbols(
constructor = symbols.arrayListConstructor,
function = symbols.arrayListAdd
)
"kotlin.collections.MutableSet",
"kotlin.collections.Set",
"java.util.Set",
in BuiltinParcelableTypes.IMMUTABLE_SET_FQNAMES -> ListSymbols(
constructor = symbols.linkedHashSetConstructor,
function = symbols.linkedHashSetAdd
)
"java.util.NavigableSet", "java.util.SortedSet" -> ListSymbols(
constructor = symbols.treeSetConstructor,
function = symbols.treeSetAdd
)
else -> error("Unknown list interface type: ${irClass.render()}")
}
}
@@ -506,7 +540,12 @@ class IrMapParcelSerializer(
}
}
private fun mapSymbols(symbols: AndroidSymbols): Pair<IrConstructorSymbol, IrSimpleFunctionSymbol> {
data class MapSymbols(
val constructor: IrConstructorSymbol,
val function: IrSimpleFunctionSymbol,
)
private fun mapSymbols(symbols: AndroidSymbols): MapSymbols {
// If the IrClass refers to a concrete type, try to find a constructor with capacity or fall back
// the the default constructor if none exist.
if (!irClass.isJvmInterface) {
@@ -520,14 +559,25 @@ class IrMapParcelSerializer(
function.name.asString() == "put" && function.valueParameters.size == 2
}
return constructor.symbol to put.symbol
return MapSymbols(
constructor = constructor.symbol,
function = put.symbol,
)
}
return when (irClass.fqNameWhenAvailable?.asString()) {
"kotlin.collections.MutableMap", "kotlin.collections.Map", "java.util.Map" ->
symbols.linkedHashMapConstructor to symbols.linkedHashMapPut
"java.util.SortedMap", "java.util.NavigableMap" ->
symbols.treeMapConstructor to symbols.treeMapPut
"kotlin.collections.MutableMap",
"kotlin.collections.Map",
"java.util.Map",
in BuiltinParcelableTypes.IMMUTABLE_MAP_FQNAMES -> MapSymbols(
constructor = symbols.linkedHashMapConstructor,
function = symbols.linkedHashMapPut,
)
"java.util.SortedMap",
"java.util.NavigableMap" -> MapSymbols(
constructor = symbols.treeMapConstructor,
function = symbols.treeMapPut
)
else -> error("Unknown map interface type: ${irClass.render()}")
}
}
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
class ParcelizeFirIrGeneratorExtension : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, moduleFragment)
val androidSymbols = AndroidSymbols(pluginContext, moduleFragment)
ParcelizeFirIrTransformer(pluginContext, androidSymbols).transform(moduleFragment)
}
}
@@ -11,7 +11,7 @@ import org.jetbrains.kotlin.ir.declarations.IrModuleFragment
class ParcelizeIrGeneratorExtension : IrGenerationExtension {
override fun generate(moduleFragment: IrModuleFragment, pluginContext: IrPluginContext) {
val androidSymbols = AndroidSymbols(pluginContext.irBuiltIns, moduleFragment)
val androidSymbols = AndroidSymbols(pluginContext, moduleFragment)
ParcelizeIrTransformer(pluginContext, androidSymbols).transform(moduleFragment)
}
}
@@ -15,6 +15,27 @@ package org.jetbrains.kotlin.parcelize
* reflection, as well as all objects, enums, and function types (since they are implicitly serializable).
*/
object BuiltinParcelableTypes {
val IMMUTABLE_LIST_FQNAMES = setOf(
kotlinxImmutable("PersistentList"),
kotlinxImmutable("ImmutableList"),
)
val IMMUTABLE_SET_FQNAMES = setOf(
kotlinxImmutable("PersistentSet"),
kotlinxImmutable("ImmutableSet"),
)
val IMMUTABLE_MAP_FQNAMES = setOf(
kotlinxImmutable("PersistentMap"),
kotlinxImmutable("ImmutableMap"),
)
val IMMUTABLE_COLLECTIONS_FQNAMES = setOf(
*IMMUTABLE_LIST_FQNAMES.toTypedArray(),
*IMMUTABLE_SET_FQNAMES.toTypedArray(),
*IMMUTABLE_MAP_FQNAMES.toTypedArray()
)
val PARCELABLE_SUPERTYPE_FQNAMES = setOf(
"android.os.Parcelable",
"android.os.IBinder",
@@ -97,5 +118,6 @@ object BuiltinParcelableTypes {
"kotlin.collections.MutableMap",
"kotlin.collections.MutableSet",
"kotlin.collections.Set",
*IMMUTABLE_COLLECTIONS_FQNAMES.toTypedArray(),
)
}
@@ -0,0 +1,16 @@
/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package org.jetbrains.kotlin.parcelize
/**
* Required because :kotlin-compiler-embeddable performs package relocation
* If there's a "kotlinx.collections.immutable" string literal in bytecode
* it becomes "org.jetbrains.kotlin.kotlinx.collections.immutable" thus
* breaking target project class name matching
*/
fun kotlinxImmutable(name: String? = null): String {
return listOfNotNull("kotlinx", "collections", "immutable", name).joinToString(".")
}
@@ -44,7 +44,7 @@ interface ParcelSerializer {
val frameMap: FrameMap
) {
fun findParcelerClass(type: KotlinType): KotlinType? {
return typeParcelers.firstOrNull { it.first == type }?.second
return typeParcelers.firstOrNull { it.mappedType == type }?.parcelerType
}
}
@@ -10,7 +10,10 @@ import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe
import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.TypeUtils
typealias TypeParcelerMapping = Pair<KotlinType, KotlinType>
data class TypeParcelerMapping(
val mappedType: KotlinType,
val parcelerType: KotlinType,
)
fun KotlinType.isParcelable() = matchesFqNameWithSupertypes(ParcelizeNames.PARCELABLE_FQN.asString())
@@ -8,6 +8,7 @@ import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.*
import kotlinx.collections.immutable.*
@Parcelize
data class Test(
@@ -21,7 +22,11 @@ data class Test(
val h: HashSet<String>,
val i: LinkedHashSet<String>,
val j: NavigableSet<String>,
val k: SortedSet<String>
val k: SortedSet<String>,
val l: PersistentList<String>,
val m: PersistentSet<String>,
val n: ImmutableList<String>,
val o: ImmutableSet<String>,
) : Parcelable
fun box() = parcelTest { parcel ->
@@ -36,7 +41,11 @@ fun box() = parcelTest { parcel ->
h = HashSet<String>().apply { this += "H" },
i = LinkedHashSet<String>().apply { this += "I" },
j = TreeSet<String>().apply { this += "J" },
k = TreeSet<String>().apply { this += "K" }
k = TreeSet<String>().apply { this += "K" },
l = persistentListOf("L"),
m = persistentSetOf("M"),
n = persistentListOf("N"),
o = persistentSetOf("O"),
)
first.writeToParcel(parcel, 0)
@@ -0,0 +1,27 @@
// WITH_STDLIB
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import kotlinx.collections.immutable.*
@Parcelize
data class Test(val a: PersistentList<String>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(persistentListOf("A", "B"))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = parcelableCreator<Test>().createFromParcel(parcel)
assert(first == first2)
}
@@ -8,6 +8,7 @@ import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import java.util.*
import kotlinx.collections.immutable.*
@Parcelize
data class Test(
@@ -17,7 +18,9 @@ data class Test(
val d: LinkedHashMap<String, String>,
val e: TreeMap<String, String>,
val f: SortedMap<String, String>,
val g: NavigableMap<String, String>
val g: NavigableMap<String, String>,
val h: PersistentMap<String, String>,
val i: ImmutableMap<String, String>,
) : Parcelable
fun box() = parcelTest { parcel ->
@@ -28,7 +31,9 @@ fun box() = parcelTest { parcel ->
d = LinkedHashMap<String, String>().apply { put("A", "B") },
e = TreeMap<String, String>().apply { put("A", "B") },
f = TreeMap<String, String>().apply { put("A", "B") },
g = TreeMap<String, String>().apply { put("A", "B") }
g = TreeMap<String, String>().apply { put("A", "B") },
h = persistentMapOf("A" to "B"),
i = persistentMapOf("A" to "B"),
)
first.writeToParcel(parcel, 0)
@@ -0,0 +1,27 @@
// WITH_STDLIB
@file:JvmName("TestKt")
package test
import kotlinx.parcelize.*
import android.os.Parcel
import android.os.Parcelable
import kotlinx.collections.immutable.*
@Parcelize
data class Test(val a: PersistentMap<String, String>) : Parcelable
fun box() = parcelTest { parcel ->
val first = Test(persistentMapOf("A" to "B", "C" to "D"))
first.writeToParcel(parcel, 0)
val bytes = parcel.marshall()
parcel.unmarshall(bytes, 0, bytes.size)
parcel.setDataPosition(0)
val first2 = parcelableCreator<Test>().createFromParcel(parcel)
assert(first == first2)
}
@@ -0,0 +1,48 @@
public final class Test$Creator : java/lang/Object, android/os/Parcelable$Creator {
public void <init>()
public final Test createFromParcel(android.os.Parcel parcel) {
LABEL (L0)
ALOAD (1)
LDC (parcel)
INVOKESTATIC (kotlin/jvm/internal/Intrinsics, checkNotNullParameter, (Ljava/lang/Object;Ljava/lang/String;)V)
NEW (Test)
DUP
ALOAD (1)
INVOKEVIRTUAL (android/os/Parcel, createStringArrayList, ()Ljava/util/ArrayList;)
CHECKCAST (java/lang/Iterable)
INVOKESTATIC (kotlinx/collections/immutable/ExtensionsKt, toPersistentList, (Ljava/lang/Iterable;)Lkotlinx/collections/immutable/PersistentList;)
INVOKESPECIAL (Test, <init>, (Lkotlinx/collections/immutable/PersistentList;)V)
ARETURN
LABEL (L1)
}
public java.lang.Object createFromParcel(android.os.Parcel source) {
LABEL (L0)
ALOAD (0)
ALOAD (1)
INVOKEVIRTUAL (Test$Creator, createFromParcel, (Landroid/os/Parcel;)LTest;)
ARETURN
LABEL (L1)
}
public final Test[] newArray(int size)
public java.lang.Object[] newArray(int size)
}
public final class Test : java/lang/Object, android/os/Parcelable {
public final static android.os.Parcelable$Creator CREATOR
private final kotlinx.collections.immutable.PersistentList names
static void <clinit>()
public void <init>(kotlinx.collections.immutable.PersistentList names)
public int describeContents()
public final kotlinx.collections.immutable.PersistentList getNames()
public void writeToParcel(android.os.Parcel out, int flags)
}
@@ -0,0 +1,9 @@
// CURIOUS_ABOUT: createFromParcel
// WITH_STDLIB
import kotlinx.parcelize.*
import android.os.Parcelable
import kotlinx.collections.immutable.*
@Parcelize
class Test(val names: PersistentList<String>): Parcelable
@@ -283,6 +283,12 @@ public class ParcelizeFirLightTreeBoxTestGenerated extends AbstractParcelizeFirL
runTest("plugins/parcelize/parcelize-compiler/testData/box/listSimple.kt");
}
@Test
@TestMetadata("listSimplePersistent.kt")
public void testListSimplePersistent() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/listSimplePersistent.kt");
}
@Test
@TestMetadata("lists.kt")
public void testLists() throws Exception {
@@ -301,6 +307,12 @@ public class ParcelizeFirLightTreeBoxTestGenerated extends AbstractParcelizeFirL
runTest("plugins/parcelize/parcelize-compiler/testData/box/mapSimple.kt");
}
@Test
@TestMetadata("mapSimplePersistent.kt")
public void testMapSimplePersistent() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/mapSimplePersistent.kt");
}
@Test
@TestMetadata("maps.kt")
public void testMaps() throws Exception {
@@ -283,6 +283,12 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/listSimple.kt");
}
@Test
@TestMetadata("listSimplePersistent.kt")
public void testListSimplePersistent() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/listSimplePersistent.kt");
}
@Test
@TestMetadata("lists.kt")
public void testLists() throws Exception {
@@ -301,6 +307,12 @@ public class ParcelizeIrBoxTestGenerated extends AbstractParcelizeIrBoxTest {
runTest("plugins/parcelize/parcelize-compiler/testData/box/mapSimple.kt");
}
@Test
@TestMetadata("mapSimplePersistent.kt")
public void testMapSimplePersistent() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/box/mapSimplePersistent.kt");
}
@Test
@TestMetadata("maps.kt")
public void testMaps() throws Exception {
@@ -151,6 +151,12 @@ public class ParcelizeIrBytecodeListingTestGenerated extends AbstractParcelizeIr
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/simpleList.kt");
}
@Test
@TestMetadata("simplePersistentList.kt")
public void testSimplePersistentList() throws Exception {
runTest("plugins/parcelize/parcelize-compiler/testData/codegen/simplePersistentList.kt");
}
@Test
@TestMetadata("size.kt")
public void testSize() throws Exception {
@@ -9,6 +9,7 @@ import org.jetbrains.kotlin.cli.jvm.config.addJvmClasspathRoots
import org.jetbrains.kotlin.compiler.plugin.CompilerPluginRegistrar
import org.jetbrains.kotlin.config.CompilerConfiguration
import org.jetbrains.kotlin.parcelize.ParcelizeComponentRegistrar
import org.jetbrains.kotlin.parcelize.kotlinxImmutable
import org.jetbrains.kotlin.test.model.FrontendKinds
import org.jetbrains.kotlin.test.model.TestModule
import org.jetbrains.kotlin.test.services.EnvironmentConfigurator
@@ -17,12 +18,29 @@ import org.jetbrains.kotlin.test.util.KtTestUtil
import org.jetbrains.kotlin.utils.PathUtil
import java.io.File
private fun getLibraryJar(classToDetect: String): File? = try {
PathUtil.getResourcePathForClass(Class.forName(classToDetect))
} catch (e: ClassNotFoundException) {
null
}
class ParcelizeEnvironmentConfigurator(testServices: TestServices) : EnvironmentConfigurator(testServices) {
override fun configureCompilerConfiguration(configuration: CompilerConfiguration, module: TestModule) {
val runtimeLibrary = File(PathUtil.kotlinPathsForCompiler.libPath, PathUtil.PARCELIZE_RUNTIME_PLUGIN_JAR_NAME)
val androidExtensionsRuntimeLibrary = File(PathUtil.kotlinPathsForCompiler.libPath, PathUtil.ANDROID_EXTENSIONS_RUNTIME_PLUGIN_JAR_NAME)
val libPath = PathUtil.kotlinPathsForCompiler.libPath
val runtimeLibrary = File(libPath, PathUtil.PARCELIZE_RUNTIME_PLUGIN_JAR_NAME)
val androidExtensionsRuntimeLibrary = File(libPath, PathUtil.ANDROID_EXTENSIONS_RUNTIME_PLUGIN_JAR_NAME)
val androidApiJar = KtTestUtil.findAndroidApiJar()
configuration.addJvmClasspathRoots(listOf(runtimeLibrary, androidExtensionsRuntimeLibrary, androidApiJar))
val kotlinxCollectionsImmutable = getLibraryJar(kotlinxImmutable("ImmutableList"))
?: error("kotlinx-collections-immutable is not found on classpath")
configuration.addJvmClasspathRoots(
listOf(
runtimeLibrary,
androidExtensionsRuntimeLibrary,
androidApiJar,
kotlinxCollectionsImmutable
)
)
}
override fun CompilerPluginRegistrar.ExtensionStorage.registerCompilerExtensions(