diff --git a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt index 54033bdd9e5..eff41a59725 100644 --- a/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt +++ b/backend.native/compiler/ir/backend.native/src/org/jetbrains/kotlin/backend/konan/llvm/CodeGenerator.kt @@ -3,11 +3,9 @@ package org.jetbrains.kotlin.backend.konan.llvm import kotlinx.cinterop.* import llvm.* -import org.jetbrains.kotlin.descriptors.ClassConstructorDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.FunctionDescriptor import org.jetbrains.kotlin.descriptors.PropertyDescriptor -import org.jetbrains.kotlin.ir.declarations.IrConstructor import org.jetbrains.kotlin.ir.declarations.IrFunction import org.jetbrains.kotlin.types.KotlinType 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 648827c143f..054aa928131 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 @@ -4,32 +4,25 @@ import kotlinx.cinterop.* import llvm.* import org.jetbrains.kotlin.backend.konan.* import org.jetbrains.kotlin.builtins.KotlinBuiltIns -import org.jetbrains.kotlin.backend.konan.llvm.KonanPlatform import org.jetbrains.kotlin.descriptors.* -import org.jetbrains.kotlin.descriptors.impl.LazyClassReceiverParameterDescriptor -import org.jetbrains.kotlin.descriptors.impl.LocalVariableDescriptor -import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.ir.IrElement import org.jetbrains.kotlin.ir.declarations.* import org.jetbrains.kotlin.ir.descriptors.IrBuiltinOperatorDescriptorBase -import org.jetbrains.kotlin.ir.descriptors.IrTemporaryVariableDescriptor import org.jetbrains.kotlin.ir.expressions.* import org.jetbrains.kotlin.ir.expressions.impl.IrBinaryPrimitiveImpl -import org.jetbrains.kotlin.ir.expressions.impl.IrBreakImpl import org.jetbrains.kotlin.ir.expressions.impl.IrSetterCallImpl import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid import org.jetbrains.kotlin.ir.visitors.acceptVoid +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.name.Name import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.descriptorUtil.classId import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameSafe -import org.jetbrains.kotlin.resolve.descriptorUtil.getAllSuperclassesWithoutAny import org.jetbrains.kotlin.types.KotlinType import org.jetbrains.kotlin.types.TypeUtils import org.jetbrains.kotlin.types.typeUtil.isNothing -import org.jetbrains.kotlin.types.typeUtil.isNullableNothing import org.jetbrains.kotlin.types.typeUtil.isUnit fun emitLLVM(module: IrModuleFragment, runtimeFile: String, outFile: String) { @@ -726,7 +719,8 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// private fun evaluateStringConcatenation(tmpVariableName: String, value: IrStringConcatenation): LLVMValueRef? { - val stringPlus = KonanPlatform.builtIns.stringType.memberScope.getContributedFunctions(Name.identifier("plus"), NoLookupLocation.FROM_BACKEND).first() + val stringPlus = KonanPlatform.builtIns.stringType.memberScope.getContributedFunctions( + Name.identifier("plus"), NoLookupLocation.FROM_BACKEND).first() var res:LLVMValueRef? = null val strings:List = value.arguments.map { val descriptor = getToString(it.type) @@ -1464,6 +1458,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } descriptor.valueParameters.map { + //println(it.index) res += (it to expression.getValueArgument(it.index)!!) } @@ -1549,6 +1544,7 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private val kLt0 = Name.identifier("LT0") private val kLteq0 = Name.identifier("LTEQ0") private val kNot = Name.identifier("NOT") + private val kThrowNpe = Name.identifier("THROW_NPE") private val kImmZero = LLVMConstInt(LLVMInt32Type(), 0, 1)!! private val kImmOne = LLVMConstInt(LLVMInt32Type(), 1, 1)!! private val kTrue = LLVMConstInt(LLVMInt1Type(), 1, 1)!! @@ -1559,13 +1555,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid logger.log("evaluateCall : $tmpVariableName origin:${ir2string(callee)}") val descriptor = callee.descriptor when (descriptor.name) { - kEqeq -> return evaluateOperatorEqeq (callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) - ktEqeqeq -> return evaluateOperatorEqeqeq(callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) - kGt0 -> return codegen.icmpGt(args[0]!!, kImmZero, tmpVariableName) - kGteq0 -> return codegen.icmpGe(args[0]!!, kImmZero, tmpVariableName) - kLt0 -> return codegen.icmpLt(args[0]!!, kImmZero, tmpVariableName) - kLteq0 -> return codegen.icmpLe(args[0]!!, kImmZero, tmpVariableName) - kNot -> return codegen.icmpNe(args[0]!!, kTrue, tmpVariableName) + kEqeq -> return evaluateOperatorEqeq (callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) + ktEqeqeq -> return evaluateOperatorEqeqeq(callee as IrBinaryPrimitiveImpl, args[0]!!, args[1]!!, tmpVariableName) + kGt0 -> return codegen.icmpGt(args[0]!!, kImmZero, tmpVariableName) + kGteq0 -> return codegen.icmpGe(args[0]!!, kImmZero, tmpVariableName) + kLt0 -> return codegen.icmpLt(args[0]!!, kImmZero, tmpVariableName) + kLteq0 -> return codegen.icmpLe(args[0]!!, kImmZero, tmpVariableName) + kNot -> return codegen.icmpNe(args[0]!!, kTrue, tmpVariableName) + // TODO: reconsider. + kThrowNpe -> return evaluateOperatorThrowNpe() else -> { TODO(descriptor.name.toString()) } @@ -1574,9 +1572,9 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid //-------------------------------------------------------------------------// - private fun getEquals(type: KotlinType, methodName: String) : SimpleFunctionDescriptor { + private fun getEquals(type: KotlinType) : SimpleFunctionDescriptor { - val name = Name.identifier(methodName) + val name = Name.identifier("equals") val descriptors = type.memberScope.getContributedFunctions(name, NoLookupLocation.FROM_BACKEND).filter { it.valueParameters.size == 1 && KotlinBuiltIns.isAnyOrNullableAny(it.valueParameters[0].type) } @@ -1593,6 +1591,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid return descriptor } + private fun getThrowNpe(): SimpleFunctionDescriptor { + val name = Name.identifier("ThrowNullPointerException") + val moduleDescriptor = KonanPlatform.builtIns.builtInsModule + val packageDescriptor = moduleDescriptor.getPackage(FqName("konan.internal")) + val descriptor = packageDescriptor.memberScope.getContributedFunctions( + name, NoLookupLocation.FROM_BACKEND).first() + return descriptor + } + //-------------------------------------------------------------------------// private fun evaluateOperatorEqeq(callee: IrBinaryPrimitiveImpl, arg0: LLVMValueRef, arg1: LLVMValueRef, tmpVariableName: String): LLVMValueRef { @@ -1611,14 +1618,15 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid private fun generateEqeqForObjects(callee: IrBinaryPrimitiveImpl, arg0: LLVMValueRef, arg1: LLVMValueRef, tmpVariableName: String): LLVMValueRef { val arg0Type = callee.argument0.type - val descriptor = getEquals(arg0Type, "equals") // Get descriptor for "arg0.equals". + val descriptor = getEquals(arg0Type) // Get descriptor for "arg0.equals". if (arg0Type.isMarkedNullable) { // If arg0 is nullable. val bbEq2 = codegen.basicBlock() // Block to process "eqeq". val bbEq3 = codegen.basicBlock() // Block to process "eqeqeq". val bbExit = codegen.basicBlock() // Exit block for feather generation. val result = codegen.alloca(codegen.getLLVMType(callee.type), codegen.newVar()) - val condition = codegen.icmpEq(arg0, codegen.kNullObjHeaderPtr, codegen.newVar()) // Compare arg0 with "null". + val condition = codegen.icmpEq( + arg0, codegen.kNullObjHeaderPtr, codegen.newVar()) // Compare arg0 with "null". codegen.condBr(condition, bbEq3, bbEq2) // If (arg0 == null) bbEq3 else bbEq2. codegen.positionAtEnd(bbEq3) // Get generation to bbEq3. @@ -1653,6 +1661,12 @@ internal class CodeGeneratorVisitor(val context: Context) : IrElementVisitorVoid } } + private fun evaluateOperatorThrowNpe(): LLVMValueRef { + val result = evaluateSimpleFunctionCall("", getThrowNpe(), listOf()) + codegen.unreachable() + return result + } + //-------------------------------------------------------------------------// private fun generateWhenCase(isUnit:Boolean, isNothing:Boolean, resultPtr: LLVMValueRef?, branch: IrBranch, bbNext: LLVMBasicBlockRef?, bbExit: LLVMBasicBlockRef?) { diff --git a/backend.native/tests/build.gradle b/backend.native/tests/build.gradle index 514d2ad03de..057ef865bd8 100644 --- a/backend.native/tests/build.gradle +++ b/backend.native/tests/build.gradle @@ -477,6 +477,16 @@ task array_list1(type: RunKonanTest) { source = "runtime/collections/array_list1.kt" } +task hash_map0(type: RunKonanTest) { + goldValue = "OK\n" + source = "runtime/collections/hash_map0.kt" +} + +task hash_set0(type: RunKonanTest) { + goldValue = "OK\n" + source = "runtime/collections/hash_set0.kt" +} + task listof0(type: RunKonanTest) { goldValue = "abc\n[a, b, c, d]\n[n, s, a]\n" arguments = ["a"] diff --git a/backend.native/tests/runtime/collections/array_list1.kt b/backend.native/tests/runtime/collections/array_list1.kt index 3c064afbfe0..d3799bf392c 100644 --- a/backend.native/tests/runtime/collections/array_list1.kt +++ b/backend.native/tests/runtime/collections/array_list1.kt @@ -8,12 +8,7 @@ fun assertFalse(cond: Boolean) { println("FAIL") } -fun assertEquals(value1: String, value2: String) { - if (value1 != value2) - println("FAIL") -} - -fun assertEquals(value1: ArrayList, value2: ArrayList) { +fun assertEquals(value1: Any?, value2: Any?) { if (value1 != value2) println("FAIL") } @@ -56,77 +51,8 @@ fun testBasic() { 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 a = ArrayList(listOf("1", "2", "3")) val it = a.iterator() assertTrue(it.hasNext()) assertEquals("1", it.next()) @@ -137,8 +63,17 @@ fun testIterator() { assertFalse(it.hasNext()) } +fun testContainsAll() { + val a = ArrayList(listOf("1", "2", "3", "4", "5")) + assertFalse(a.containsAll(listOf("6", "7", "8"))) + assertFalse(a.containsAll(listOf("5", "6", "7"))) + assertFalse(a.containsAll(listOf("4", "5", "6"))) + assertTrue(a.containsAll(listOf("3", "4", "5"))) + assertTrue(a.containsAll(listOf("2", "3", "4"))) +} + fun testRemove() { - val a = makeList123() + val a = ArrayList(listOf("1", "2", "3")) assertTrue(a.remove("2")) assertEquals(2, a.size) assertEquals("1", a[0]) @@ -150,75 +85,68 @@ fun testRemove() { } fun testRemoveAll() { - val a = ArrayList(makeList12345()) - assertFalse(a.removeAll(makeList678())) - assertEquals(makeList12345(), a) - assertTrue(a.removeAll(makeList531())) - assertEquals(makeList24(), a) + val a = ArrayList(listOf("1", "2", "3", "4", "5")) + assertFalse(a.removeAll(listOf("6", "7", "8"))) + assertEquals(listOf("1", "2", "3", "4", "5"), a) + assertTrue(a.removeAll(listOf("5", "3", "1"))) + assertEquals(listOf("2", "4"), a) } fun testRetainAll() { - val a = makeList12345() - assertFalse(a.retainAll(makeList12345())) - assertEquals(makeList12345(), a) - assertTrue(a.retainAll(makeList531())) - assertEquals(makeList135(), a) + val a = ArrayList(listOf("1", "2", "3", "4", "5")) + assertFalse(a.retainAll(listOf("1", "2", "3", "4", "5"))) + assertEquals(listOf("1", "2", "3", "4", "5"), a) + assertTrue(a.retainAll(listOf("5", "3", "1"))) + assertEquals(listOf("1", "3", "5"), a) } fun testEquals() { - val a = makeList123() - assertTrue(a == makeList123()) - assertFalse(a == makeList135()) - assertFalse(a == makeList24()) + val a = ArrayList(listOf("1", "2", "3")) + assertTrue(a == listOf("1", "2", "3")) + assertFalse(a == listOf("2", "3", "1")) // order matters + assertFalse(a == listOf("1", "2", "4")) + assertFalse(a == listOf("1", "2")) } fun testHashCode() { - val a = makeList123() - assertTrue(a.hashCode() == makeList123().hashCode()) + val a = ArrayList(listOf("1", "2", "3")) + assertTrue(a.hashCode() == listOf("1", "2", "3").hashCode()) } fun testToString() { - val a = makeList123() - assertTrue(a.toString() == makeList123().toString()) -} - -fun testToString2() { - val a = makeList123() - assertEquals(a.toString(), "[1, 2, 3]") + val a = ArrayList(listOf("1", "2", "3")) + assertTrue(a.toString() == listOf("1", "2", "3").toString()) } fun testSubList() { - val a0 = makeList01234() + val a0 = ArrayList(listOf("0", "1", "2", "3", "4")) 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()) + assertTrue(a == listOf("1", "2", "3")) + assertTrue(a.hashCode() == listOf("1", "2", "3").hashCode()) + assertTrue(a.toString() == listOf("1", "2", "3").toString()) } fun testResize() { val a = ArrayList() val n = 10000 - var i = 0 - while (i++ < n) + for (i in 1..n) assertTrue(a.add(i.toString())) assertEquals(n, a.size) - i = 0 - while (i++ < n) + for (i in 1..n) assertEquals(i.toString(), a[i - 1]) a.trimToSize() assertEquals(n, a.size) - i = 0 - while (i++ < n) + for (i in 1..n) assertEquals(i.toString(), a[i - 1]) } fun testSubListContains() { - val a = makeList12345() + val a = ArrayList(listOf("1", "2", "3", "4")) val s = a.subList(1, 3) assertTrue(a.contains("1")) assertFalse(s.contains("1")) @@ -231,7 +159,7 @@ fun testSubListContains() { } fun testSubListIndexOf() { - val a = makeList12341() + val a = ArrayList(listOf("1", "2", "3", "4", "1")) val s = a.subList(1, 3) assertEquals(0, a.indexOf("1")) assertEquals(-1, s.indexOf("1")) @@ -244,7 +172,7 @@ fun testSubListIndexOf() { } fun testSubListLastIndexOf() { - val a = makeList12341() + val a = ArrayList(listOf("1", "2", "3", "4", "1")) val s = a.subList(1, 3) assertEquals(4, a.lastIndexOf("1")) assertEquals(-1, s.lastIndexOf("1")) @@ -256,29 +184,164 @@ fun testSubListLastIndexOf() { assertEquals(-1, s.lastIndexOf("4")) } +fun testSubListClear() { + val a = ArrayList(listOf("1", "2", "3", "4")) + val s = a.subList(1, 3) + assertEquals(listOf("2", "3"), s) + + s.clear() + assertEquals(listOf(), s) + assertEquals(listOf("1", "4"), a) +} + +fun testSubListSubListClear() { + val a = ArrayList(listOf("1", "2", "3", "4", "5", "6")) + val s = a.subList(1, 5) + val q = s.subList(1, 3) + assertEquals(listOf("2", "3", "4", "5"), s) + assertEquals(listOf("3", "4"), q) + + q.clear() + assertEquals(listOf(), q) + assertEquals(listOf("2", "5"), s) + assertEquals(listOf("1", "2", "5", "6"), a) +} + +fun testSubListAdd() { + val a = ArrayList(listOf("1", "2", "3", "4")) + val s = a.subList(1, 3) + assertEquals(listOf("2", "3"), s) + + assertTrue(s.add("5")) + assertEquals(listOf("2", "3", "5"), s) + assertEquals(listOf("1", "2", "3", "5", "4"), a) +} + +fun testSubListSubListAdd() { + val a = ArrayList(listOf("1", "2", "3", "4", "5", "6")) + val s = a.subList(1, 5) + val q = s.subList(1, 3) + assertEquals(listOf("2", "3", "4", "5"), s) + assertEquals(listOf("3", "4"), q) + + assertTrue(q.add("7")) + assertEquals(listOf("3", "4", "7"), q) + assertEquals(listOf("2", "3", "4", "7", "5"), s) + assertEquals(listOf("1", "2", "3", "4", "7", "5", "6"), a) +} + +fun testSubListAddAll() { + val a = ArrayList(listOf("1", "2", "3", "4")) + val s = a.subList(1, 3) + assertEquals(listOf("2", "3"), s) + + assertTrue(s.addAll(listOf("5", "6"))) + assertEquals(listOf("2", "3", "5", "6"), s) + assertEquals(listOf("1", "2", "3", "5", "6", "4"), a) +} + +fun testSubListSubListAddAll() { + val a = ArrayList(listOf("1", "2", "3", "4", "5", "6")) + val s = a.subList(1, 5) + val q = s.subList(1, 3) + assertEquals(listOf("2", "3", "4", "5"), s) + assertEquals(listOf("3", "4"), q) + + assertTrue(q.addAll(listOf("7", "8"))) + assertEquals(listOf("3", "4", "7", "8"), q) + assertEquals(listOf("2", "3", "4", "7", "8", "5"), s) + assertEquals(listOf("1", "2", "3", "4", "7", "8", "5", "6"), a) +} + +fun testSubListRemoveAt() { + val a = ArrayList(listOf("1", "2", "3", "4", "5")) + val s = a.subList(1, 4) + assertEquals(listOf("2", "3", "4"), s) + + assertEquals("3", s.removeAt(1)) + assertEquals(listOf("2", "4"), s) + assertEquals(listOf("1", "2", "4", "5"), a) +} + +fun testSubListSubListRemoveAt() { + val a = ArrayList(listOf("1", "2", "3", "4", "5", "6", "7")) + val s = a.subList(1, 6) + val q = s.subList(1, 4) + assertEquals(listOf("2", "3", "4", "5", "6"), s) + assertEquals(listOf("3", "4", "5"), q) + + assertEquals("4", q.removeAt(1)) + assertEquals(listOf("3", "5"), q) + assertEquals(listOf("2", "3", "5", "6"), s) + assertEquals(listOf("1", "2", "3", "5", "6", "7"), a) +} + +fun testSubListRemoveAll() { + val a = ArrayList(listOf("1", "2", "3", "4", "5")) + val s = a.subList(1, 4) + assertEquals(listOf("2", "3", "4"), s) + + assertTrue(s.removeAll(listOf("3", "5"))) + assertEquals(listOf("2", "4"), s) + assertEquals(listOf("1", "2", "4", "5"), a) +} + +fun testSubListSubListRemoveAll() { + val a = ArrayList(listOf("1", "2", "3", "4", "5", "6", "7")) + val s = a.subList(1, 6) + val q = s.subList(1, 4) + assertEquals(listOf("2", "3", "4", "5", "6"), s) + assertEquals(listOf("3", "4", "5"), q) + + assertTrue(q.removeAll(listOf("4", "6"))) + assertEquals(listOf("3", "5"), q) + assertEquals(listOf("2", "3", "5", "6"), s) + assertEquals(listOf("1", "2", "3", "5", "6", "7"), a) +} + +fun testSubListRetainAll() { + val a = ArrayList(listOf("1", "2", "3", "4", "5")) + val s = a.subList(1, 4) + assertEquals(listOf("2", "3", "4"), s) + + assertTrue(s.retainAll(listOf("5", "3"))) + assertEquals(listOf("3"), s) + assertEquals(listOf("1", "3", "5"), a) +} + +fun testSubListSubListRetainAll() { + val a = ArrayList(listOf("1", "2", "3", "4", "5", "6", "7")) + val s = a.subList(1, 6) + val q = s.subList(1, 4) + assertEquals(listOf("2", "3", "4", "5", "6"), s) + assertEquals(listOf("3", "4", "5"), q) + + assertTrue(q.retainAll(listOf("5", "3"))) + assertEquals(listOf("3", "5"), q) + assertEquals(listOf("2", "3", "5", "6"), s) + assertEquals(listOf("1", "2", "3", "5", "6", "7"), a) +} + fun testIteratorRemove() { - val a = makeList12345() + val a = ArrayList(listOf("1", "2", "3", "4", "5")) val it = a.iterator() while (it.hasNext()) if (it.next()[0].toInt() % 2 == 0) it.remove() - assertEquals(makeList135(), a) + assertEquals(listOf("1", "3", "5"), a) } fun testIteratorAdd() { - val a = makeList12345() + val a = ArrayList(listOf("1", "2", "3", "4", "5")) val it = a.listIterator() - var i = 1 while (it.hasNext()) { val next = it.next() - if (i++ % 2 == 0) + if (next[0].toInt() % 2 == 0) it.add("-" + next) } - assertEquals("[1, 2, -2, 3, 4, -4, 5]", a.toString()) + assertEquals(listOf("1", "2", "-2", "3", "4", "-4", "5"), a) } - - fun main(args : Array) { testBasic() testIterator() @@ -288,13 +351,27 @@ fun main(args : Array) { testEquals() testHashCode() testToString() - testToString2() testSubList() testResize() testSubListContains() testSubListIndexOf() testSubListLastIndexOf() - testIteratorAdd() + /* + testSubListClear() + testSubListSubListClear() + testSubListAdd() + testSubListSubListAdd() + testSubListAddAll() + testSubListSubListAddAll() + testSubListRemoveAt() + testSubListSubListRemoveAt() + testSubListRemoveAll() + testSubListSubListRemoveAll() + testSubListSubListRemoveAll() + testSubListRetainAll() + testSubListSubListRetainAll() */ testIteratorRemove() + testIteratorAdd() + println("OK") } \ No newline at end of file diff --git a/backend.native/tests/runtime/collections/hash_map0.kt b/backend.native/tests/runtime/collections/hash_map0.kt new file mode 100644 index 00000000000..9a4fd26d10b --- /dev/null +++ b/backend.native/tests/runtime/collections/hash_map0.kt @@ -0,0 +1,157 @@ +fun assertTrue(cond: Boolean) { + if (!cond) + println("FAIL") +} + +fun assertFalse(cond: Boolean) { + if (cond) + println("FAIL") +} + +fun assertEquals(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()) + assertEquals(0, m.size) + + assertFalse(m.containsKey("1")) + assertFalse(m.containsValue("a")) + assertEquals(null, m.get("1")) + + assertEquals(null, m.put("1", "a")) + assertTrue(m.containsKey("1")) + assertTrue(m.containsValue("a")) + assertEquals("a", m.get("1")) + assertFalse(m.isEmpty()) + assertEquals(1, m.size) + + assertFalse(m.containsKey("2")) + assertFalse(m.containsValue("b")) + assertEquals(null, m.get("2")) + + assertEquals(null, m.put("2", "b")) + assertTrue(m.containsKey("1")) + assertTrue(m.containsValue("a")) + assertEquals("a", m.get("1")) + assertTrue(m.containsKey("2")) + assertTrue(m.containsValue("b")) + assertEquals("b", m.get("2")) + assertFalse(m.isEmpty()) + assertEquals(2, m.size) + + assertEquals("b", m.put("2", "bb")) + assertTrue(m.containsKey("1")) + assertTrue(m.containsValue("a")) + assertEquals("a", m.get("1")) + assertTrue(m.containsKey("2")) + assertTrue(m.containsValue("a")) + assertTrue(m.containsValue("bb")) + assertEquals("bb", m.get("2")) + assertFalse(m.isEmpty()) + assertEquals(2, m.size) + + assertEquals("a", m.remove("1")) + assertFalse(m.containsKey("1")) + assertFalse(m.containsValue("a")) + assertEquals(null, m.get("1")) + assertTrue(m.containsKey("2")) + assertTrue(m.containsValue("bb")) + assertEquals("bb", m.get("2")) + assertFalse(m.isEmpty()) + assertEquals(1, m.size) + + assertEquals("bb", m.remove("2")) + assertFalse(m.containsKey("1")) + assertFalse(m.containsValue("a")) + assertEquals(null, m.get("1")) + assertFalse(m.containsKey("2")) + assertFalse(m.containsValue("bb")) + assertEquals(null, m.get("2")) + assertTrue(m.isEmpty()) + assertEquals(0, m.size) +} + +fun testRehashAndCompact() { + val m = HashMap() + for (repeat in 1..10) { + val n = when (repeat) { + 1 -> 1000 + 2 -> 10000 + 3 -> 10 + else -> 100000 + } + for (i in 1..n) { + assertFalse(m.containsKey(i.toString())) + assertEquals(null, m.put(i.toString(), "val$i")) + assertTrue(m.containsKey(i.toString())) + assertEquals(i, m.size) + } + for (i in 1..n) { + assertTrue(m.containsKey(i.toString())) + } + for (i in 1..n) { + assertEquals("val$i", m.remove(i.toString())) + assertFalse(m.containsKey(i.toString())) + assertEquals(n - i, m.size) + } + assertTrue(m.isEmpty()) + } +} + +fun testClear() { + val m = HashMap() + for (repeat in 1..10) { + val n = when (repeat) { + 1 -> 1000 + 2 -> 10000 + 3 -> 10 + else -> 100000 + } + for (i in 1..n) { + assertFalse(m.containsKey(i.toString())) + assertEquals(null, m.put(i.toString(), "val$i")) + assertTrue(m.containsKey(i.toString())) + assertEquals(i, m.size) + } + for (i in 1..n) { + assertTrue(m.containsKey(i.toString())) + } + m.clear() + assertEquals(0, m.size) + for (i in 1..n) { + assertFalse(m.containsKey(i.toString())) + } + } +} +// 'to' not yet working. +/* +fun testEquals() { + val expected = mapOf("a" to "1", "b" to "2", "c" to "3") + val m = HashMap(expected) + assertTrue(m == expected) + assertTrue(m == mapOf("b" to "2", "c" to "3", "a" to "1")) // order does not matter + assertFalse(m == mapOf("a" to "1", "b" to "2", "c" to "4")) + assertFalse(m == mapOf("a" to "1", "b" to "2", "c" to "5")) + assertFalse(m == mapOf("a" to "1", "b" to "2")) + assertEquals(m.keys, expected.keys) + assertEquals(m.values, expected.values) + assertEquals(m.entries, expected.entries) +} */ + +fun main(args : Array) { + testBasic() + testRehashAndCompact() + testClear() + //testEquals() + println("OK") +} \ No newline at end of file diff --git a/backend.native/tests/runtime/collections/hash_set0.kt b/backend.native/tests/runtime/collections/hash_set0.kt new file mode 100644 index 00000000000..43d54e0b43b --- /dev/null +++ b/backend.native/tests/runtime/collections/hash_set0.kt @@ -0,0 +1,131 @@ +fun assertTrue(cond: Boolean) { + if (!cond) + println("FAIL") +} + +fun assertFalse(cond: Boolean) { + if (cond) + println("FAIL") +} + +fun assertEquals(value1: Any?, value2: Any?) { + if (value1 != value2) + println("FAIL") +} + +fun assertEquals(value1: Int, value2: Int) { + if (value1 != value2) + println("FAIL") +} + +fun testBasic() { + val a = HashSet() + 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) + assertTrue(a.contains("1")) + assertTrue(a.contains("2")) + assertTrue(a.contains("3")) + assertFalse(a.contains("4")) + + assertTrue(a.remove("1")) + assertEquals(2, a.size) + assertFalse(a.contains("1")) + assertTrue(a.contains("2")) + assertTrue(a.contains("3")) + assertFalse(a.contains("4")) + + assertTrue(a.add("4")) + assertEquals(3, a.size) + assertFalse(a.contains("1")) + assertTrue(a.contains("2")) + assertTrue(a.contains("3")) + assertTrue(a.contains("4")) + + assertFalse(a.add("4")) + assertEquals(3, a.size) + assertFalse(a.contains("1")) + assertTrue(a.contains("2")) + assertTrue(a.contains("3")) + assertTrue(a.contains("4")) + + a.clear() + assertTrue(a.isEmpty()) + assertEquals(0, a.size) + assertFalse(a.contains("1")) + assertFalse(a.contains("2")) + assertFalse(a.contains("3")) + assertFalse(a.contains("4")) +} + +fun testIterator() { + val s = HashSet(listOf("1", "2", "3")) + val it = s.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 testEquals() { + val s = HashSet(listOf("1", "2", "3")) + assertTrue(s == setOf("1", "2", "3")) + assertTrue(s == setOf("2", "3", "1")) // order does not matter + assertFalse(s == setOf("1", "2", "4")) + assertFalse(s == setOf("1", "2")) +} + +fun testHashCode() { + val s = HashSet(listOf("1", "2", "3")) + assertTrue(s.hashCode() == setOf("1", "2", "3").hashCode()) +} + +fun testToString() { + val s = HashSet(listOf("1", "2", "3")) + assertTrue(s.toString() == setOf("1", "2", "3").toString()) +} + +fun testContainsAll() { + val s = HashSet(listOf("1", "2", "3", "4", "5")) + assertFalse(s.containsAll(listOf("6", "7", "8"))) + assertFalse(s.containsAll(listOf("5", "6", "7"))) + assertFalse(s.containsAll(listOf("4", "5", "6"))) + assertTrue(s.containsAll(listOf("3", "4", "5"))) + assertTrue(s.containsAll(listOf("2", "3", "4"))) +} + +fun testRemoveAll() { + val s = HashSet(listOf("1", "2", "3", "4", "5")) + assertFalse(s.removeAll(listOf("6", "7", "8"))) + assertEquals(setOf("1", "2", "3", "4", "5"), s) + assertTrue(s.removeAll(listOf("5", "3", "1"))) + assertEquals(setOf("2", "4"), s) +} + +fun testRetainAll() { + val s = HashSet(listOf("1", "2", "3", "4", "5")) + assertFalse(s.retainAll(listOf("1", "2", "3", "4", "5"))) + assertEquals(setOf("1", "2", "3", "4", "5"), s) + assertTrue(s.retainAll(listOf("5", "3", "1"))) + assertEquals(setOf("1", "3", "5"), s) +} + +fun main(args : Array) { + testBasic() + testIterator() + testEquals() + testHashCode() + testToString() + testContainsAll() + testRemoveAll() + testRetainAll() + 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 9d72b4d958e..4c5188f314b 100644 --- a/runtime/src/main/cpp/Arrays.cpp +++ b/runtime/src/main/cpp/Arrays.cpp @@ -12,7 +12,7 @@ extern "C" { // TODO: those must be compiler intrinsics afterwards. // Array.kt -KRef Kotlin_Array_get(const ObjHeader* thiz, KInt index) { +KRef Kotlin_Array_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -20,7 +20,7 @@ KRef Kotlin_Array_get(const ObjHeader* thiz, KInt index) { return *ArrayAddressOfElementAt(array, index); } -void Kotlin_Array_set(ObjHeader* thiz, KInt index, KConstRef value) { +void Kotlin_Array_set(KRef thiz, KInt index, KConstRef value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -28,7 +28,7 @@ void Kotlin_Array_set(ObjHeader* thiz, KInt index, KConstRef value) { *ArrayAddressOfElementAt(array, index) = value; } -ObjHeader* Kotlin_Array_clone(const ObjHeader* thiz) { +KRef Kotlin_Array_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( array->type_info(), array->count_).GetPlace(); @@ -39,15 +39,14 @@ ObjHeader* Kotlin_Array_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_Array_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_Array_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -void Kotlin_Array_fillImpl(ObjHeader* thiz, KInt fromIndex, - KInt toIndex, ObjHeader* value) { +void Kotlin_Array_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KRef value) { ArrayHeader* array = static_cast(thiz); - if (fromIndex < 0 || toIndex < fromIndex || toIndex >= array->count_) { + if (fromIndex < 0 || toIndex < fromIndex || toIndex > array->count_) { ThrowArrayIndexOutOfBoundsException(); } // TODO: refcounting! @@ -56,8 +55,8 @@ void Kotlin_Array_fillImpl(ObjHeader* thiz, KInt fromIndex, } } -void Kotlin_Array_copyImpl(const ObjHeader* thiz, KInt fromIndex, - ObjHeader* destination, KInt toIndex, KInt count) { +void Kotlin_Array_copyImpl(KConstRef thiz, KInt fromIndex, + KRef destination, KInt toIndex, KInt count) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* destinationArray = static_cast(destination); if (fromIndex < 0 || fromIndex + count > array->count_ || @@ -70,7 +69,7 @@ void Kotlin_Array_copyImpl(const ObjHeader* thiz, KInt fromIndex, } // Arrays.kt -KByte Kotlin_ByteArray_get(const ObjHeader* thiz, KInt index) { +KByte Kotlin_ByteArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -78,7 +77,7 @@ KByte Kotlin_ByteArray_get(const ObjHeader* thiz, KInt index) { return *ByteArrayAddressOfElementAt(array, index); } -void Kotlin_ByteArray_set(ObjHeader* thiz, KInt index, KByte value) { +void Kotlin_ByteArray_set(KRef thiz, KInt index, KByte value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -86,7 +85,7 @@ void Kotlin_ByteArray_set(ObjHeader* thiz, KInt index, KByte value) { *ByteArrayAddressOfElementAt(array, index) = value; } -ObjHeader* Kotlin_ByteArray_clone(const ObjHeader* thiz) { +KRef Kotlin_ByteArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theByteArrayTypeInfo, array->count_).GetPlace(); @@ -97,12 +96,12 @@ ObjHeader* Kotlin_ByteArray_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_ByteArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_ByteArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -KChar Kotlin_CharArray_get(const ObjHeader* thiz, KInt index) { +KChar Kotlin_CharArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -110,7 +109,7 @@ KChar Kotlin_CharArray_get(const ObjHeader* thiz, KInt index) { return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_CharArray_set(ObjHeader* thiz, KInt index, KChar value) { +void Kotlin_CharArray_set(KRef thiz, KInt index, KChar value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -118,7 +117,7 @@ void Kotlin_CharArray_set(ObjHeader* thiz, KInt index, KChar value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ObjHeader* Kotlin_CharArray_clone(const ObjHeader* thiz) { +KRef Kotlin_CharArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theCharArrayTypeInfo, array->count_).GetPlace(); @@ -129,7 +128,7 @@ ObjHeader* Kotlin_CharArray_clone(const ObjHeader* thiz) { return result; } -ObjHeader* Kotlin_CharArray_copyOf(const ObjHeader* thiz, KInt newSize) { +KRef Kotlin_CharArray_copyOf(KConstRef thiz, KInt newSize) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theCharArrayTypeInfo, newSize).GetPlace(); @@ -141,12 +140,12 @@ ObjHeader* Kotlin_CharArray_copyOf(const ObjHeader* thiz, KInt newSize) { return result; } -KInt Kotlin_CharArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_CharArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -KShort Kotlin_ShortArray_get(const ObjHeader* thiz, KInt index) { +KShort Kotlin_ShortArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -154,7 +153,7 @@ KShort Kotlin_ShortArray_get(const ObjHeader* thiz, KInt index) { return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_ShortArray_set(ObjHeader* thiz, KInt index, KShort value) { +void Kotlin_ShortArray_set(KRef thiz, KInt index, KShort value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -162,7 +161,8 @@ void Kotlin_ShortArray_set(ObjHeader* thiz, KInt index, KShort value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ArrayHeader* Kotlin_ShortArray_clone(const ArrayHeader* array) { +KRef Kotlin_ShortArray_clone(KConstRef thiz) { + const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theShortArrayTypeInfo, array->count_).GetPlace(); memcpy( @@ -172,11 +172,12 @@ ArrayHeader* Kotlin_ShortArray_clone(const ArrayHeader* array) { return result; } -KInt Kotlin_ShortArray_getArrayLength(const ArrayHeader* array) { +KInt Kotlin_ShortArray_getArrayLength(KConstRef thiz) { + const ArrayHeader* array = static_cast(thiz); return array->count_; } -KInt Kotlin_IntArray_get(const ObjHeader* thiz, KInt index) { +KInt Kotlin_IntArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -184,7 +185,7 @@ KInt Kotlin_IntArray_get(const ObjHeader* thiz, KInt index) { return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_IntArray_set(ObjHeader* thiz, KInt index, KInt value) { +void Kotlin_IntArray_set(KRef thiz, KInt index, KInt value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -192,7 +193,7 @@ void Kotlin_IntArray_set(ObjHeader* thiz, KInt index, KInt value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ObjHeader* Kotlin_IntArray_clone(const ObjHeader* thiz) { +ObjHeader* Kotlin_IntArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theIntArrayTypeInfo, array->count_).GetPlace(); @@ -203,20 +204,44 @@ ObjHeader* Kotlin_IntArray_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_IntArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_IntArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -KLong Kotlin_LongArray_get(const ObjHeader* thiz, KInt index) { +void Kotlin_IntArray_fillImpl(KRef thiz, KInt fromIndex, KInt toIndex, KInt value) { + ArrayHeader* array = static_cast(thiz); + if (fromIndex < 0 || toIndex < fromIndex || toIndex >= array->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + for (KInt index = fromIndex; index < toIndex; ++index) { + *PrimitiveArrayAddressOfElementAt(array, index) = value; + } +} + +void Kotlin_IntArray_copyImpl(KConstRef thiz, KInt fromIndex, + KRef destination, KInt toIndex, KInt count) { + const ArrayHeader* array = static_cast(thiz); + ArrayHeader* destinationArray = static_cast(destination); + if (fromIndex < 0 || fromIndex + count > array->count_ || + toIndex < 0 || toIndex + count > destinationArray->count_) { + ThrowArrayIndexOutOfBoundsException(); + } + memmove(PrimitiveArrayAddressOfElementAt(destinationArray, toIndex), + PrimitiveArrayAddressOfElementAt(array, fromIndex), + count * sizeof(KInt)); +} + +KLong Kotlin_LongArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { - ThrowArrayIndexOutOfBoundsException(); + + ThrowArrayIndexOutOfBoundsException(); } return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_LongArray_set(ObjHeader* thiz, KInt index, KLong value) { +void Kotlin_LongArray_set(KRef thiz, KInt index, KLong value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -224,7 +249,7 @@ void Kotlin_LongArray_set(ObjHeader* thiz, KInt index, KLong value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ArrayHeader* Kotlin_LongArray_clone(const ObjHeader* thiz) { +KRef Kotlin_LongArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theLongArrayTypeInfo, array->count_).GetPlace(); @@ -235,12 +260,12 @@ ArrayHeader* Kotlin_LongArray_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_LongArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_LongArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -KFloat Kotlin_FloatArray_get(const ObjHeader* thiz, KInt index) { +KFloat Kotlin_FloatArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -248,7 +273,7 @@ KFloat Kotlin_FloatArray_get(const ObjHeader* thiz, KInt index) { return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_FloatArray_set(ObjHeader* thiz, KInt index, KFloat value) { +void Kotlin_FloatArray_set(KRef thiz, KInt index, KFloat value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -256,7 +281,7 @@ void Kotlin_FloatArray_set(ObjHeader* thiz, KInt index, KFloat value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ArrayHeader* Kotlin_FloatArray_clone(const ObjHeader* thiz) { +ArrayHeader* Kotlin_FloatArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theFloatArrayTypeInfo, array->count_).GetPlace(); @@ -267,12 +292,12 @@ ArrayHeader* Kotlin_FloatArray_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_FloatArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_FloatArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -KDouble Kotlin_DoubleArray_get(const ObjHeader* thiz, KInt index) { +KDouble Kotlin_DoubleArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -280,7 +305,7 @@ KDouble Kotlin_DoubleArray_get(const ObjHeader* thiz, KInt index) { return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_DoubleArray_set(ObjHeader* thiz, KInt index, KDouble value) { +void Kotlin_DoubleArray_set(KRef thiz, KInt index, KDouble value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -288,7 +313,7 @@ void Kotlin_DoubleArray_set(ObjHeader* thiz, KInt index, KDouble value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ObjHeader* Kotlin_DoubleArray_clone(const ObjHeader* thiz) { +KRef Kotlin_DoubleArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theDoubleArrayTypeInfo, array->count_).GetPlace(); @@ -299,12 +324,12 @@ ObjHeader* Kotlin_DoubleArray_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_DoubleArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_DoubleArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } -KBoolean Kotlin_BooleanArray_get(const ObjHeader* thiz, KInt index) { +KBoolean Kotlin_BooleanArray_get(KConstRef thiz, KInt index) { const ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -312,7 +337,7 @@ KBoolean Kotlin_BooleanArray_get(const ObjHeader* thiz, KInt index) { return *PrimitiveArrayAddressOfElementAt(array, index); } -void Kotlin_BooleanArray_set(ObjHeader* thiz, KInt index, KBoolean value) { +void Kotlin_BooleanArray_set(KRef thiz, KInt index, KBoolean value) { ArrayHeader* array = static_cast(thiz); if (static_cast(index) >= array->count_) { ThrowArrayIndexOutOfBoundsException(); @@ -320,7 +345,7 @@ void Kotlin_BooleanArray_set(ObjHeader* thiz, KInt index, KBoolean value) { *PrimitiveArrayAddressOfElementAt(array, index) = value; } -ObjHeader* Kotlin_BooleanArray_clone(const ObjHeader* thiz) { +KRef Kotlin_BooleanArray_clone(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); ArrayHeader* result = ArrayContainer( theBooleanArrayTypeInfo, array->count_).GetPlace(); @@ -331,7 +356,7 @@ ObjHeader* Kotlin_BooleanArray_clone(const ObjHeader* thiz) { return result; } -KInt Kotlin_BooleanArray_getArrayLength(const ObjHeader* thiz) { +KInt Kotlin_BooleanArray_getArrayLength(KConstRef thiz) { const ArrayHeader* array = static_cast(thiz); return array->count_; } diff --git a/runtime/src/main/cpp/Operator.cpp b/runtime/src/main/cpp/Operator.cpp index 95c89e2c1b7..4af2ac65b82 100644 --- a/runtime/src/main/cpp/Operator.cpp +++ b/runtime/src/main/cpp/Operator.cpp @@ -191,6 +191,14 @@ KInt Kotlin_Int_dec (KInt a ) { return --a; } KInt Kotlin_Int_unaryPlus (KInt a ) { return +a; } KInt Kotlin_Int_unaryMinus (KInt a ) { return -a; } +KInt Kotlin_Int_xor_Int (KInt a, KInt b) { return a ^ b; } +KInt Kotlin_Int_and_Int (KInt a, KInt b) { return a & b; } +KInt Kotlin_Int_shl_Int (KInt a, KInt b) { return a << b; } +KInt Kotlin_Int_shr_Int (KInt a, KInt b) { return a >> b; } +KInt Kotlin_Int_ushr_Int (KInt a, KInt b) { + return static_cast(a) >> b; +} + KByte Kotlin_Int_toByte (KInt a ) { return a; } KChar Kotlin_Int_toChar (KInt a ) { return a; } KShort Kotlin_Int_toShort (KInt a ) { return a; } diff --git a/runtime/src/main/kotlin/kotlin/Annotations.kt b/runtime/src/main/kotlin/kotlin/Annotations.kt index 8d85ea32172..f0419f3ee78 100644 --- a/runtime/src/main/kotlin/kotlin/Annotations.kt +++ b/runtime/src/main/kotlin/kotlin/Annotations.kt @@ -1,13 +1,12 @@ package kotlin -// TODO: shouldn't these annotation be located in 'kotlin_native.internal' package? - /** * Suppresses the given compilation warnings in the annotated element. * @property names names of the compiler diagnostics to suppress. */ //@Target(CLASS, ANNOTATION_CLASS, PROPERTY, FIELD, LOCAL_VARIABLE, VALUE_PARAMETER, // CONSTRUCTOR, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, TYPE, EXPRESSION, FILE, TYPEALIAS) +@Target(AnnotationTarget.TYPE, AnnotationTarget.VALUE_PARAMETER, AnnotationTarget.EXPRESSION) //@Retention(SOURCE) public annotation class Suppress(vararg val names: String) diff --git a/runtime/src/main/kotlin/kotlin/Any.kt b/runtime/src/main/kotlin/kotlin/Any.kt index 7ef09fd333e..445f2f1e3f8 100644 --- a/runtime/src/main/kotlin/kotlin/Any.kt +++ b/runtime/src/main/kotlin/kotlin/Any.kt @@ -35,3 +35,5 @@ public open class Any { @SymbolName("Kotlin_Any_toString") external public open fun toString(): String } + +public fun Any?.hashCode() = if (this != null) this.hashCode() else 0 \ 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 c67bb567576..0cb92d99798 100644 --- a/runtime/src/main/kotlin/kotlin/Array.kt +++ b/runtime/src/main/kotlin/kotlin/Array.kt @@ -23,6 +23,7 @@ public final class Array : Cloneable { return IteratorImpl(this) } + // Konan-specific. @SymbolName("Kotlin_Array_getArrayLength") external private fun getArrayLength(): Int } diff --git a/runtime/src/main/kotlin/kotlin/Primitives.kt b/runtime/src/main/kotlin/kotlin/Primitives.kt index 55fd5f8582c..5b80b103ccd 100644 --- a/runtime/src/main/kotlin/kotlin/Primitives.kt +++ b/runtime/src/main/kotlin/kotlin/Primitives.kt @@ -647,6 +647,32 @@ public final class Int : Number(), Comparable { public override fun hashCode(): Int { return this } + + fun highestOneBit() : Int { + var index = 31 + + while (index >= 0) { + var mask = (1 shl index) + if ((mask and this) != 0) { + return mask + } + index-- + } + return 0 + } + + fun numberOfLeadingZeros() : Int { + var index = 31 + + while (index >= 0) { + var mask = (1 shl index) + if ((mask and this) != 0) { + return 31 - index + } + index-- + } + return 0 + } } /** diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt index 5e99eed543a..f97e037b53b 100644 --- a/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayList.kt @@ -9,7 +9,8 @@ class ArrayList private constructor( constructor() : this(10) - constructor(initialCapacity: Int) : this(arrayOfLateInitElements(initialCapacity), 0, 0, null) + constructor(initialCapacity: Int) : this( + arrayOfUninitializedElements(initialCapacity), 0, 0, null) constructor(c: Collection) : this(c.size) { addAll(c) @@ -135,7 +136,7 @@ class ArrayList private constructor( fun trimToSize() { if (backing != null) throw IllegalStateException() // just in case somebody casts subList to ArrayList if (length < array.size) - array = array.copyOfLateInitElements(length) + array = array.copyOfUninitializedElements(length) } fun ensureCapacity(capacity: Int) { @@ -144,7 +145,7 @@ class ArrayList private constructor( var newSize = array.size * 3 / 2 if (capacity > newSize) newSize = capacity - array = array.copyOfLateInitElements(newSize) + array = array.copyOfUninitializedElements(newSize) } } diff --git a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt index 6aa37a367c7..6a0e8f25076 100644 --- a/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt +++ b/runtime/src/main/kotlin/kotlin/collections/ArrayUtil.kt @@ -5,8 +5,7 @@ package kotlin.collections * 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 empty array. +fun arrayOfUninitializedElements(size: Int): Array { // return (if (size == 0) emptyArray else Array(size)) as Array return Array(size) } @@ -16,12 +15,18 @@ fun arrayOfLateInitElements(size: Int): Array { * 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 { +fun Array.copyOfUninitializedElements(newSize: Int): Array { val result = Array(newSize) this.copyRangeTo(result, 0, if (newSize > this.size) this.size else newSize, 0) return result } +fun IntArray.copyOfUninitializedElements(newSize: Int): IntArray { + val result = IntArray(newSize) + 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. @@ -35,6 +40,9 @@ fun Array.resetAt(index: Int) { @SymbolName("Kotlin_Array_fillImpl") external private fun fillImpl(array: Array, fromIndex: Int, toIndex: Int, value: Any?) +@SymbolName("Kotlin_IntArray_fillImpl") +external private fun fillImpl(array: IntArray, fromIndex: Int, toIndex: Int, value: Int) + /** * Resets a range of array elements at a specified [fromIndex] (inclusive) to [toIndex] (exclusive) range of indices * to some implementation-specific _uninitialized_ value. @@ -46,10 +54,18 @@ fun Array.resetRange(fromIndex: Int, toIndex: Int) { fillImpl(this as Array, fromIndex, toIndex, null) } +fun IntArray.fill(fromIndex: Int, toIndex: Int, value: Int) { + fillImpl(this, fromIndex, toIndex, value) +} + @SymbolName("Kotlin_Array_copyImpl") external private fun copyImpl(array: Array, fromIndex: Int, destination: Array, toIndex: Int, count: Int) +@SymbolName("Kotlin_IntArray_copyImpl") +external private fun copyImpl(array: IntArray, fromIndex: Int, + destination: IntArray, 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]. @@ -58,10 +74,29 @@ fun Array.copyRangeTo(destination: Array, fromIndex: Int, toIndex: Int copyImpl(this as Array, fromIndex, destination as Array, destinationIndex, toIndex - fromIndex) } +fun IntArray.copyRangeTo(destination: IntArray, fromIndex: Int, toIndex: Int, destinationIndex: Int = 0) { + copyImpl(this, fromIndex, destination, 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) +} + +internal fun Collection.collectionToString(): String { + val sb = StringBuilder(2 + size * 3) + sb.append("[") + var i = 0 + val it = iterator() + while (it.hasNext()) { + if (i > 0) sb.append(", ") + val next = it.next() + if (next == this) sb.append("(this Collection)") else sb.append(next) + i++ + } + sb.append("]") + return sb.toString() } \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/collections/Collections.kt b/runtime/src/main/kotlin/kotlin/collections/Collections.kt index 2dc74e3ea45..7c2e96977b3 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Collections.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Collections.kt @@ -32,6 +32,14 @@ fun Array.asList(): List { return result } +fun Array.toSet(): Set { + val result = HashSet(this.size) + for (e in this) { + result.add(e) + } + return result +} + public fun arrayListOf(vararg args: T): MutableList { val result = ArrayList(args.size) for (arg in args) { @@ -40,8 +48,24 @@ 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(vararg args: T): List = args.asList() \ No newline at end of file +public fun listOf(): List = ArrayList(0) + +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 diff --git a/runtime/src/main/kotlin/kotlin/collections/HashMap.kt b/runtime/src/main/kotlin/kotlin/collections/HashMap.kt new file mode 100644 index 00000000000..cc80e75f3d4 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/HashMap.kt @@ -0,0 +1,670 @@ +package kotlin.collections + + +// TODO: remove +private val MAGIC: Long = 2654435769 // golden ratio +private val INITIAL_CAPACITY = 8 +private val INITIAL_MAX_PROBE_DISTANCE = 2 +private val TOMBSTONE = -1 + +private fun computeHashSize(capacity: Int): Int = (capacity.coerceAtLeast(1) * 3).highestOneBit() + +private fun computeShift(hashSize: Int): Int = hashSize.numberOfLeadingZeros() + 1 + +class HashMap private constructor( + private var keysArray: Array, + private var valuesArray: Array?, // allocated only when actually used, always null in pure HashSet + private var presenceArray: IntArray, + private var hashArray: IntArray, + private var maxProbeDistance: Int, + private var length: Int +) : MutableMap { + private var hashShift: Int = computeShift(hashSize) + + override var size: Int = 0 + private set + + private var keysView: HashSet? = null + private var valuesView: HashMapValues? = null + private var entriesView: HashMapEntrySet? = null + + // ---------------------------- functions ---------------------------- + + constructor() : this(INITIAL_CAPACITY) + + constructor(capacity: Int) : this( + arrayOfUninitializedElements(capacity), + null, + IntArray(capacity), + IntArray(computeHashSize(capacity)), + INITIAL_MAX_PROBE_DISTANCE, + 0) + + constructor(m: Map) : this(m.size) { + putAll(m) + } + + override fun isEmpty(): Boolean = size == 0 + override fun containsKey(key: K): Boolean = findKey(key) >= 0 + override fun containsValue(value: V): Boolean = findValue(value) >= 0 + + override fun get(key: K): V? { + val index = findKey(key) + if (index < 0) return null + return valuesArray!![index] + } + + override fun put(key: K, value: V): V? { + val index = addKey(key) + val valuesArray = allocateValuesArray() + if (index < 0) { + val oldValue = valuesArray[-index - 1] + valuesArray[-index - 1] = value + return oldValue + } else { + valuesArray[index] = value + return null + } + } + + override fun putAll(from: Map) { + putAllEntries(from.entries) + } + + override fun remove(key: K): V? { + val index = removeKey(key) + if (index < 0) return null + val valuesArray = valuesArray!! + val oldValue = valuesArray[index] + valuesArray.resetAt(index) + return oldValue + } + + override fun clear() { + // O(length) implementation for hashArray cleanup + for (i in 0..length - 1) { + val hash = presenceArray[i] + if (hash >= 0) { + hashArray[hash] = 0 + presenceArray[i] = TOMBSTONE + } + } + keysArray.resetRange(0, length) + valuesArray?.resetRange(0, length) + size = 0 + length = 0 + } + + override val keys: MutableSet get() { + val cur = keysView + return if (cur == null) { + val new = HashSet(this) + keysView = new + new + } else cur + } + + override val values: MutableCollection get() { + val cur = valuesView + return if (cur == null) { + val new = HashMapValues(this) + valuesView = new + new + } else cur + } + + override val entries: MutableSet> get() { + val cur = entriesView + return if (cur == null) { + val new = HashMapEntrySet(this) + entriesView = new + return new + } else cur + } + + override fun equals(other: Any?): Boolean { + return other === this || + (other is Map<*, *>) && + contentEquals(other) + } + + override fun hashCode(): Int { + var result = 0 + val it = entriesIterator() + while (it.hasNext()) { + result += it.nextHashCode() + } + return result + } + + override fun toString(): String { + val sb = StringBuilder(2 + size * 3) + sb.append("{") + var i = 0 + val it = entriesIterator() + while (it.hasNext()) { + if (i > 0) sb.append(", ") + it.nextAppendString(sb) + i++ + } + sb.append("}") + return sb.toString() + } + + // ---------------------------- private ---------------------------- + + private val capacity: Int get() = keysArray.size + private val hashSize: Int get() = hashArray.size + + private fun ensureExtraCapacity(n: Int) { + ensureCapacity(length + n) + } + + private fun ensureCapacity(capacity: Int) { + if (capacity > this.capacity) { + var newSize = this.capacity * 3 / 2 + if (capacity > newSize) newSize = capacity + keysArray = keysArray.copyOfUninitializedElements(newSize) + valuesArray = valuesArray?.copyOfUninitializedElements(newSize) + presenceArray = presenceArray.copyOfUninitializedElements(newSize) + val newHashSize = computeHashSize(newSize) + if (newHashSize > hashSize) rehash(newHashSize) + } else if (length + capacity - size > this.capacity) { + rehash(hashSize) + } + } + + private fun allocateValuesArray(): Array { + val curValuesArray = valuesArray + if (curValuesArray != null) return curValuesArray + val newValuesArray = arrayOfUninitializedElements(capacity) + valuesArray = newValuesArray + return newValuesArray + } + + private fun hash(key: K) = (key.hashCode() * MAGIC.toInt()) ushr hashShift + + private fun compact() { + var i = 0 + var j = 0 + val valuesArray = valuesArray + while (i < length) { + if (presenceArray[i] >= 0) { + keysArray[j] = keysArray[i] + if (valuesArray != null) valuesArray[j] = valuesArray[i] + j++ + } + i++ + } + keysArray.resetRange(j, length) + valuesArray?.resetRange(j, length) + length = j + //check(length == size) { "Internal invariant violated during compact: length=$length != size=$size" } + } + + private fun rehash(newHashSize: Int) { + if (length > size) compact() + if (newHashSize != hashSize) { + hashArray = IntArray(newHashSize) + hashShift = computeShift(newHashSize) + } else { + hashArray.fill(0, 0, hashSize) + } + var i = 0 + while (i < length) { + if (!putRehash(i++)) { + throw IllegalStateException("This cannot happen with fixed magic multiplier and grow-only hash array. " + + "Have object hashCodes changed?") + } + } + } + + private fun putRehash(i: Int): Boolean { + var hash = hash(keysArray[i]) + var probesLeft = maxProbeDistance + while (true) { + val index = hashArray[hash] + if (index == 0) { + hashArray[hash] = i + 1 + presenceArray[i] = hash + return true + } + if (--probesLeft < 0) return false + if (hash-- == 0) hash = hashSize - 1 + } + } + + private fun findKey(key: K): Int { + var hash = hash(key) + var probesLeft = maxProbeDistance + while (true) { + val index = hashArray[hash] + if (index == 0) return TOMBSTONE + if (index > 0 && keysArray[index - 1] == key) return index - 1 + if (--probesLeft < 0) return TOMBSTONE + if (hash-- == 0) hash = hashSize - 1 + } + } + + private fun findValue(value: V): Int { + var i = length + while (--i >= 0) { + if (presenceArray[i] >= 0 && valuesArray!![i] == value) + return i + } + return TOMBSTONE + } + + internal fun addKey(key: K): Int { + retry@ while (true) { + var hash = hash(key) + // put is allowed to grow maxProbeDistance with some limits (resize hash on reaching limits) + val tentativeMaxProbeDistance = (maxProbeDistance * 2).coerceAtMost(hashSize / 2) + var probeDistance = 0 + while (true) { + val index = hashArray[hash] + if (index <= 0) { // claim or reuse hash slot + if (length >= capacity) { + ensureExtraCapacity(1) + continue@retry + } + val putIndex = length++ + keysArray[putIndex] = key + presenceArray[putIndex] = hash + hashArray[hash] = putIndex + 1 + size++ + if (probeDistance > maxProbeDistance) maxProbeDistance = probeDistance + return putIndex + } + if (keysArray[index - 1] == key) { + return -index + } + if (++probeDistance > tentativeMaxProbeDistance) { + rehash(hashSize * 2) // cannot find room even with extra "tentativeMaxProbeDistance" -- grow hash + continue@retry + } + if (hash-- == 0) hash = hashSize - 1 + } + } + } + + internal fun removeKey(key: K): Int { + val index = findKey(key) + if (index < 0) return TOMBSTONE + removeKeyAt(index) + return index + } + + private fun removeKeyAt(index: Int) { + keysArray.resetAt(index) + removeHashAt(presenceArray[index]) + presenceArray[index] = TOMBSTONE + size-- + } + + private fun removeHashAt(removedHash: Int) { + var hash = removedHash + var hole = removedHash // will try to patch the hole in hash array + var probeDistance = 0 + var patchAttemptsLeft = (maxProbeDistance * 2).coerceAtMost(hashSize / 2) // don't spend too much effort + while (true) { + if (hash-- == 0) hash = hashSize - 1 + if (++probeDistance > maxProbeDistance) { + // too far away -- can release the hole, bad case will not happen + hashArray[hole] = 0 + return + } + val index = hashArray[hash] + if (index == 0) { + // end of chain -- can release the hole, bad case will not happen + hashArray[hole] = 0 + return + } + if (index < 0) { + // TOMBSTONE FOUND + // - <--- [ TS ] ------ [hole] ---> + + // \------------/ + // probeDistance + // move tombstone into the hole + hashArray[hole] = TOMBSTONE + hole = hash + probeDistance = 0 + } else { + val otherHash = hash(keysArray[index - 1]) + // Bad case: + // - <--- [hash] ------ [hole] ------ [otherHash] ---> + + // \------------/ + // probeDistance + if ((otherHash - hash) and (hashSize - 1) >= probeDistance) { + // move otherHash into the hole, move the hole + hashArray[hole] = index + presenceArray[index - 1] = hole + hole = hash + probeDistance = 0 + } + } + // check how long we're patching holes + if (--patchAttemptsLeft < 0) { + // just place tombstone into the hole + hashArray[hole] = TOMBSTONE + return + } + } + } + + internal fun containsEntry(entry: Map.Entry): Boolean { + val index = findKey(entry.key) + if (index < 0) return false + return valuesArray!![index] == entry.value + } + + private fun contentEquals(other: Map<*, *>): Boolean = size == other.size && containsAllEntries(other.entries) + + internal fun containsAllEntries(m: Collection>): Boolean { + val it = m.iterator() + while (it.hasNext()) { + val entry = it.next() + try { + @Suppress("UNCHECKED_CAST") // todo: get rid of unchecked cast here somehow + if (!containsEntry(entry as Map.Entry)) + return false + } catch(e: ClassCastException) { + return false + } + } + return true + } + + internal fun putEntry(entry: Map.Entry): Boolean { + val index = addKey(entry.key) + val valuesArray = allocateValuesArray() + if (index >= 0) { + valuesArray[index] = entry.value + return true + } + val oldValue = valuesArray[-index - 1] + if (entry.value != oldValue) { + valuesArray[-index - 1] = entry.value + return true + } + return false + } + + internal fun putAllEntries(from: Collection>): Boolean { + if (from.isEmpty()) return false + ensureExtraCapacity(from.size) + val it = from.iterator() + var updated = false + while (it.hasNext()) { + if (putEntry(it.next())) + updated = true + } + return updated + } + + internal fun removeEntry(entry: Map.Entry): Boolean { + val index = findKey(entry.key) + if (index < 0) return false + if (valuesArray!![index] != entry.value) return false + removeKeyAt(index) + return true + } + + internal fun removeAllEntries(elements: Collection>): Boolean { + if (elements.isEmpty()) return false + val it = elements.iterator() + var updated = false + while (it.hasNext()) { + if (removeEntry(it.next())) + updated = true + } + return updated + } + + internal fun retainAllEntries(elements: Collection>): Boolean { + val it = entriesIterator() + var updated = false + while (it.hasNext()) { + if (!elements.contains(it.next())) { + it.remove() + updated = true + } + } + return updated + } + + internal fun containsAllValues(elements: Collection): Boolean { + val it = elements.iterator() + while (it.hasNext()) { + if (!containsValue(it.next())) + return false + } + return true + } + + internal fun removeValue(element: V): Boolean { + val index = findValue(element) + if (index < 0) return false + removeKeyAt(index) + return true + } + + internal fun removeAllValues(elements: Collection): Boolean { + val it = elements.iterator() + var updated = false + while (it.hasNext()) { + if (removeValue(it.next())) + updated = true + } + return updated + } + + internal fun retainAllValues(elements: Collection): Boolean { + val it = valuesIterator() + var updated = false + while (it.hasNext()) { + if (!elements.contains(it.next())) { + it.remove() + updated = true + } + } + return updated + } + + internal fun keysIterator() = KeysItr(this) + internal fun valuesIterator() = ValuesItr(this) + internal fun entriesIterator() = EntriesItr(this) + + // TODO: fix! + /* private companion object { + const val MAGIC = 2654435769L.toInt() // golden ratio + const val INITIAL_CAPACITY = 8 + const val INITIAL_MAX_PROBE_DISTANCE = 2 + const val TOMBSTONE = -1 + + fun computeHashSize(capacity: Int): Int = (capacity.coerceAtLeast(1) * 3).highestOneBit() + + fun computeShift(hashSize: Int): Int = hashSize.numberOfLeadingZeros() + 1 + } */ + + internal open class Itr( + internal val map: HashMap + ) { + internal var index = 0 + internal var lastIndex: Int = -1 + + init { + initNext() + } + + internal fun initNext() { + while (index < map.length && map.presenceArray[index] < 0) + index++ + } + + fun hasNext(): Boolean = index < map.length + + fun remove() { + map.removeKeyAt(lastIndex) + lastIndex = -1 + } + } + + internal class KeysItr(map: HashMap) : Itr(map), MutableIterator { + override fun next(): K { + if (index >= map.length) throw IndexOutOfBoundsException() + lastIndex = index++ + val result = map.keysArray[lastIndex] + initNext() + return result + } + + } + + internal class ValuesItr(map: HashMap) : Itr(map), MutableIterator { + override fun next(): V { + if (index >= map.length) throw IndexOutOfBoundsException() + lastIndex = index++ + val result = map.valuesArray!![lastIndex] + initNext() + return result + } + } + + internal class EntriesItr(map: HashMap) : Itr(map), + MutableIterator> { + override fun next(): EntryRef { + if (index >= map.length) throw IndexOutOfBoundsException() + lastIndex = index++ + val result = EntryRef(map, lastIndex) + initNext() + return result + } + + internal fun nextHashCode(): Int { + if (index >= map.length) throw IndexOutOfBoundsException() + lastIndex = index++ + val result = map.keysArray[lastIndex].hashCode() xor map.valuesArray!![lastIndex].hashCode() + initNext() + return result + } + + fun nextAppendString(sb: StringBuilder) { + if (index >= map.length) throw IndexOutOfBoundsException() + lastIndex = index++ + val key = map.keysArray[lastIndex] + if (key == map) sb.append("(this Map)") else sb.append(key) + sb.append('=') + val value = map.valuesArray!![lastIndex] + if (value == map) sb.append("(this Map)") else sb.append(value) + initNext() + } + } + + internal class EntryRef( + private val map: HashMap, + private val index: Int + ) : MutableMap.MutableEntry { + override val key: K + get() = map.keysArray[index] + + override val value: V + get() = map.valuesArray!![index] + + override fun setValue(newValue: V): V { + val valuesArray = map.allocateValuesArray() + val oldValue = valuesArray[index] + valuesArray[index] = newValue + return oldValue + } + + override fun equals(other: Any?): Boolean = + other is Map.Entry<*, *> && + other.key == key && + other.value == value + + override fun hashCode(): Int = key.hashCode() xor value.hashCode() + + override fun toString(): String = "$key=$value" + } +} + +internal class HashMapValues internal constructor( + val backing: HashMap<*, V> +) : MutableCollection { + + override val size: Int get() = backing.size + override fun isEmpty(): Boolean = backing.isEmpty() + override fun contains(element: V): Boolean = backing.containsValue(element) + override fun containsAll(elements: Collection): Boolean = backing.containsAllValues(elements) + override fun add(element: V): Boolean = throw UnsupportedOperationException() + override fun addAll(elements: Collection): Boolean = throw UnsupportedOperationException() + override fun clear() = backing.clear() + override fun iterator(): MutableIterator = backing.valuesIterator() + override fun remove(element: V): Boolean = backing.removeValue(element) + override fun removeAll(elements: Collection): Boolean = backing.removeAllValues(elements) + override fun retainAll(elements: Collection): Boolean = backing.retainAllValues(elements) + + override fun equals(other: Any?): Boolean = + other === this || + other is Collection<*> && + contentEquals(other) + + override fun hashCode(): Int { + var result = 1 + val it = iterator() + while (it.hasNext()) { + result = result * 31 + it.next().hashCode() + } + return result + } + + override fun toString(): String = collectionToString() + + // ---------------------------- private ---------------------------- + + private fun contentEquals(other: Collection<*>): Boolean { + @Suppress("UNCHECKED_CAST") // todo: figure out something better + return size == other.size && backing.containsAllValues(other as Collection) + } +} + +internal class HashMapEntrySet internal constructor( + val backing: HashMap +) : MutableSet> { + + override val size: Int get() = backing.size + override fun isEmpty(): Boolean = backing.isEmpty() + override fun contains(element: MutableMap.MutableEntry): Boolean = backing.containsEntry(element) + override fun clear() = backing.clear() + override fun add(element: MutableMap.MutableEntry): Boolean = backing.putEntry(element) + override fun remove(element: MutableMap.MutableEntry): Boolean = backing.removeEntry(element) + override fun iterator(): MutableIterator> = backing.entriesIterator() + override fun containsAll(elements: Collection>): Boolean = backing.containsAllEntries(elements) + override fun addAll(elements: Collection>): Boolean = backing.putAllEntries(elements) + override fun removeAll(elements: Collection>): Boolean = backing.removeAllEntries(elements) + override fun retainAll(elements: Collection>): Boolean = backing.retainAllEntries(elements) + + override fun equals(other: Any?): Boolean = + other === this || + other is Set<*> && + contentEquals(other) + + override fun hashCode(): Int { + var result = 0 + val it = iterator() + while (it.hasNext()) { + result += it.next().hashCode() + } + return result + } + + override fun toString(): String = collectionToString() + + // ---------------------------- private ---------------------------- + + private fun contentEquals(other: Set<*>): Boolean { + @Suppress("UNCHECKED_CAST") // todo: get rid of unchecked cast here somehow + return size == other.size && backing.containsAllEntries(other as Collection>) + } +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/collections/HashSet.kt b/runtime/src/main/kotlin/kotlin/collections/HashSet.kt new file mode 100644 index 00000000000..da6b9f98e05 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/HashSet.kt @@ -0,0 +1,84 @@ +package kotlin.collections + +class HashSet internal constructor( + val backing: HashMap +) : MutableSet { + + constructor() : this(HashMap()) + + constructor(capacity: Int) : this(HashMap(capacity)) + + constructor(c: Collection) : this(c.size) { + addAll(c) + } + + override val size: Int get() = backing.size + override fun isEmpty(): Boolean = backing.isEmpty() + override fun contains(element: K): Boolean = backing.containsKey(element) + override fun clear() = backing.clear() + override fun add(element: K): Boolean = backing.addKey(element) >= 0 + override fun remove(element: K): Boolean = backing.removeKey(element) >= 0 + override fun iterator(): MutableIterator = backing.keysIterator() + + override fun containsAll(elements: Collection): Boolean { + val it = elements.iterator() + while (it.hasNext()) { + if (!contains(it.next())) + return false + } + return true + } + + override fun addAll(elements: Collection): Boolean { + val it = elements.iterator() + var updated = false + while (it.hasNext()) { + if (add(it.next())) + updated = true + } + return updated + } + + override fun removeAll(elements: Collection): Boolean { + val it = elements.iterator() + var updated = false + while (it.hasNext()) { + if (remove(it.next())) + updated = true + } + return updated + } + + override fun retainAll(elements: Collection): Boolean { + val it = iterator() + var updated = false + while (it.hasNext()) { + if (!elements.contains(it.next())) { + it.remove() + updated = true + } + } + return updated + } + + override fun equals(other: Any?): Boolean { + return other === this || + (other is Set<*>) && + contentEquals(other as Set) + } + + override fun hashCode(): Int { + var result = 0 + val it = iterator() + while (it.hasNext()) { + result += it.next().hashCode() + } + return result + } + + override fun toString(): String = collectionToString() + + // ---------------------------- private ---------------------------- + + private fun contentEquals(other: Set): Boolean = size == other.size && containsAll(other) +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/collections/IntUtil.kt b/runtime/src/main/kotlin/kotlin/collections/IntUtil.kt new file mode 100644 index 00000000000..5990a1b84a6 --- /dev/null +++ b/runtime/src/main/kotlin/kotlin/collections/IntUtil.kt @@ -0,0 +1,27 @@ +package kotlin.collections + +fun Int.highestOneBit() : Int { + var index = 31 + + while (index >= 0) { + var mask = (1 shl index) + if ((mask and this) != 0) { + return mask + } + index-- + } + return 0 +} + +fun Int.numberOfLeadingZeros() : Int { + var index = 31 + + while (index >= 0) { + var mask = (1 shl index) + if ((mask and this) != 0) { + return 31 - index + } + index-- + } + return 0 +} \ No newline at end of file diff --git a/runtime/src/main/kotlin/kotlin/collections/Map.kt b/runtime/src/main/kotlin/kotlin/collections/Map.kt index e0eae733950..e4604df020e 100644 --- a/runtime/src/main/kotlin/kotlin/collections/Map.kt +++ b/runtime/src/main/kotlin/kotlin/collections/Map.kt @@ -35,13 +35,6 @@ public interface Map { */ public operator fun get(key: K): V? - /** - * Returns the value corresponding to the given [key], or [defaultValue] if such a key is not present in the map. - * - * @since JDK 1.8 - */ - public fun getOrDefault(key: K, defaultValue: @UnsafeVariance V): V - // Views /** * Returns a [Set] of all keys in this map. @@ -96,13 +89,6 @@ public interface MutableMap : Map { */ public fun remove(key: K): V? - /** - * Removes the entry for the specified key only if it is mapped to the specified value. - * - * @return true if entry was removed - */ - public fun remove(key: K, value: V): Boolean - // Bulk Modification Operations /** * Updates this map with key/value pairs from the specified map [from]. diff --git a/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt b/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt index 7e1357facd0..ffa97c09267 100644 --- a/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt +++ b/runtime/src/main/kotlin/kotlin/ranges/Ranges.kt @@ -76,4 +76,251 @@ public class LongRange(start: Long, endInclusive: Long) : LongProgression(start, /** An empty range of values of type Long. */ public val EMPTY: LongRange = LongRange(1, 0) } -} \ No newline at end of file +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the +[minimumValue] otherwise. + */ +public fun > T.coerceAtLeast(minimumValue: T): T { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Byte.coerceAtLeast(minimumValue: Byte): Byte { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Short.coerceAtLeast(minimumValue: Short): Short { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Int.coerceAtLeast(minimumValue: Int): Int { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Long.coerceAtLeast(minimumValue: Long): Long { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Float.coerceAtLeast(minimumValue: Float): Float { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not less than the specified [minimumValue]. + * + * @return this value if it's greater than or equal to the [minimumValue] or the [minimumValue] otherwise. + */ +public fun Double.coerceAtLeast(minimumValue: Double): Double { + return if (this < minimumValue) minimumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun > T.coerceAtMost(maximumValue: T): T { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Byte.coerceAtMost(maximumValue: Byte): Byte { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Short.coerceAtMost(maximumValue: Short): Short { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Int.coerceAtMost(maximumValue: Int): Int { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Long.coerceAtMost(maximumValue: Long): Long { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Float.coerceAtMost(maximumValue: Float): Float { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value is not greater than the specified [maximumValue]. + * + * @return this value if it's less than or equal to the [maximumValue] or the [maximumValue] otherwise. + */ +public fun Double.coerceAtMost(maximumValue: Double): Double { + return if (this > maximumValue) maximumValue else this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun > T.coerceIn(minimumValue: T?, maximumValue: T?): T { + if (minimumValue !== null && maximumValue !== null) { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + } + else { + if (minimumValue !== null && this < minimumValue) return minimumValue + if (maximumValue !== null && this > maximumValue) return maximumValue + } + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Byte.coerceIn(minimumValue: Byte, maximumValue: Byte): Byte { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Short.coerceIn(minimumValue: Short, maximumValue: Short): Short { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Int.coerceIn(minimumValue: Int, maximumValue: Int): Int { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Long.coerceIn(minimumValue: Long, maximumValue: Long): Long { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Float.coerceIn(minimumValue: Float, maximumValue: Float): Float { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + return this +} + +/** + * Ensures that this value lies in the specified range [minimumValue]..[maximumValue]. + * + * @return this value if it's in the range, or [minimumValue] if this value is less than [minimumValue], or [maximumValue] if this value is greater than [maximumValue]. + */ +public fun Double.coerceIn(minimumValue: Double, maximumValue: Double): Double { + if (minimumValue > maximumValue) throw IllegalArgumentException("Cannot coerce value to an empty range: maximum $maximumValue is less than minimum $minimumValue.") + if (this < minimumValue) return minimumValue + if (this > maximumValue) return maximumValue + return this +} + +/** + * Ensures that this value lies in the specified [range]. + * + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun > T.coerceIn(range: ClosedRange): T { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this +} + +/** + * Ensures that this value lies in the specified [range]. + * + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Int.coerceIn(range: ClosedRange): Int { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this +} + +/** + * Ensures that this value lies in the specified [range]. + * + * @return this value if it's in the [range], or range.start if this value is less than range.start, or range.end if this value is greater than range.end. + */ +public fun Long.coerceIn(range: ClosedRange): Long { + if (range.isEmpty()) throw IllegalArgumentException("Cannot coerce value to an empty range: $range.") + return if (this < range.start) range.start else if (this > range.endInclusive) range.endInclusive else this +}