[stdlib] Improve EnumEntries

* Provide short-circuit methods for contract "EnumEntries contains all
  enum entries"
* Make EnumEntries serializable
* Introduce more convenient overload for other backends

#KT-53152


Merge-request: KT-MR-6867
Merged-by: Vsevolod Tolstopyatov <qwwdfsad@gmail.com>
This commit is contained in:
Vsevolod Tolstopyatov
2022-08-22 16:50:28 +00:00
committed by Space
parent cc00dcc038
commit 5054e301be
10 changed files with 209 additions and 21 deletions
+4
View File
@@ -51,6 +51,10 @@ Low-level building blocks for libraries that provide coroutine-based APIs.
Utility functions for working with the browser DOM.
# Package kotlin.enum
Utilities for working with Kotlin enum classes.
# Package kotlin.experimental
Experimental APIs, subject to change in future versions of Kotlin.
@@ -21,9 +21,14 @@ public sealed interface EnumEntries<E : Enum<E>> : List<E>
@PublishedApi
@ExperimentalStdlibApi
@SinceKotlin("1.8")
@SinceKotlin("1.8") // Used by JVM compiler
internal fun <E : Enum<E>> enumEntries(entriesProvider: () -> Array<E>): EnumEntries<E> = EnumEntriesList(entriesProvider)
@PublishedApi
@ExperimentalStdlibApi
@SinceKotlin("1.8") // Used by Native/JS compilers and Java serialization
internal fun <E : Enum<E>> enumEntries(entries: Array<E>): EnumEntries<E> = EnumEntriesList { entries }
/*
* For enum class E, this class is instantiated in the following manner (NB it's pseudocode that does not
* reflect code generation strategy precisely):
@@ -55,24 +60,7 @@ internal fun <E : Enum<E>> enumEntries(entriesProvider: () -> Array<E>): EnumEnt
*/
@SinceKotlin("1.8")
@ExperimentalStdlibApi
private class EnumEntriesList<E : Enum<E>>(private val entriesProvider: () -> Array<E>) : EnumEntries<E>, AbstractList<E>() {
/*
* Open questions to implementation:
*
* - Are we allowed to use e.ordinal as an index?
* - e.g. indexOf(e) = e.ordinal
*
* - Are we allowed to short-circuit methods?
* - e.g. `EEL.contains(anyE)` is always true as long as no reflection is involved
*
* - Should it be Java-serializable? (then we definitely can suffer from short-circuiting and should be extra-careful around read-resolve)
*
* - Should it be sealed or just a class with internal constructor? TODO discuss on design to align this policy over all the language
* - Probably should to avoid exposing AbstractList superclass directly?
*
* - TODO package-info for kotlinlang
*/
private class EnumEntriesList<E : Enum<E>>(private val entriesProvider: () -> Array<E>) : EnumEntries<E>, AbstractList<E>(), Serializable {
@Volatile // Volatile is required for safe publication of the array. It doesn't incur any real-world penalties
private var _entries: Array<E>? = null
@@ -93,4 +81,33 @@ private class EnumEntriesList<E : Enum<E>>(private val entriesProvider: () -> Ar
checkElementIndex(index, entries.size)
return entries[index]
}
// By definition, EnumEntries contains **all** enums in declaration order,
// thus we are able to short-circuit the implementation here
override fun contains(element: E): Boolean {
@Suppress("SENSELESS_COMPARISON")
if (element === null) return false // WA for JS IR bug
// Check identity due to UnsafeVariance
val target = entries.getOrNull(element.ordinal)
return target === element
}
override fun indexOf(element: E): Int {
@Suppress("SENSELESS_COMPARISON")
if (element === null) return -1 // WA for JS IR bug
// Check identity due to UnsafeVariance
val ordinal = element.ordinal
val target = entries.getOrNull(ordinal)
return if (target === element) ordinal else -1
}
override fun lastIndexOf(element: E): Int = indexOf(element)
@Suppress("unused") // Used for Java serialization
private fun writeReplace(): Any {
return EnumEntriesSerializationProxy(entries)
}
}
internal expect class EnumEntriesSerializationProxy<E : Enum<E>>(entries: Array<E>)