Support kotlinx.collections.immutable in kotlin-parcelize plugin
#KT-57685 Fixed Co-authored-by: Ilya Gulya <ilyagulya@gmail.com>
This commit is contained in:
@@ -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 }
|
||||
|
||||
+35
-2
@@ -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>")
|
||||
|
||||
+91
-27
@@ -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)
|
||||
|
||||
+64
-14
@@ -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()}")
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
+22
@@ -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(),
|
||||
)
|
||||
}
|
||||
|
||||
+16
@@ -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(".")
|
||||
}
|
||||
+1
-1
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+4
-1
@@ -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)
|
||||
}
|
||||
+48
@@ -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
|
||||
+12
@@ -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 {
|
||||
|
||||
+12
@@ -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 {
|
||||
|
||||
+6
@@ -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 {
|
||||
|
||||
+21
-3
@@ -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(
|
||||
|
||||
Reference in New Issue
Block a user