Initial implementation of EnumEntries
KT-53152 Merge-request: KT-MR-6656 Merged-by: Vsevolod Tolstopyatov <vsevolod.tolstopyatov@jetbrains.com>
This commit is contained in:
committed by
Space
parent
3051c36780
commit
59ac756f22
@@ -0,0 +1,4 @@
|
|||||||
|
@kotlin.ExperimentalStdlibApi
|
||||||
|
@kotlin.SinceKotlin(version = "1.8")
|
||||||
|
public sealed interface EnumEntries<E : kotlin.Enum<E>> : kotlin.collections.List<E> {
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
@kotlin.ExperimentalStdlibApi
|
||||||
|
@kotlin.SinceKotlin(version = "1.8")
|
||||||
|
public sealed interface EnumEntries<E : kotlin.Enum<E>> : kotlin.collections.List<E> {
|
||||||
|
}
|
||||||
@@ -47,7 +47,8 @@ val commonMainSources by task<Sync> {
|
|||||||
"libraries/stdlib/src/kotlin/text/**",
|
"libraries/stdlib/src/kotlin/text/**",
|
||||||
"libraries/stdlib/src/kotlin/time/**",
|
"libraries/stdlib/src/kotlin/time/**",
|
||||||
"libraries/stdlib/src/kotlin/util/KotlinVersion.kt",
|
"libraries/stdlib/src/kotlin/util/KotlinVersion.kt",
|
||||||
"libraries/stdlib/src/kotlin/util/Tuples.kt"
|
"libraries/stdlib/src/kotlin/util/Tuples.kt",
|
||||||
|
"libraries/stdlib/src/kotlin/enums/EnumEntries.kt"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
fullCommonMainSources.outputs.files.singleFile
|
fullCommonMainSources.outputs.files.singleFile
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ module kotlin.stdlib {
|
|||||||
exports kotlin.coroutines.cancellation;
|
exports kotlin.coroutines.cancellation;
|
||||||
exports kotlin.coroutines.intrinsics;
|
exports kotlin.coroutines.intrinsics;
|
||||||
exports kotlin.coroutines.jvm.internal;
|
exports kotlin.coroutines.jvm.internal;
|
||||||
|
exports kotlin.enums;
|
||||||
exports kotlin.io;
|
exports kotlin.io;
|
||||||
exports kotlin.jvm;
|
exports kotlin.jvm;
|
||||||
exports kotlin.jvm.functions;
|
exports kotlin.jvm.functions;
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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 kotlin.enums
|
||||||
|
|
||||||
|
import kotlin.jvm.Volatile
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A specialized immutable implementation of [List] interface that
|
||||||
|
* contains all enum entries of the specified enum type [E].
|
||||||
|
* [EnumEntries] contains all enum entries in the order they are declared in the source code,
|
||||||
|
* consistently with the corresponding [Enum.ordinal] values.
|
||||||
|
*
|
||||||
|
* An instance of this interface can only be obtained from `EnumClass.entries` property.
|
||||||
|
*/
|
||||||
|
@ExperimentalStdlibApi
|
||||||
|
@SinceKotlin("1.8")
|
||||||
|
public sealed interface EnumEntries<E : Enum<E>> : List<E>
|
||||||
|
|
||||||
|
@PublishedApi
|
||||||
|
@ExperimentalStdlibApi
|
||||||
|
@SinceKotlin("1.8")
|
||||||
|
internal fun <E : Enum<E>> enumEntries(entriesProvider: () -> Array<E>): EnumEntries<E> = EnumEntriesList(entriesProvider)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* For enum class E, this class is instantiated in the following manner (NB it's pseudocode that does not
|
||||||
|
* reflect code generation strategy precisely):
|
||||||
|
* ```
|
||||||
|
* class E extends Enum<E> {
|
||||||
|
* private static final E[] $VALUES
|
||||||
|
* private static final EnumEntries[] $ENTRIES
|
||||||
|
*
|
||||||
|
* static {
|
||||||
|
* $VALUES = $values();
|
||||||
|
* val supplier = #invokedynamic ..args.. values;
|
||||||
|
* $ENTRIES = new EnumEntriesList(supplier);
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* public static EnumEntries<MyEnum> getEntries() {
|
||||||
|
* return $ENTRIES;
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* private synthetic static E[] $values() {
|
||||||
|
* return new E[] { ... };
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
*
|
||||||
|
* This machinery is required as a workaround for a long-standing issue when people do reflectively change `$VALUES` of
|
||||||
|
* enums in order to workaround project-specific issues.
|
||||||
|
* We allow racy initialization (e.g. entriesProvider can be invoked multiple times), but the resulting array is safely
|
||||||
|
* published, preventing any read races after the initialization.
|
||||||
|
*/
|
||||||
|
@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
|
||||||
|
*/
|
||||||
|
|
||||||
|
@Volatile // Volatile is required for safe publication of the array. It doesn't incur any real-world penalties
|
||||||
|
private var _entries: Array<E>? = null
|
||||||
|
private val entries: Array<E>
|
||||||
|
get() {
|
||||||
|
var e = _entries
|
||||||
|
if (e != null) return e
|
||||||
|
e = entriesProvider()
|
||||||
|
_entries = e
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
override val size: Int
|
||||||
|
get() = entries.size
|
||||||
|
|
||||||
|
override fun get(index: Int): E {
|
||||||
|
val entries = entries
|
||||||
|
checkElementIndex(index, entries.size)
|
||||||
|
return entries[index]
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010-2022 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.
|
||||||
|
*/
|
||||||
|
@file:Suppress("INVISIBLE_MEMBER", "INVISIBLE_REFERENCE")
|
||||||
|
package test.enums
|
||||||
|
|
||||||
|
import kotlin.enums.enumEntries
|
||||||
|
import kotlin.test.*
|
||||||
|
import test.collections.behaviors.listBehavior
|
||||||
|
import test.collections.compare
|
||||||
|
|
||||||
|
class EnumEntriesListTest {
|
||||||
|
|
||||||
|
enum class EmptyEnum
|
||||||
|
|
||||||
|
enum class NonEmptyEnum {
|
||||||
|
A, B, C
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testForEmptyEnum() {
|
||||||
|
val list = enumEntries(EmptyEnum::values)
|
||||||
|
assertTrue(list.isEmpty())
|
||||||
|
assertEquals(0, list.size)
|
||||||
|
assertFalse { list is MutableList<*> }
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { list[0] }
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { list[-1] }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testEmptyEnumBehaviour() {
|
||||||
|
val list = enumEntries(EmptyEnum::values)
|
||||||
|
compare(EmptyEnum.values().toList(), list) { listBehavior() }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testForEnum() {
|
||||||
|
val list = enumEntries(NonEmptyEnum::values)
|
||||||
|
val goldenCopy = NonEmptyEnum.values().toList()
|
||||||
|
assertEquals(goldenCopy, list)
|
||||||
|
assertFalse { list is MutableList<*> }
|
||||||
|
for ((idx, e) in goldenCopy.withIndex()) {
|
||||||
|
assertEquals(e, list[e.ordinal])
|
||||||
|
assertEquals(idx, list.indexOf(e))
|
||||||
|
assertEquals(e, list[idx])
|
||||||
|
}
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { list[-1] }
|
||||||
|
assertFailsWith<IndexOutOfBoundsException> { list[goldenCopy.size] }
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testyEnumBehaviour() {
|
||||||
|
val list = enumEntries(NonEmptyEnum::values)
|
||||||
|
compare(NonEmptyEnum.values().toList(), list) { listBehavior() }
|
||||||
|
}
|
||||||
|
}
|
||||||
+7
@@ -3115,6 +3115,13 @@ public abstract interface class kotlin/coroutines/jvm/internal/CoroutineStackFra
|
|||||||
public abstract fun getStackTraceElement ()Ljava/lang/StackTraceElement;
|
public abstract fun getStackTraceElement ()Ljava/lang/StackTraceElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public abstract interface class kotlin/enums/EnumEntries : java/util/List, kotlin/jvm/internal/markers/KMappedMarker {
|
||||||
|
}
|
||||||
|
|
||||||
|
public final class kotlin/enums/EnumEntriesKt {
|
||||||
|
public static final fun enumEntries (Lkotlin/jvm/functions/Function0;)Lkotlin/enums/EnumEntries;
|
||||||
|
}
|
||||||
|
|
||||||
public abstract interface annotation class kotlin/experimental/ExperimentalTypeInference : java/lang/annotation/Annotation {
|
public abstract interface annotation class kotlin/experimental/ExperimentalTypeInference : java/lang/annotation/Annotation {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user