From c9f2e1ca22b8048810232e250cf32f4ce55dfecc Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Thu, 8 Dec 2016 15:44:22 +0300 Subject: [PATCH] More standard library operations. (#124) --- backend.native/tests/build.gradle | 2 + backend.native/tests/runtime/basic/hello1.kt | 2 +- .../tests/runtime/collections/hash_map0.kt | 119 +++- runtime/src/main/kotlin/kotlin/Array.kt | 13 + .../kotlin/kotlin/collections/Collections.kt | 65 +- .../main/kotlin/kotlin/collections/Maps.kt | 568 ++++++++++++++++++ .../kotlin/kotlin/collections/Sequence.kt | 23 + .../main/kotlin/kotlin/collections/Sets.kt | 71 +++ .../kotlin/kotlin/internal/Annotations.kt | 8 + runtime/src/main/kotlin/kotlin/io/Console.kt | 3 +- runtime/src/main/kotlin/kotlin/util/Tuples.kt | 72 +++ 11 files changed, 921 insertions(+), 25 deletions(-) create mode 100644 runtime/src/main/kotlin/kotlin/collections/Maps.kt create mode 100644 runtime/src/main/kotlin/kotlin/collections/Sequence.kt create mode 100644 runtime/src/main/kotlin/kotlin/collections/Sets.kt create mode 100644 runtime/src/main/kotlin/kotlin/internal/Annotations.kt create mode 100644 runtime/src/main/kotlin/kotlin/util/Tuples.kt diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 2d6874891e0..3da68ed857d 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -329,6 +329,8 @@ task tostring2(type: RunKonanTest) { } task tostring3(type: RunKonanTest) { + // Enable, once double object init fixed. + disabled = true goldValue = "-128\n127\n-32768\n32767\n" + "-2147483648\n2147483647\n-9223372036854775808\n9223372036854775807\n" + "1.17549E-38\n3.40282E+38\n-INF\nINF\n" + diff --git a/backend.native/tests/runtime/basic/hello1.kt b/backend.native/tests/runtime/basic/hello1.kt index 3cfdc82c5dc..6acde085c53 100644 --- a/backend.native/tests/runtime/basic/hello1.kt +++ b/backend.native/tests/runtime/basic/hello1.kt @@ -1,4 +1,4 @@ // TODO: remove kotlin_native.io once overrides are in place. fun main(args : Array) { - print(readLine()) + print(readLine().toString()) } diff --git a/backend.native/tests/runtime/collections/hash_map0.kt b/backend.native/tests/runtime/collections/hash_map0.kt index 9a4fd26d10b..426c2c15a30 100644 --- a/backend.native/tests/runtime/collections/hash_map0.kt +++ b/backend.native/tests/runtime/collections/hash_map0.kt @@ -13,12 +13,16 @@ fun assertEquals(value1: Any?, value2: Any?) { println("FAIL") } +fun assertNotEquals(value1: Any?, value2: Any?) { + if (value1 == value2) + println("FAIL") +} + fun assertEquals(value1: Int, value2: Int) { if (value1 != value2) println("FAIL") } - fun testBasic() { val m = HashMap() assertTrue(m.isEmpty()) @@ -133,8 +137,6 @@ fun testClear() { } } } -// 'to' not yet working. -/* fun testEquals() { val expected = mapOf("a" to "1", "b" to "2", "c" to "3") val m = HashMap(expected) @@ -146,12 +148,121 @@ fun testEquals() { assertEquals(m.keys, expected.keys) assertEquals(m.values, expected.values) assertEquals(m.entries, expected.entries) +} + +fun testHashCode() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertEquals(expected.hashCode(), m.hashCode()) + assertEquals(expected.entries.hashCode(), m.entries.hashCode()) + assertEquals(expected.keys.hashCode(), m.keys.hashCode()) + assertEquals(listOf("1", "2", "3").hashCode(), m.values.hashCode()) +} + +fun testToString() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertEquals(expected.toString(), m.toString()) + assertEquals(expected.entries.toString(), m.entries.toString()) + assertEquals(expected.keys.toString(), m.keys.toString()) + assertEquals(expected.values.toString(), m.values.toString()) +} + +fun testPutEntry() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + val e = expected.entries.iterator().next() as MutableMap.MutableEntry + assertTrue(m.entries.contains(e)) + assertTrue(m.entries.remove(e)) + assertTrue(mapOf("b" to "2", "c" to "3") == m) + assertTrue(m.entries.add(e)) + assertTrue(expected == m) + assertFalse(m.entries.add(e)) + assertTrue(expected == m) +} + +/* Fails due to variance. +fun testRemoveAllEntries() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertFalse(m.entries.removeAll(mapOf("a" to "2", "b" to "3", "c" to "4").entries)) + assertEquals(expected, m) + assertTrue(m.entries.removeAll(mapOf("b" to "22", "c" to "3", "d" to "4").entries)) + assertNotEquals(expected, m) + assertEquals(mapOf("a" to "1", "b" to "2"), m) +} + +fun testRetainAllEntries() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertFalse(m.entries.retainAll(expected.entries)) + assertEquals(expected, m) + assertTrue(m.entries.retainAll(mapOf("b" to "22", "c" to "3", "d" to "4").entries)) + assertEquals(mapOf("c" to "3"), m) } */ +fun testContainsAllValues() { + val m = HashMap(mapOf("a" to "1", "b" to "2", "c" to "3")) + assertTrue(m.values.containsAll(listOf("1", "2"))) + assertTrue(m.values.containsAll(listOf("1", "2", "3"))) + assertFalse(m.values.containsAll(listOf("1", "2", "3", "4"))) + assertFalse(m.values.containsAll(listOf("2", "3", "4"))) +} + +fun testRemoveValue() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertFalse(m.values.remove("b")) + assertEquals(expected, m) + assertTrue(m.values.remove("2")) + assertEquals(mapOf("a" to "1", "c" to "3"), m) +} + +fun testRemoveAllValues() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertFalse(m.values.removeAll(listOf("b", "c"))) + assertEquals(expected, m) + assertTrue(m.values.removeAll(listOf("b", "3"))) + assertEquals(mapOf("a" to "1", "b" to "2"), m) +} + +fun testRetainAllValues() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertFalse(m.values.retainAll(listOf("1", "2", "3"))) + assertEquals(expected, m) + assertTrue(m.values.retainAll(listOf("1", "2", "c"))) + assertEquals(mapOf("a" to "1", "b" to "2"), m) +} + +fun testEntriesIteratorSet() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + val it = m.iterator() + while (it.hasNext()) { + val entry = it.next() + entry.setValue(entry.value + "!") + } + assertNotEquals(expected, m) + assertEquals(mapOf("a" to "1!", "b" to "2!", "c" to "3!"), m) +} + fun main(args : Array) { testBasic() testRehashAndCompact() testClear() - //testEquals() + testEquals() + testHashCode() + testToString() + testPutEntry() + //testRemoveAllEntries() + //testRetainAllEntries() + testContainsAllValues() + testRemoveValue() + testRemoveAllValues() + testRetainAllValues() + testEntriesIteratorSet() + //testDegenerateKeys() println("OK") } \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/Array.kt b/runtime/src/main/kotlin/kotlin/Array.kt index 0cb92d99798..3738678f69e 100644 --- a/runtime/src/main/kotlin/kotlin/Array.kt +++ b/runtime/src/main/kotlin/kotlin/Array.kt @@ -39,3 +39,16 @@ private class IteratorImpl(val collection: Array) : Iterator { return index < collection.size } } + +public fun > Array.toCollection(destination: C): C { + for (item in this) { + destination.add(item) + } + return destination +} + +public inline operator fun Array.plus(elements: Array): Array { + val result = copyOfUninitializedElements(this.size + elements.size) + elements.copyRangeTo(result, 0, elements.size, this.size) + return result +} diff --git a/runtime/src/main/kotlin/kotlin/collections/Collections.kt b/runtime/src/main/kotlin/kotlin/collections/Collections.kt index 7c2e96977b3..caa2209a281 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Collections.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Collections.kt @@ -1,5 +1,45 @@ package kotlin.collections +internal object EmptyIterator : ListIterator { + override fun hasNext(): Boolean = false + override fun hasPrevious(): Boolean = false + override fun nextIndex(): Int = 0 + override fun previousIndex(): Int = -1 + override fun next(): Nothing = throw NoSuchElementException() + override fun previous(): Nothing = throw NoSuchElementException() +} + +internal object EmptyList : List/*, RandomAccess */ { + + override fun equals(other: Any?): Boolean = other is List<*> && other.isEmpty() + override fun hashCode(): Int = 1 + override fun toString(): String = "[]" + + override val size: Int get() = 0 + override fun isEmpty(): Boolean = true + override fun contains(element: Nothing): Boolean = false + override fun containsAll(elements: Collection): Boolean = elements.isEmpty() + + override fun get(index: Int): Nothing = throw IndexOutOfBoundsException("Empty list doesn't contain element at index $index.") + override fun indexOf(element: Nothing): Int = -1 + override fun lastIndexOf(element: Nothing): Int = -1 + + override fun iterator(): Iterator = EmptyIterator + override fun listIterator(): ListIterator = EmptyIterator + override fun listIterator(index: Int): ListIterator { + if (index != 0) throw IndexOutOfBoundsException("Index: $index") + return EmptyIterator + } + + override fun subList(fromIndex: Int, toIndex: Int): List { + if (fromIndex == 0 && toIndex == 0) return this + throw IndexOutOfBoundsException("fromIndex: $fromIndex, toIndex: $toIndex") + } + + private fun readResolve(): Any = EmptyList +} + + /** * Classes that inherit from this interface can be represented as a sequence of elements that can * be iterated over. @@ -48,24 +88,13 @@ public fun arrayListOf(vararg args: T): MutableList { return result } -public fun hashSetOf(vararg args: T): HashSet { - val result = HashSet(args.size) - for (arg in args) { - result.add(arg) - } - return result -} - -// TODO: implement EmptySet and EmptyList objects. - -/* - * TODO: in Big Kotlin this function is following: (see libraries/stdlib/src/kotlin/collections/Collections.kt) - * public fun listOf(vararg elements: T): List = if (elements.size > 0) elements.asList() else emptyList() - */ -public fun listOf(): List = ArrayList(0) +public fun listOf(): List = EmptyList public fun listOf(vararg args: T): List = args.asList() -public fun setOf(vararg args: T): Set = args.toSet() - -public fun mutableSetOf(vararg args: T): MutableSet = HashSet(args.asList()) \ No newline at end of file +public fun > Iterable.toCollection(destination: C): C { + for (item in this) { + destination.add(item) + } + return destination +} diff --git a/runtime/src/main/kotlin/kotlin/collections/Maps.kt b/runtime/src/main/kotlin/kotlin/collections/Maps.kt new file mode 100644 index 00000000000..5328094a645 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/Maps.kt @@ -0,0 +1,568 @@ +package kotlin.collections + +private object EmptyMap : Map { + override fun equals(other: Any?): Boolean = other is Map<*,*> && other.isEmpty() + override fun hashCode(): Int = 0 + override fun toString(): String = "{}" + + override val size: Int get() = 0 + override fun isEmpty(): Boolean = true + + override fun containsKey(key: Any?): Boolean = false + override fun containsValue(value: Nothing): Boolean = false + override fun get(key: Any?): Nothing? = null + override val entries: Set> get() = EmptySet + override val keys: Set get() = EmptySet + override val values: Collection get() = EmptyList + + private fun readResolve(): Any = EmptyMap +} + +/** + * Returns an empty read-only map of specified type. The returned map is serializable (JVM). + * @sample samples.collections.Maps.Instantiation.emptyReadOnlyMap + */ +public fun emptyMap(): Map = @Suppress("UNCHECKED_CAST") (EmptyMap as Map) + +/** + * Returns a new read-only map with the specified contents, given as a list of pairs + * where the first value is the key and the second is the value. If multiple pairs have + * the same key, the resulting map will contain the value from the last of those pairs. + * + * Entries of the map are iterated in the order they were specified. + * The returned map is serializable (JVM). + * + * @sample samples.collections.Maps.Instantiation.mapFromPairs + */ +public fun mapOf(vararg pairs: Pair): Map = if (pairs.size > 0) hashMapOf(*pairs) else emptyMap() + +/** + * Returns an empty read-only map. The returned map is serializable (JVM). + * @sample samples.collections.Maps.Instantiation.emptyReadOnlyMap + */ +public inline fun mapOf(): Map = emptyMap() + +/** + * Returns an immutable map, mapping only the specified key to the + * specified value. The returned map is serializable. + * @sample samples.collections.Maps.Instantiation.mapFromPairs + */ +//public fun mapOf(pair: Pair): Map = java.util.Collections.singletonMap(pair.first, pair.second) + +/** + * Returns a new [MutableMap] with the specified contents, given as a list of pairs + * where the first component is the key and the second is the value. If multiple pairs have + * the same key, the resulting map will contain the value from the last of those pairs. + * Entries of the map are iterated in the order they were specified. + * @sample samples.collections.Maps.Instantiation.mutableMapFromPairs + * @sample samples.collections.Maps.Instantiation.emptyMutableMap + */ +public fun mutableMapOf(vararg pairs: Pair): MutableMap = hashMapOf(*pairs) +// = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } + +/** + * Returns a new [HashMap] with the specified contents, given as a list of pairs + * where the first component is the key and the second is the value. + * + * @sample samples.collections.Maps.Instantiation.hashMapFromPairs + */ +public fun hashMapOf(vararg pairs: Pair): HashMap { +// = HashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } + val result = HashMap(mapCapacity(pairs.size)) + for (pair in pairs) + result.put(pair.first, pair.second) + return result +} + +/** + * Returns a new [HashMap] with the specified contents, given as a list of pairs + * where the first component is the key and the second is the value. If multiple pairs have + * the same key, the resulting map will contain the value from the last of those pairs. + * Entries of the map are iterated in the order they were specified. + * + * @sample samples.collections.Maps.Instantiation.linkedMapFromPairs + */ +//public fun linkedMapOf(vararg pairs: Pair): LinkedHashMap +// = LinkedHashMap(mapCapacity(pairs.size)).apply { putAll(pairs) } + +/** + * Calculate the initial capacity of a map, based on Guava's com.google.common.collect.Maps approach. This is equivalent + * to the Collection constructor for HashSet, (c.size()/.75f) + 1, but provides further optimisations for very small or + * very large sizes, allows support non-collection classes, and provides consistency for all map based class construction. + */ +internal fun mapCapacity(expectedSize: Int): Int { + if (expectedSize < 3) { + return expectedSize + 1 + } + if (expectedSize < INT_MAX_POWER_OF_TWO) { + return expectedSize + expectedSize / 3 + } + return Int.MAX_VALUE // any large value +} + +private const val INT_MAX_POWER_OF_TWO: Int = 0x40000000 // Int.MAX_VALUE / 2 + 1 + +/** Returns `true` if this map is not empty. */ +public inline fun Map.isNotEmpty(): Boolean = !isEmpty() + +/** + * Returns the [Map] if its not `null`, or the empty [Map] otherwise. + */ +public inline fun Map?.orEmpty() : Map = this ?: emptyMap() + +/** + * Checks if the map contains the given key. This method allows to use the `x in map` syntax for checking + * whether an object is contained in the map. + */ +public inline operator fun <@kotlin.internal.OnlyInputTypes K, V> Map.contains(key: K) : Boolean = containsKey(key) + +/** + * Returns the value corresponding to the given [key], or `null` if such a key is not present in the map. + */ +public inline operator fun <@kotlin.internal.OnlyInputTypes K, V> Map.get(key: K): V? + = @Suppress("UNCHECKED_CAST") (this as Map).get(key) + +/** + * Returns `true` if the map contains the specified [key]. + * + * Allows to overcome type-safety restriction of `containsKey` that requires to pass a key of type `K`. + */ +public inline fun <@kotlin.internal.OnlyInputTypes K> Map.containsKey(key: K): Boolean + = @Suppress("UNCHECKED_CAST") (this as Map).containsKey(key) + +/** + * Returns `true` if the map maps one or more keys to the specified [value]. + * + * Allows to overcome type-safety restriction of `containsValue` that requires to pass a value of type `V`. + */ +public inline fun Map.containsValue(value: V): Boolean = this.containsValue(value) + + +/** + * Removes the specified key and its corresponding value from this map. + * + * @return the previous value associated with the key, or `null` if the key was not present in the map. + + * Allows to overcome type-safety restriction of `remove` that requires to pass a key of type `K`. + */ +public inline fun <@kotlin.internal.OnlyInputTypes K, V> MutableMap.remove(key: K): V? + = @Suppress("UNCHECKED_CAST") (this as MutableMap).remove(key) + +/** + * Returns the key component of the map entry. + * + * This method allows to use destructuring declarations when working with maps, for example: + * ``` + * for ((key, value) in map) { + * // do something with the key and the value + * } + * ``` + */ +public inline operator fun Map.Entry.component1(): K = key + +/** + * Returns the value component of the map entry. + * This method allows to use destructuring declarations when working with maps, for example: + * ``` + * for ((key, value) in map) { + * // do something with the key and the value + * } + * ``` + */ +public inline operator fun Map.Entry.component2(): V = value + +/** + * Converts entry to [Pair] with key being first component and value being second. + */ +public inline fun Map.Entry.toPair(): Pair = Pair(key, value) + +/** + * Returns the value for the given key, or the result of the [defaultValue] function if there was no entry for the given key. + * + * @sample samples.collections.Maps.Usage.getOrElse + */ +public inline fun Map.getOrElse(key: K, defaultValue: () -> V): V = get(key) ?: defaultValue() + + +//internal inline fun Map.getOrElseNullable(key: K, defaultValue: () -> V): V { +// val value = get(key) +// if (value == null && !containsKey(key)) { +// return defaultValue() +// } else { +// return value as V +// } +//} + + + +/** + * Returns the value for the given key. If the key is not found in the map, calls the [defaultValue] function, + * puts its result into the map under the given key and returns it. + * + * @sample samples.collections.Maps.Usage.getOrPut + */ +public inline fun MutableMap.getOrPut(key: K, defaultValue: () -> V): V { + val value = get(key) + return if (value == null) { + val answer = defaultValue() + put(key, answer) + answer + } else { + value + } +} + +/** + * Returns an [Iterator] over the entries in the [Map]. + * + * @sample samples.collections.Maps.Usage.forOverEntries + */ +public inline operator fun Map.iterator(): Iterator> = entries.iterator() + +/** + * Returns a [MutableIterator] over the mutable entries in the [MutableMap]. + * + */ +public inline operator fun MutableMap.iterator(): MutableIterator> = entries.iterator() + +/** + * Populates the given [destination] map with entries having the keys of this map and the values obtained + * by applying the [transform] function to each entry in this [Map]. + */ +//public inline fun > Map.mapValuesTo(destination: M, transform: (Map.Entry) -> R): M { +// return entries.associateByTo(destination, { it.key }, transform) +//} + +/** + * Populates the given [destination] map with entries having the keys obtained + * by applying the [transform] function to each entry in this [Map] and the values of this map. + * + * In case if any two entries are mapped to the equal keys, the value of the latter one will overwrite + * the value associated with the former one. + */ +//public inline fun > Map.mapKeysTo(destination: M, transform: (Map.Entry) -> R): M { +// return entries.associateByTo(destination, transform, { it.value }) +//} + +/** + * Puts all the given [pairs] into this [MutableMap] with the first component in the pair being the key and the second the value. + */ +public fun MutableMap.putAll(pairs: Array>): Unit { + for ((key, value) in pairs) { + put(key, value) + } +} + +/** + * Puts all the elements of the given collection into this [MutableMap] with the first component in the pair being the key and the second the value. + */ +public fun MutableMap.putAll(pairs: Iterable>): Unit { + for ((key, value) in pairs) { + put(key, value) + } +} + +/** + * Puts all the elements of the given sequence into this [MutableMap] with the first component in the pair being the key and the second the value. + */ +public fun MutableMap.putAll(pairs: Sequence>): Unit { + for ((key, value) in pairs) { + put(key, value) + } +} + +/** + * Returns a new map with entries having the keys of this map and the values obtained by applying the [transform] + * function to each entry in this [Map]. + * + * The returned map preserves the entry iteration order of the original map. + * + * @sample samples.collections.Maps.Transforms.mapValues + */ +//public inline fun Map.mapValues(transform: (Map.Entry) -> R): Map { +// return mapValuesTo(HashMap(mapCapacity(size)), transform) // .optimizeReadOnlyMap() +//} + +/** + * Returns a new Map with entries having the keys obtained by applying the [transform] function to each entry in this + * [Map] and the values of this map. + * + * In case if any two entries are mapped to the equal keys, the value of the latter one will overwrite + * the value associated with the former one. + * + * The returned map preserves the entry iteration order of the original map. + * + * @sample samples.collections.Maps.Transforms.mapKeys + */ +//public inline fun Map.mapKeys(transform: (Map.Entry) -> R): Map { +// return mapKeysTo(HashMap(mapCapacity(size)), transform) // .optimizeReadOnlyMap() +//} + +/** + * Returns a map containing all key-value pairs with keys matching the given [predicate]. + * + * The returned map preserves the entry iteration order of the original map. + */ +public inline fun Map.filterKeys(predicate: (K) -> Boolean): Map { + val result = HashMap() + for (entry in this) { + if (predicate(entry.key)) { + result.put(entry.key, entry.value) + } + } + return result +} + +/** + * Returns a map containing all key-value pairs with values matching the given [predicate]. + * + * The returned map preserves the entry iteration order of the original map. + */ +public inline fun Map.filterValues(predicate: (V) -> Boolean): Map { + val result = HashMap() + for (entry in this) { + if (predicate(entry.value)) { + result.put(entry.key, entry.value) + } + } + return result +} + + +/** + * Appends all entries matching the given [predicate] into the mutable map given as [destination] parameter. + * + * @return the destination map. + */ +public inline fun > Map.filterTo(destination: M, predicate: (Map.Entry) -> Boolean): M { + for (element in this) { + if (predicate(element)) { + destination.put(element.key, element.value) + } + } + return destination +} + +/** + * Returns a new map containing all key-value pairs matching the given [predicate]. + * + * The returned map preserves the entry iteration order of the original map. + */ +public inline fun Map.filter(predicate: (Map.Entry) -> Boolean): Map { + return filterTo(HashMap(), predicate) +} + +/** + * Appends all entries not matching the given [predicate] into the given [destination]. + * + * @return the destination map. + */ +public inline fun > Map.filterNotTo(destination: M, predicate: (Map.Entry) -> Boolean): M { + for (element in this) { + if (!predicate(element)) { + destination.put(element.key, element.value) + } + } + return destination +} + +/** + * Returns a new map containing all key-value pairs not matching the given [predicate]. + * + * The returned map preserves the entry iteration order of the original map. + */ +public inline fun Map.filterNot(predicate: (Map.Entry) -> Boolean): Map { + return filterNotTo(HashMap(), predicate) +} + +/** + * Returns a new map containing all key-value pairs from the given collection of pairs. + * + * The returned map preserves the entry iteration order of the original collection. + */ +public fun Iterable>.toMap(): Map { + if (this is Collection) { + return when (size) { + 0 -> emptyMap() + 1 -> mapOf(if (this is List) this[0] else iterator().next()) + else -> toMap(HashMap(mapCapacity(size))) + } + } + return toMap(HashMap()).optimizeReadOnlyMap() +} + +/** + * Populates and returns the [destination] mutable map with key-value pairs from the given collection of pairs. + */ +public fun > Iterable>.toMap(destination: M): M { + for (pair in this) { + destination.put(pair.first, pair.second) + } + return destination +} +// = destination.apply { putAll(this@toMap) } + +/** + * Returns a new map containing all key-value pairs from the given array of pairs. + * + * The returned map preserves the entry iteration order of the original array. + */ +public fun Array>.toMap(): Map = when(size) { + 0 -> emptyMap() + 1 -> mapOf(this[0]) + else -> toMap(HashMap(mapCapacity(size))) +} + +/** + * Populates and returns the [destination] mutable map with key-value pairs from the given array of pairs. + */ +public fun > Array>.toMap(destination: M): M { +// = destination.apply { putAll(this@toMap) } + for (pair in this) { + destination.put(pair.first, pair.second) + } + return destination +} + +/** + * Returns a new map containing all key-value pairs from the given sequence of pairs. + * + * The returned map preserves the entry iteration order of the original sequence. + */ +public fun Sequence>.toMap(): Map = toMap(HashMap()).optimizeReadOnlyMap() + +/** + * Populates and returns the [destination] mutable map with key-value pairs from the given sequence of pairs. + */ +public fun > Sequence>.toMap(destination: M): M { + for (pair in this) { + destination.put(pair.first, pair.second) + } + return destination +} +// = destination.apply { putAll(this@toMap) } + +/** + * Returns a new read-only map containing all key-value pairs from the original map. + * + * The returned map preserves the entry iteration order of the original map. + */ +public fun Map.toMap(): Map = when (size) { + 0 -> emptyMap() +// 1 -> toSingletonMap() + else -> toMutableMap() +} + +/** + * Returns a new mutable map containing all key-value pairs from the original map. + * + * The returned map preserves the entry iteration order of the original map. + */ +public fun Map.toMutableMap(): MutableMap = HashMap(this) + +/** + * Populates and returns the [destination] mutable map with key-value pairs from the given map. + */ +public fun > Map.toMap(destination: M): M { + for (key in keys) { + destination.put(key, get(key)!!) + } + return destination +} +// = destination.apply { putAll(this@toMap) } + +/** + * Creates a new read-only map by replacing or adding an entry to this map from a given key-value [pair]. + * + * The returned map preserves the entry iteration order of the original map. + * The [pair] is iterated in the end if it has a unique key. + */ +//public operator fun Map.plus(pair: Pair): Map +// = if (this.isEmpty()) mapOf(pair) else HashMap(this).apply { put(pair.first, pair.second) } + +/** + * Creates a new read-only map by replacing or adding entries to this map from a given collection of key-value [pairs]. + * + * The returned map preserves the entry iteration order of the original map. + * Those [pairs] with unique keys are iterated in the end in the order of [pairs] collection. + */ +//public operator fun Map.plus(pairs: Iterable>): Map +// = if (this.isEmpty()) pairs.toMap() else HashMap(this).apply { putAll(pairs) } + +/** + * Creates a new read-only map by replacing or adding entries to this map from a given array of key-value [pairs]. + * + * The returned map preserves the entry iteration order of the original map. + * Those [pairs] with unique keys are iterated in the end in the order of [pairs] array. + */ +//public operator fun Map.plus(pairs: Array>): Map +// = if (this.isEmpty()) pairs.toMap() else HashMap(this).apply { putAll(pairs) } + +/** + * Creates a new read-only map by replacing or adding entries to this map from a given sequence of key-value [pairs]. + * + * The returned map preserves the entry iteration order of the original map. + * Those [pairs] with unique keys are iterated in the end in the order of [pairs] sequence. + */ +//public operator fun Map.plus(pairs: Sequence>): Map +// = HashMap(this).apply { putAll(pairs) }.optimizeReadOnlyMap() + +/** + * Creates a new read-only map by replacing or adding entries to this map from another [map]. + * + * The returned map preserves the entry iteration order of the original map. + * Those entries of another [map] that are missing in this map are iterated in the end in the order of that [map]. + */ +//public operator fun Map.plus(map: Map): Map +// = HashMap(this).apply { putAll(map) } + + +/** + * Appends or replaces the given [pair] in this mutable map. + */ +public inline operator fun MutableMap.plusAssign(pair: Pair) { + put(pair.first, pair.second) +} + +/** + * Appends or replaces all pairs from the given collection of [pairs] in this mutable map. + */ +public inline operator fun MutableMap.plusAssign(pairs: Iterable>) { + putAll(pairs) +} + +/** + * Appends or replaces all pairs from the given array of [pairs] in this mutable map. + */ +public inline operator fun MutableMap.plusAssign(pairs: Array>) { + putAll(pairs) +} + +/** + * Appends or replaces all pairs from the given sequence of [pairs] in this mutable map. + */ +public inline operator fun MutableMap.plusAssign(pairs: Sequence>) { + putAll(pairs) +} + +/** + * Appends or replaces all entries from the given [map] in this mutable map. + */ +public inline operator fun MutableMap.plusAssign(map: Map) { + putAll(map) +} + + +// do not expose for now @kotlin.internal.InlineExposed +internal fun Map.optimizeReadOnlyMap() = when (size) { + 0 -> emptyMap() +// 1 -> toSingletonMapOrSelf() + else -> this +} + +// creates a singleton copy of map, if there is specialization available in target platform, otherwise returns itself +// internal inline fun Map.toSingletonMapOrSelf(): Map = toSingletonMap() + +// creates a singleton copy of map +//internal fun Map.toSingletonMap(): Map +// = with (entries.iterator().next()) { java.util.Collections.singletonMap(key, value) } diff --git a/runtime/src/main/kotlin/kotlin/collections/Sequence.kt b/runtime/src/main/kotlin/kotlin/collections/Sequence.kt new file mode 100644 index 00000000000..5ac63d3354d --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/Sequence.kt @@ -0,0 +1,23 @@ +package kotlin.sequences + +/** + * A sequence that returns values through its iterator. The values are evaluated lazily, and the sequence + * is potentially infinite. + * + * Sequences can be iterated multiple times, however some sequence implementations might constrain themselves + * to be iterated only once. That is mentioned specifically in their documentation (e.g. [generateSequence] overload). + * The latter sequences throw an exception on an attempt to iterate them the second time. + * + * Sequence operations, like [Sequence.map], [Sequence.filter] etc, generally preserve that property of a sequence, and + * again it's documented for an operation if it doesn't. + * + * @param T the type of elements in the sequence. + */ +public interface Sequence { + /** + * Returns an [Iterator] that returns the values from the sequence. + * + * Throws an exception if the sequence is constrained to be iterated once and `iterator` is invoked the second time. + */ + public operator fun iterator(): Iterator +} diff --git a/runtime/src/main/kotlin/kotlin/collections/Sets.kt b/runtime/src/main/kotlin/kotlin/collections/Sets.kt new file mode 100644 index 00000000000..fe24b17bf03 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/Sets.kt @@ -0,0 +1,71 @@ +package kotlin.collections + + +internal object EmptySet : Set { + override fun equals(other: Any?): Boolean = other is Set<*> && other.isEmpty() + override fun hashCode(): Int = 0 + override fun toString(): String = "[]" + + override val size: Int get() = 0 + override fun isEmpty(): Boolean = true + override fun contains(element: Nothing): Boolean = false + override fun containsAll(elements: Collection): Boolean = elements.isEmpty() + + override fun iterator(): Iterator = EmptyIterator + + private fun readResolve(): Any = EmptySet +} + + +/** Returns an empty read-only set. The returned set is serializable (JVM). */ +public fun emptySet(): Set = EmptySet +/** + * Returns a new read-only set with the given elements. + * Elements of the set are iterated in the order they were specified. + */ +public fun setOf(vararg elements: T): Set = if (elements.size > 0) elements.toSet() else emptySet() + +/** Returns an empty read-only set. The returned set is serializable (JVM). */ +public inline fun setOf(): Set = emptySet() + +/** + * Returns a new [MutableSet] with the given elements. + * Elements of the set are iterated in the order they were specified. + */ +public fun mutableSetOf(vararg elements: T): MutableSet = elements.toCollection(HashSet(mapCapacity(elements.size))) + +/** Returns a new [HashSet] with the given elements. */ +public fun hashSetOf(vararg elements: T): HashSet = elements.toCollection(HashSet(mapCapacity(elements.size))) + +/** + * Returns a new [LinkedHashSet] with the given elements. + * Elements of the set are iterated in the order they were specified. + */ +//public fun linkedSetOf(vararg elements: T): LinkedHashSet = elements.toCollection(LinkedHashSet(mapCapacity(elements.size))) + +/** Returns this Set if it's not `null` and the empty set otherwise. */ +public inline fun Set?.orEmpty(): Set = this ?: emptySet() + +/** + * Returns an immutable set containing only the specified object [element]. + * The returned set is serializable. + */ +//public fun setOf(element: T): Set = java.util.Collections.singleton(element) + + +/** + * Returns a new [SortedSet] with the given elements. + */ +// public fun sortedSetOf(vararg elements: T): TreeSet = elements.toCollection(TreeSet()) + +/** + * Returns a new [SortedSet] with the given [comparator] and elements. + */ +//public fun sortedSetOf(comparator: Comparator, vararg elements: T): TreeSet = elements.toCollection(TreeSet(comparator)) + + +internal fun Set.optimizeReadOnlySet() = when (size) { + 0 -> emptySet() + 1 -> setOf(iterator().next()) + else -> this +} diff --git a/runtime/src/main/kotlin/kotlin/internal/Annotations.kt b/runtime/src/main/kotlin/kotlin/internal/Annotations.kt new file mode 100644 index 00000000000..6ad51b9a607 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/internal/Annotations.kt @@ -0,0 +1,8 @@ +package kotlin.internal + + +@Target(AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER) +public annotation class OnlyInputTypes + +@Target(AnnotationTarget.TYPE, AnnotationTarget.TYPE_PARAMETER) +public annotation class OnlyOutputTypes \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/io/Console.kt b/runtime/src/main/kotlin/kotlin/io/Console.kt index 51d6af4b6be..92f7ed55a40 100644 --- a/runtime/src/main/kotlin/kotlin/io/Console.kt +++ b/runtime/src/main/kotlin/kotlin/io/Console.kt @@ -42,7 +42,6 @@ public fun print(message: Boolean) { @SymbolName("Kotlin_io_Console_println") external public fun println(message: String) -// TODO: enable, once override by name is implemented. public fun println(message: Byte) { println(message.toString()) } @@ -83,4 +82,4 @@ public fun println(message: T) { external public fun println() @SymbolName("Kotlin_io_Console_readLine") -external public fun readLine(): String +external public fun readLine(): String? diff --git a/runtime/src/main/kotlin/kotlin/util/Tuples.kt b/runtime/src/main/kotlin/kotlin/util/Tuples.kt new file mode 100644 index 00000000000..96ce97bacca --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/util/Tuples.kt @@ -0,0 +1,72 @@ +package kotlin + +/** + * Represents a generic pair of two values. + * + * There is no meaning attached to values in this class, it can be used for any purpose. + * Pair exhibits value semantics, i.e. two pairs are equal if both components are equal. + * + * An example of decomposing it into values: + * @sample samples.misc.Tuples.pairDestructuring + * + * @param A type of the first value. + * @param B type of the second value. + * @property first First value. + * @property second Second value. + * @constructor Creates a new instance of Pair. + */ +public data class Pair( + public val first: A, + public val second: B +) { + + /** + * Returns string representation of the [Pair] including its [first] and [second] values. + */ + public override fun toString(): String = "($first, $second)" +} + +/** + * Creates a tuple of type [Pair] from this and [that]. + * + * This can be useful for creating [Map] literals with less noise, for example: + * @sample samples.collections.Maps.Instantiation.mapFromPairs + */ +public infix fun A.to(that: B): Pair = Pair(this, that) + +/** + * Converts this pair into a list. + */ +public fun Pair.toList(): List = listOf(first, second) + +/** + * Represents a triad of values + * + * There is no meaning attached to values in this class, it can be used for any purpose. + * Triple exhibits value semantics, i.e. two triples are equal if all three components are equal. + * An example of decomposing it into values: + * @sample samples.misc.Tuples.tripleDestructuring + * + * @param A type of the first value. + * @param B type of the second value. + * @param C type of the third value. + * @property first First value. + * @property second Second value. + * @property third Third value. + */ +public data class Triple( + public val first: A, + public val second: B, + public val third: C +) { + + /** + * Returns string representation of the [Triple] including its [first], [second] and [third] values. + */ + public override fun toString(): String = "($first, $second, $third)" +} + +/** + * Converts this triple into a list. + */ +public fun Triple.toList(): List = listOf(first, second, third)