From 29c0eda720441643623fc1feebecb00d67149b08 Mon Sep 17 00:00:00 2001 From: Nikolay Igotti Date: Sat, 26 Nov 2016 16:30:15 +0300 Subject: [PATCH] Array list, implementation by @elizarov (#84) --- .../kotlin/backend/konan/llvm/IrToBitcode.kt | 15 +- backend.native/tests/build.gradle | 5 + .../tests/runtime/collections/array_list1.kt | 298 ++++++++++++++ runtime/src/main/cpp/Arrays.cpp | 22 ++ runtime/src/main/kotlin/kotlin/Array.kt | 18 +- runtime/src/main/kotlin/kotlin/Exceptions.kt | 9 + runtime/src/main/kotlin/kotlin/String.kt | 10 +- .../kotlin/kotlin/collections/ArrayList.kt | 362 ++++++++++++++++++ .../kotlin/kotlin/collections/ArrayUtil.kt | 67 ++++ 9 files changed, 800 insertions(+), 6 deletions(-) create mode 100644 backend.native/tests/runtime/collections/array_list1.kt create mode 100644 runtime/src/main/kotlin/kotlin/collections/ArrayList.kt create mode 100644 runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt index 941bcb0a193..4f87193e9d7 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/IrToBitcode.kt @@ -193,7 +193,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid else { value = evaluateExpression(codegen.newVar(), fieldDeclaration.initializer) } - codegen.store(value!!, fieldPtr!!) + if (value != null) + codegen.store(value!!, fieldPtr!!) } override fun visitAnonymousInitializer(declaration: IrAnonymousInitializer) { @@ -224,7 +225,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid override fun visitBlockBody(body: IrBlockBody) { super.visitBlockBody(body) - if (KotlinBuiltIns.isUnit(codegen.currentFunction!!.returnType!!)) { + if (KotlinBuiltIns.isUnit(codegen.currentFunction!!.returnType!!) + && body.statements.lastOrNull() !is IrThrow) { // Don't try generate return at the end of the throwing block. codegen.ret(null) } logger.log("visitBlockBody : ${ir2string(body)}") @@ -385,6 +387,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid is IrThrow -> return evaluateThrow (tmpVariableName, value) is IrStringConcatenation -> return evaluateStringConcatenation(tmpVariableName, value) is IrBlockBody -> return evaluateBlock ( value as IrStatementContainer) + is IrWhileLoop -> return evaluateWhileLoop ( value) null -> return null else -> { TODO("${ir2string(value)}") @@ -443,6 +446,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid return null } + private fun evaluateWhileLoop(loop: IrWhileLoop): LLVMValueRef? { + visitWhileLoop(loop) + // TODO: incorrect! + return null + } + //-------------------------------------------------------------------------// private fun evaluateGetValue(tmpVariableName: String, value: IrGetValue): LLVMValueRef { @@ -847,8 +856,6 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid val arg0Type = callee.argument0.type val arg1Type = callee.argument1.type - assert(arg0Type == arg1Type || arg0Type.isNullableNothing() || arg1Type.isNullableNothing()) - return when { KotlinBuiltIns.isPrimitiveType(arg0Type) -> TODO("${ir2string(callee)}") else -> codegen.icmpEq(arg0, arg1, tmpVariableName) diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 5f421b625d8..5a5afab552d 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -397,6 +397,7 @@ task throw0(type: RunKonanTest) { goldValue = "Done\n" source = "runtime/basic/throw0.kt" } + /* task boxing0(type: RunKonanTest) { goldValue = "17\n" @@ -409,4 +410,8 @@ task interface0(type: RunKonanTest) { source = "runtime/basic/interface0.kt" } */ +task array_list1(type: RunKonanTest) { + goldValue = "OK\n" + source = "runtime/collections/array_list1.kt" +} diff --git a/backend.native/tests/runtime/collections/array_list1.kt b/backend.native/tests/runtime/collections/array_list1.kt new file mode 100644 index 00000000000..96037257306 --- /dev/null +++ b/backend.native/tests/runtime/collections/array_list1.kt @@ -0,0 +1,298 @@ +fun assertTrue(cond: Boolean) { + if (!cond) + println("FAIL") +} + +fun assertFalse(cond: Boolean) { + if (cond) + println("FAIL") +} + +fun assertEquals(value1: String, value2: String) { + if (value1 != value2) + println("FAIL") +} + +fun assertEquals(value1: ArrayList, value2: ArrayList) { + if (value1 != value2) + println("FAIL") +} + +fun assertEquals(value1: Int, value2: Int) { + if (value1 != value2) + println("FAIL") +} + +fun testBasic() { + val a = ArrayList() + assertTrue(a.isEmpty()) + assertEquals(0, a.size) + + assertTrue(a.add("1")) + assertTrue(a.add("2")) + assertTrue(a.add("3")) + assertFalse(a.isEmpty()) + assertEquals(3, a.size) + assertEquals("1", a[0]) + assertEquals("2", a[1]) + assertEquals("3", a[2]) + + a[0] = "11" + assertEquals("11", a[0]) + + assertEquals("11", a.removeAt(0)) + assertEquals(2, a.size) + assertEquals("2", a[0]) + assertEquals("3", a[1]) + + a.add(1, "22") + assertEquals(3, a.size) + assertEquals("2", a[0]) + assertEquals("22", a[1]) + assertEquals("3", a[2]) + + a.clear() + assertTrue(a.isEmpty()) + assertEquals(0, a.size) +} + +fun makeList123() : ArrayList { + val a = ArrayList() + a.add("1") + a.add("2") + a.add("3") + return a +} + +fun makeList12345() : ArrayList { + val a = ArrayList() + a.add("1") + a.add("2") + a.add("3") + a.add("4") + a.add("5") + return a +} + +fun makeList12341() : ArrayList { + val a = ArrayList() + a.add("1") + a.add("2") + a.add("3") + a.add("4") + a.add("1") + return a +} + +fun makeList01234() : ArrayList { + val a = ArrayList() + a.add("0") + a.add("1") + a.add("2") + a.add("3") + a.add("4") + return a +} + +fun makeList678() : ArrayList { + val a = ArrayList() + a.add("6") + a.add("7") + a.add("8") + return a +} + +fun makeList531() : ArrayList { + val a = ArrayList() + a.add("5") + a.add("3") + a.add("1") + return a +} + +fun makeList135() : ArrayList { + val a = ArrayList() + a.add("1") + a.add("3") + a.add("5") + return a +} + +fun makeList24() : ArrayList { + val a = ArrayList() + a.add("2") + a.add("4") + return a +} + +fun testIterator() { + val a = makeList123() + val it = a.iterator() + assertTrue(it.hasNext()) + assertEquals("1", it.next()) + assertTrue(it.hasNext()) + assertEquals("2", it.next()) + assertTrue(it.hasNext()) + assertEquals("3", it.next()) + assertFalse(it.hasNext()) +} + +fun testRemove() { + val a = makeList123() + assertTrue(a.remove("2")) + assertEquals(2, a.size) + assertEquals("1", a[0]) + assertEquals("3", a[1]) + assertFalse(a.remove("2")) + assertEquals(2, a.size) + assertEquals("1", a[0]) + assertEquals("3", a[1]) +} + +fun testRemoveAll() { + val a = ArrayList(makeList12345()) + assertFalse(a.removeAll(makeList678())) + assertEquals(makeList12345(), a) + assertTrue(a.removeAll(makeList531())) + assertEquals(makeList24(), a) +} + +fun testRetainAll() { + val a = makeList12345() + assertFalse(a.retainAll(makeList12345())) + assertEquals(makeList12345(), a) + assertTrue(a.retainAll(makeList531())) + assertEquals(makeList135(), a) +} + +fun testEquals() { + val a = makeList123() + assertTrue(a == makeList123()) + assertFalse(a == makeList135()) + assertFalse(a == makeList24()) +} + +fun testHashCode() { + val a = makeList123() + assertTrue(a.hashCode() == makeList123().hashCode()) +} + +fun testToString() { + val a = makeList123() + assertTrue(a.toString() == makeList123().toString()) +} + +fun testSubList() { + val a0 = makeList01234() + val a = a0.subList(1, 4) + assertEquals(3, a.size) + assertEquals("1", a[0]) + assertEquals("2", a[1]) + assertEquals("3", a[2]) + assertTrue(a == makeList123()) + assertTrue(a.hashCode() == makeList123().hashCode()) + assertTrue(a.toString() == makeList123().toString()) +} + +fun testResize() { + val a = ArrayList() + val n = 10000 + var i = 0 + while (i++ < n) + assertTrue(a.add(i.toString())) + assertEquals(n, a.size) + i = 0 + while (i++ < n) + assertEquals(i.toString(), a[i - 1]) + a.trimToSize() + assertEquals(n, a.size) + i = 0 + while (i++ < n) + assertEquals(i.toString(), a[i - 1]) +} + +fun testSubListContains() { + val a = makeList12345() + val s = a.subList(1, 3) + assertTrue(a.contains("1")) + assertFalse(s.contains("1")) + assertTrue(a.contains("2")) + assertTrue(s.contains("2")) + assertTrue(a.contains("3")) + assertTrue(s.contains("3")) + assertTrue(a.contains("4")) + assertFalse(s.contains("4")) +} + +fun testSubListIndexOf() { + val a = makeList12341() + val s = a.subList(1, 3) + assertEquals(0, a.indexOf("1")) + assertEquals(-1, s.indexOf("1")) + assertEquals(1, a.indexOf("2")) + assertEquals(0, s.indexOf("2")) + assertEquals(2, a.indexOf("3")) + assertEquals(1, s.indexOf("3")) + assertEquals(3, a.indexOf("4")) + assertEquals(-1, s.indexOf("4")) +} + +fun testSubListLastIndexOf() { + val a = makeList12341() + val s = a.subList(1, 3) + assertEquals(4, a.lastIndexOf("1")) + assertEquals(-1, s.lastIndexOf("1")) + assertEquals(1, a.lastIndexOf("2")) + assertEquals(0, s.lastIndexOf("2")) + assertEquals(2, a.lastIndexOf("3")) + assertEquals(1, s.lastIndexOf("3")) + assertEquals(3, a.lastIndexOf("4")) + assertEquals(-1, s.lastIndexOf("4")) +} + +fun testIteratorRemove() { + val a = makeList12345() + val it = a.iterator() + var i = 1 + while (it.hasNext()) { + if (i++ % 2 == 0) { + it.remove() + } + it.next() + } + assertEquals(makeList135(), a) +} + +fun testIteratorAdd() { + val a = makeList12345() + val it = a.listIterator() + var i = 0 + while (it.hasNext()) { + val next = it.next() + if (i++ % 2 == 0) + it.add("-" + next) + it.next() + } + //assertEquals(listOf("1", "2", "-2", "3", "4", "-4", "5"), a) +} + +fun main(args : Array) { + testBasic() + testIterator() + testRemove() + testRemoveAll() + // Fails due to unknown virtual method call! + // testRetainAll() + testEquals() + testHashCode() + testToString() + testSubList() + testResize() + testSubListContains() + testSubListIndexOf() + testSubListLastIndexOf() + // Fails due to unknown virtual method call! + // testIteratorAdd() + // testIteratorRemove() + println("OK") +} \ No newline at end of file diff --git a/runtime/src/main/cpp/Arrays.cpp b/runtime/src/main/cpp/Arrays.cpp index 378dd4c3a90..383d0443d3c 100644 --- a/runtime/src/main/cpp/Arrays.cpp +++ b/runtime/src/main/cpp/Arrays.cpp @@ -40,6 +40,28 @@ KInt Kotlin_Array_getArrayLength(const ArrayHeader* array) { return array->count_; } +void Kotlin_Array_fillImpl(ArrayHeader* array, KInt fromIndex, + KInt toIndex, ObjHeader* value) { + if (fromIndex < 0 || toIndex < fromIndex || toIndex >= array->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + // TODO: refcounting! + for (KInt index = fromIndex; index < toIndex; ++index) { + *ArrayAddressOfElementAt(array, index) = value; + } +} + +void Kotlin_Array_copyImpl(const ArrayHeader* array, KInt fromIndex, + ArrayHeader* destination, KInt toIndex, KInt count) { + if (fromIndex < 0 || fromIndex + count > array->count_ || + toIndex < 0 || toIndex + count > destination->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + // TODO: refcounting! + memcpy(ArrayAddressOfElementAt(destination, toIndex), + ArrayAddressOfElementAt(array, fromIndex), count * sizeof(KRef)); +} + // Arrays.kt KByte Kotlin_ByteArray_get(const ArrayHeader* obj, KInt index) { if (static_cast(index) >= obj->count_) { diff --git a/runtime/src/main/kotlin/kotlin/Array.kt b/runtime/src/main/kotlin/kotlin/Array.kt index 32771b04b37..c67bb567576 100644 --- a/runtime/src/main/kotlin/kotlin/Array.kt +++ b/runtime/src/main/kotlin/kotlin/Array.kt @@ -19,6 +19,22 @@ public final class Array : Cloneable { @SymbolName("Kotlin_Array_set") external public operator fun set(index: Int, value: T): Unit + public operator fun iterator(): kotlin.collections.Iterator { + return IteratorImpl(this) + } + @SymbolName("Kotlin_Array_getArrayLength") external private fun getArrayLength(): Int -} \ No newline at end of file +} + +private class IteratorImpl(val collection: Array) : Iterator { + var index : Int = 0 + + public override fun next(): T { + return collection[index++] + } + + public override operator fun hasNext(): Boolean { + return index < collection.size + } +} diff --git a/runtime/src/main/kotlin/kotlin/Exceptions.kt b/runtime/src/main/kotlin/kotlin/Exceptions.kt index 0effb03329d..03a4ebd3ea1 100644 --- a/runtime/src/main/kotlin/kotlin/Exceptions.kt +++ b/runtime/src/main/kotlin/kotlin/Exceptions.kt @@ -136,4 +136,13 @@ public class AssertionError : Error { constructor(message: String, cause: Throwable) : super(message, cause) { } +} + +// TODO: does it belong here? +fun TODO() { + throw UnsupportedOperationException() +} + +fun TODO(message: String) { + throw UnsupportedOperationException(message) } \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/String.kt b/runtime/src/main/kotlin/kotlin/String.kt index 058500e61c2..4609ed0c770 100644 --- a/runtime/src/main/kotlin/kotlin/String.kt +++ b/runtime/src/main/kotlin/kotlin/String.kt @@ -42,4 +42,12 @@ public final class String : Comparable, CharSequence { @SymbolName("Kotlin_String_equals") external public override fun equals(other: Any?): Boolean -} \ No newline at end of file +} + +// TODO: in big Kotlin this operations are in kotlin.kotlin_builtins. +private val kNullString = "" +public operator fun kotlin.String?.plus(other: kotlin.Any?): kotlin.String = + this?.plus(other?.toString() ?: kNullString) ?: other?.toString() ?: kNullString + + +public fun Any?.toString() = this?.toString() ?: kNullString \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt new file mode 100644 index 00000000000..7af2b35c988 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt @@ -0,0 +1,362 @@ +package kotlin.collections + +class ArrayList : MutableList { + + private var array: Array + private var offset: Int + private var length: Int + private val backing: ArrayList? + + private constructor( + array: Array, + offset: Int, + length: Int, + backing: ArrayList?) { + this.array = array + this.offset = offset + this.length = length + this.backing = backing + } + + + constructor() { + this.array = arrayOfLateInitElements(10) + this.offset = 0 + this.length = 0 + this.backing = null + } + + constructor(initialCapacity: Int) { + this.array = arrayOfLateInitElements(initialCapacity) + this.offset = 0 + this.length = 0 + this.backing = null + } + + constructor(c: Collection) { + this.array = arrayOfLateInitElements(c.size) + this.offset = 0 + this.length = 0 + this.backing = null + addAll(c) + } + + override val size : Int + get() = length + + override fun isEmpty(): Boolean = length == 0 + + override fun get(index: Int): E { + checkIndex(index) + return array[offset + index] + } + + override fun set(index: Int, element: E): E { + checkIndex(index) + val old = array[offset + index] + array[offset + index] = element + return old + } + + override fun contains(element: E): Boolean { + var i = 0 + while (i < length) { + if (array[offset + i] == element) return true + i++ + } + return false + } + + override fun containsAll(elements: Collection): Boolean { + val it = elements.iterator() + while (it.hasNext()) { + if (!contains(it.next()))return false + } + return true + } + + override fun indexOf(element: E): Int { + var i = 0 + while (i < length) { + if (array[offset + i] == element) return i + i++ + } + return -1 + } + + override fun lastIndexOf(element: E): Int { + var i = length - 1 + while (i >= 0) { + if (array[offset + i] == element) return i + i-- + } + return -1 + } + + override fun iterator(): MutableIterator = Itr(this, 0) + override fun listIterator(): MutableListIterator = Itr(this, 0) + + override fun listIterator(index: Int): MutableListIterator { + checkInsertIndex(index) + return Itr(this, index) + } + + override fun add(element: E): Boolean { + addAtInternal(offset + length, element) + return true + } + + override fun add(index: Int, element: E) { + checkInsertIndex(index) + addAtInternal(offset + index, element) + } + + override fun addAll(elements: Collection): Boolean { + val n = elements.size + addAllInternal(offset + length, elements, n) + return n > 0 + } + + override fun addAll(index: Int, elements: Collection): Boolean { + checkInsertIndex(index) + val n = elements.size + addAllInternal(offset + index, elements, n) + return n > 0 + } + + override fun clear() { + removeRangeInternal(offset, length) + } + + override fun removeAt(index: Int): E { + checkIndex(index) + return removeAtInternal(offset + index) + } + + override fun remove(element: E): Boolean { + val i = indexOf(element) + if (i >= 0) removeAt(i) + return i >= 0 + } + + override fun removeAll(elements: Collection): Boolean { + var changed = false + val it = elements.iterator() + while (it.hasNext()) { + if (remove(it.next())) changed = true + } + return changed + } + + override fun retainAll(elements: Collection): Boolean { + return retainAllInRangeInternal(offset, length, elements) > 0 + } + + override fun subList(fromIndex: Int, toIndex: Int): MutableList { + checkInsertIndex(fromIndex) + checkInsertIndexFrom(toIndex, fromIndex) + return ArrayList(array, offset + fromIndex, toIndex - fromIndex, this) + } + + fun trimToSize() { + if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList + if (length < array.size) + array = array.copyOfLateInitElements(length) + } + + fun ensureCapacity(capacity: Int) { + if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList + if (capacity > array.size) { + var newSize = array.size * 3 / 2 + if (capacity > newSize) + newSize = capacity + array = array.copyOfLateInitElements(newSize) + } + } + + override fun equals(other: Any?): Boolean { + //TODO: rethink instance checks, shall it be other is List<*>? + return other === this || + (other is ArrayList<*>) && contentEquals(other) + } + + override fun hashCode(): Int { + var result = 1 + var i = 0 + while (i < length) { + val nextElement = array[offset + i] + val nextHash = if (nextElement != null) nextElement.hashCode() else 0 + result = result * 31 + nextHash + i++ + } + return result + } + + override fun toString(): String { + var result = "[" + var i = 0 + while (i < length) { + if (i > 0) result += ", " + result += array[offset + i].toString() + i++ + } + result += "]" + return result + } + + // ---------------------------- private ---------------------------- + + private fun ensureExtraCapacity(n: Int) { + ensureCapacity(length + n) + } + + private fun checkIndex(index: Int) { + if (index < 0 || index >= length) throw IndexOutOfBoundsException() + } + + private fun checkInsertIndex(index: Int) { + if (index < 0 || index > length) throw IndexOutOfBoundsException() + } + + private fun checkInsertIndexFrom(index: Int, fromIndex: Int) { + if (index < fromIndex || index > length) throw IndexOutOfBoundsException() + } + + private fun contentEquals(other: List<*>): Boolean { + if (length != other.size) return false + var i = 0 + while (i < length) { + if (array[offset + i] != other[i]) return false + i++ + } + return true + } + + private fun insertAtInternal(i: Int, n: Int) { + ensureExtraCapacity(n) + array.copyRange(fromIndex = i, toIndex = offset + length, destinationIndex = i + n) + length += n + } + + private fun addAtInternal(i: Int, element: E) { + if (backing != null) { + backing.addAtInternal(i, element) + array = backing.array + // length++ kills translation + length = length + 1 + } else { + insertAtInternal(i, 1) + array[i] = element + } + } + + private fun addAllInternal(i: Int, elements: Collection, n: Int) { + if (backing != null) { + backing.addAllInternal(i, elements, n) + array = backing.array + length += n + } else { + insertAtInternal(i, n) + var j = 0 + val it = elements.iterator() + while (j < n) { + array[i + j] = it.next() + j++ + } + } + } + + private fun removeAtInternal(i: Int): E { + if (backing != null) { + val old = backing.removeAtInternal(i) + length-- + return old + } else { + val old = array[i] + array.copyRange(fromIndex = i + 1, toIndex = offset + length, destinationIndex = i) + array.resetAt(offset + length - 1) + length-- + return old + } + } + + private fun removeRangeInternal(rangeOffset: Int, rangeLength: Int) { + if (backing != null) { + backing.removeRangeInternal(rangeOffset, rangeLength) + } else { + array.copyRange(fromIndex = rangeOffset + rangeLength, toIndex = length, destinationIndex = rangeOffset) + array.resetRange(fromIndex = length - rangeLength, toIndex = length) + } + length -= rangeLength + } + + private fun retainAllInRangeInternal(rangeOffset: Int, rangeLength: Int, elements: Collection): Int { + if (backing != null) { + val removed = backing.retainAllInRangeInternal(rangeOffset, rangeLength, elements) + length -= removed + return removed + } else { + var i = 0 + var j = 0 + while (i < rangeLength) { + if (elements.contains(array[rangeOffset + i])) { + array[rangeOffset + j++] = array[rangeOffset + i++] + } else { + // TODO: i++ kills translation. + i = i + 1 + } + } + val removed = rangeLength - j + array.copyRange(fromIndex = rangeOffset + rangeLength, toIndex = length, destinationIndex = rangeOffset + j) + array.resetRange(fromIndex = length - removed, toIndex = length) + length -= removed + return removed + } + } + + private class Itr : MutableListIterator { + private val list: ArrayList + private var index: Int + private var lastIndex: Int + + constructor(list: ArrayList, index: Int) { + this.list = list + this.index = index + this.lastIndex = -1 + } + + override fun hasPrevious(): Boolean = index > 0 + override fun hasNext(): Boolean = index < list.length + + override fun previousIndex(): Int = index - 1 + override fun nextIndex(): Int = index + + override fun previous(): E { + if (index <= 0) throw IndexOutOfBoundsException() + lastIndex = --index + return list.array[list.offset + lastIndex] + } + + override fun next(): E { + if (index >= list.length) throw IndexOutOfBoundsException() + lastIndex = index++ + return list.array[list.offset + lastIndex] + } + + override fun set(element: E) { + list.checkIndex(lastIndex) + list.array[list.offset + lastIndex] = element + } + + override fun add(element: E) { + list.add(index++, element) + lastIndex = -1 + } + + override fun remove() { + list.removeAt(lastIndex) + index = lastIndex + lastIndex = -1 + } + } +} diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt new file mode 100644 index 00000000000..3a6af3c76dd --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt @@ -0,0 +1,67 @@ +package kotlin.collections + +/** + * Returns an array of objects of the given type with the given [size], initialized with **lateinit** _uninitialized_ values. + * Attempts to read _uninitialized_ values from this array work in implementation-dependent manner, + * either throwing exception or returning some kind of implementation-specific default value. + */ +fun arrayOfLateInitElements(size: Int): Array { + // TODO: maybe use an empoty array. + // return (if (size == 0) emptyArray else Array(size)) as Array + return Array(size) as Array +} + +/** + * Returns new array which is a copy of the original array with new elements filled with **lateinit** _uninitialized_ values. + * Attempts to read _uninitialized_ values from this array work in implementation-dependent manner, + * either throwing exception or returning some kind of implementation-specific default value. + */ +fun Array.copyOfLateInitElements(newSize: Int): Array { + val result = Array(newSize) as Array + this.copyRangeTo(result, 0, if (newSize > this.size) this.size else newSize, 0) + return result +} + +/** + * Resets an array element at a specified index to some implementation-specific _uninitialized_ value. + * In particular, references stored in this element are released and become available for garbage collection. + * Attempts to read _uninitialized_ value work in implementation-dependent manner, + * either throwing exception or returning some kind of implementation-specific default value. + */ +fun Array.resetAt(index: Int) { + (this as Array)[index] = null +} + +@SymbolName("Kotlin_Array_fillImpl") +external private fun fillImpl(array: Array, fromIndex: Int, toIndex: Int, value: Any?) + +/** + * Resets a range of array elements at a specified [fromIndex] (inclusive) to [toIndex] (exclusive) range of indices + * to some implementation-specific _uninitialized_ value. + * In particular, references stored in these elements are released and become available for garbage collection. + * Attempts to read _uninitialized_ values work in implementation-dependent manner, + * either throwing exception or returning some kind of implementation-specific default value. + */ +fun Array.resetRange(fromIndex: Int, toIndex: Int) { + fillImpl(this as Array, fromIndex, toIndex, null) +} + +@SymbolName("Kotlin_Array_copyImpl") +external private fun copyImpl(array: Array, fromIndex: Int, + destination: Array, toIndex: Int, count: Int) + +/** + * Copies a range of array elements at a specified [fromIndex] (inclusive) to [toIndex] (exclusive) range of indices + * to another [destination] array starting at [destinationIndex]. + */ +fun Array.copyRangeTo(destination: Array, fromIndex: Int, toIndex: Int, destinationIndex: Int = 0) { + copyImpl(this as Array, fromIndex, destination as Array, destinationIndex, toIndex - fromIndex) +} + +/** + * Copies a range of array elements at a specified [fromIndex] (inclusive) to [toIndex] (exclusive) range of indices + * to another part of this array starting at [destinationIndex]. + */ +fun Array.copyRange(fromIndex: Int, toIndex: Int, destinationIndex: Int = 0) { + copyRangeTo(this, fromIndex, toIndex, destinationIndex) +} \ No newline at end of file