diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt index 3591f8787dc..717437d6271 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/WasmSymbols.kt @@ -157,6 +157,7 @@ class WasmSymbols( val refIsNull = getInternalFunction("wasm_ref_is_null") val refTest = getInternalFunction("wasm_ref_test") val refCast = getInternalFunction("wasm_ref_cast") + val wasmArrayCopy = getInternalFunction("wasm_array_copy") val intToLong = getInternalFunction("wasm_i64_extend_i32_s") diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt index c773ecc83fe..8b206e43aef 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/dce/WasmUsefulDeclarationProcessor.kt @@ -48,7 +48,8 @@ internal class WasmUsefulDeclarationProcessor( context.wasmSymbols.wasmInterfaceId, context.wasmSymbols.refCast, context.wasmSymbols.refTest, - context.wasmSymbols.boxIntrinsic -> { + context.wasmSymbols.boxIntrinsic, + context.wasmSymbols.wasmArrayCopy -> { call.getTypeArgument(0)?.enqueueRuntimeClassOrAny(from, "intrinsic ${call.symbol.owner.name}") true } diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt index 142d91fdfe6..6053bb45b4e 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/BodyGenerator.kt @@ -475,6 +475,13 @@ class BodyGenerator( body.buildConstI32Symbol(WasmSymbol(context.scratchMemSizeInBytes)) } + wasmSymbols.wasmArrayCopy -> { + val immediate = WasmImmediate.GcType( + context.referenceGcType(call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol) + ) + body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate) + } + else -> { return false } diff --git a/generators/wasm/WasmIntrinsicGenerator.kt b/generators/wasm/WasmIntrinsicGenerator.kt index 358befbfb60..aa5e6836c9f 100644 --- a/generators/wasm/WasmIntrinsicGenerator.kt +++ b/generators/wasm/WasmIntrinsicGenerator.kt @@ -104,7 +104,11 @@ fun wasmArrayForType( @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic + } + + internal inline fun $name.copyTo(destination: $name, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy<$name>(destination, destinationIndex, this, sourceIndex, length) } internal inline fun $name.fill(size: Int, init: (Int) -> $type) { @@ -113,8 +117,7 @@ fun wasmArrayForType( set(i, init(i)) i++ } - } - + } """.trimIndent() } diff --git a/libraries/stdlib/wasm/builtins/kotlin/Array.kt b/libraries/stdlib/wasm/builtins/kotlin/Array.kt index 31c6f07af3e..00bdbf45547 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/Array.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/Array.kt @@ -15,7 +15,7 @@ import kotlin.wasm.internal.* * for more information on arrays. */ public class Array constructor(size: Int) { - private var storage: WasmAnyArray = WasmAnyArray(size) + internal val storage: WasmAnyArray = WasmAnyArray(size) /** * Creates a new array with the specified [size], where each element is calculated by calling the specified diff --git a/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt b/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt index 28a8c955a9d..c662ac9374c 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/Arrays.kt @@ -8,7 +8,7 @@ package kotlin import kotlin.wasm.internal.* public class ByteArray(size: Int) { - private var storage = WasmByteArray(size) + internal val storage = WasmByteArray(size) public constructor(size: Int, init: (Int) -> Byte) : this(size) { storage.fill(size, init) @@ -38,7 +38,7 @@ internal fun byteArrayIterator(array: ByteArray) = object : ByteIterator() { public class CharArray(size: Int) { - private var storage = WasmCharArray(size) + internal val storage = WasmCharArray(size) public constructor(size: Int, init: (Int) -> Char) : this(size) { storage.fill(size) { init(it) } @@ -69,7 +69,7 @@ internal fun charArrayIterator(array: CharArray) = object : CharIterator() { public class ShortArray(size: Int) { - private var storage = WasmShortArray(size) + internal val storage = WasmShortArray(size) public constructor(size: Int, init: (Int) -> Short) : this(size) { storage.fill(size, init) @@ -100,7 +100,7 @@ internal fun shortArrayIterator(array: ShortArray) = object : ShortIterator() { public class IntArray(size: Int) { - private var storage = WasmIntArray(size) + internal val storage = WasmIntArray(size) public constructor(size: Int, init: (Int) -> Int) : this(size) { storage.fill(size, init) @@ -131,7 +131,7 @@ internal fun intArrayIterator(array: IntArray) = object : IntIterator() { public class LongArray(size: Int) { - private var storage = WasmLongArray (size) + internal val storage = WasmLongArray (size) public constructor(size: Int, init: (Int) -> Long) : this(size) { storage.fill(size, init) @@ -161,7 +161,7 @@ internal fun longArrayIterator(array: LongArray) = object : LongIterator() { public class FloatArray(size: Int) { - private var storage = WasmFloatArray(size) + internal val storage = WasmFloatArray(size) public constructor(size: Int, init: (Int) -> Float) : this(size) { storage.fill(size, init) @@ -191,7 +191,7 @@ internal fun floatArrayIterator(array: FloatArray) = object : FloatIterator() { public class DoubleArray(size: Int) { - private var storage = WasmDoubleArray(size) + internal val storage = WasmDoubleArray(size) public constructor(size: Int, init: (Int) -> Double) : this(size) { storage.fill(size, init) @@ -221,7 +221,7 @@ internal fun doubleArrayIterator(array: DoubleArray) = object : DoubleIterator() public class BooleanArray(size: Int) { - private var storage = WasmByteArray(size) + internal val storage = WasmByteArray(size) public constructor(size: Int, init: (Int) -> Boolean) : this(size) { storage.fill(size) { init(it).toInt().reinterpretAsByte() } diff --git a/libraries/stdlib/wasm/builtins/kotlin/String.kt b/libraries/stdlib/wasm/builtins/kotlin/String.kt index 2b45996ce9b..b119532473a 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/String.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/String.kt @@ -22,12 +22,15 @@ public class String private constructor(internal val chars: WasmCharArray) : Com * Returns a string obtained by concatenating this string with the string representation of the given [other] object. */ public operator fun plus(other: Any?): String { + val thisChars = chars val otherChars = if (other is String) other.chars else other.toString().chars - val newCharsLen = chars.len() + otherChars.len() - val newChars = WasmCharArray(newCharsLen) - newChars.fill(newCharsLen) { i -> - if (i < chars.len()) chars.get(i) else otherChars.get(i - chars.len()) - } + val thisLen = thisChars.len() + val otherLen = otherChars.len() + if (otherLen == 0) return String(thisChars) + + val newChars = WasmCharArray(thisLen + otherLen) + thisChars.copyTo(newChars, 0, 0, thisLen) + otherChars.copyTo(newChars, 0, thisLen, otherLen) return String(newChars) } @@ -47,42 +50,48 @@ public class String private constructor(internal val chars: WasmCharArray) : Com public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence { val actualStartIndex = startIndex.coerceAtLeast(0) - val actualEndIndex = endIndex.coerceAtMost(chars.len()) + val thisChars = chars + val actualEndIndex = endIndex.coerceAtMost(thisChars.len()) val newCharsLen = actualEndIndex - actualStartIndex val newChars = WasmCharArray(newCharsLen) - newChars.fill(newCharsLen) { i -> - chars.get(actualStartIndex + i) - } + thisChars.copyTo(newChars, actualStartIndex, 0, newCharsLen) return String(newChars) } public override fun compareTo(other: String): Int { - val len = min(this.length, other.length) + val thisChars = this.chars + val otherChars = other.chars + + val thisLength = thisChars.len() + val otherLength = otherChars.len() + val len = min(thisLength, otherLength) for (i in 0 until len) { - val l = this[i] - val r = other[i] + val l = thisChars.get(i) + val r = otherChars.get(i) if (l != r) return l - r } - return this.length - other.length + return thisLength - otherLength } - public override fun equals(other: Any?): Boolean { - if (other is String) - return this.compareTo(other) == 0 - return false - } + public override fun equals(other: Any?): Boolean = + other != null && + other is String && + (this.length == other.length) && + this.compareTo(other) == 0 public override fun toString(): String = this public override fun hashCode(): Int { - if (_hashCode != 0 || this.isEmpty()) + val thisLength = length + + if (_hashCode != 0 || thisLength == null) return _hashCode var hash = 0 var i = 0 - while (i < chars.len()) { + while (i < thisLength) { hash = 31 * hash + chars.get(i).toInt() i++ } diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt index 69bbc71cafe..328f0d6a7e4 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt @@ -24,6 +24,9 @@ internal fun wasm_ref_cast(a: Any?): To = internal fun wasm_ref_test(a: Any?): Boolean = implementedAsIntrinsic +internal fun wasm_array_copy(destination: T, destinationIndex: Int, source: T, sourceIndex: Int, length: Int): Unit = + implementedAsIntrinsic + @WasmOp(WasmOp.I32_EQ) public external fun wasm_i32_eq(a: Int, b: Int): Boolean diff --git a/libraries/stdlib/wasm/src/generated/_ArraysWasm.kt b/libraries/stdlib/wasm/src/generated/_ArraysWasm.kt index c2dd04aff36..322f4152c6a 100644 --- a/libraries/stdlib/wasm/src/generated/_ArraysWasm.kt +++ b/libraries/stdlib/wasm/src/generated/_ArraysWasm.kt @@ -1017,18 +1017,8 @@ public actual fun CharArray?.contentToString(): String { @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun Array.copyInto(destination: Array, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): Array { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + @Suppress("UNCHECKED_CAST") + arrayCopy(this as Array, startIndex, destination as Array, destinationOffset, endIndex - startIndex) return destination } @@ -1051,18 +1041,7 @@ public actual fun Array.copyInto(destination: Array, destinationOf @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun ByteArray.copyInto(destination: ByteArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): ByteArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1085,18 +1064,7 @@ public actual fun ByteArray.copyInto(destination: ByteArray, destinationOffset: @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun ShortArray.copyInto(destination: ShortArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): ShortArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1119,18 +1087,7 @@ public actual fun ShortArray.copyInto(destination: ShortArray, destinationOffset @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun IntArray.copyInto(destination: IntArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): IntArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1153,18 +1110,7 @@ public actual fun IntArray.copyInto(destination: IntArray, destinationOffset: In @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun LongArray.copyInto(destination: LongArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): LongArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1187,18 +1133,7 @@ public actual fun LongArray.copyInto(destination: LongArray, destinationOffset: @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun FloatArray.copyInto(destination: FloatArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): FloatArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1221,18 +1156,7 @@ public actual fun FloatArray.copyInto(destination: FloatArray, destinationOffset @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun DoubleArray.copyInto(destination: DoubleArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): DoubleArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1255,18 +1179,7 @@ public actual fun DoubleArray.copyInto(destination: DoubleArray, destinationOffs @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun BooleanArray.copyInto(destination: BooleanArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): BooleanArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } @@ -1289,18 +1202,7 @@ public actual fun BooleanArray.copyInto(destination: BooleanArray, destinationOf @SinceKotlin("1.3") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun CharArray.copyInto(destination: CharArray, destinationOffset: Int = 0, startIndex: Int = 0, endIndex: Int = size): CharArray { - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } + arrayCopy(this, startIndex, destination, destinationOffset, endIndex - startIndex) return destination } diff --git a/libraries/stdlib/wasm/src/generated/_WasmArrays.kt b/libraries/stdlib/wasm/src/generated/_WasmArrays.kt index 1e1d41b23a7..3db1676adf2 100644 --- a/libraries/stdlib/wasm/src/generated/_WasmArrays.kt +++ b/libraries/stdlib/wasm/src/generated/_WasmArrays.kt @@ -32,7 +32,11 @@ internal class WasmAnyArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmAnyArray.copyTo(destination: WasmAnyArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmAnyArray.fill(size: Int, init: (Int) -> Any?) { @@ -41,8 +45,7 @@ internal inline fun WasmAnyArray.fill(size: Int, init: (Int) -> Any?) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Byte::class, isNullable = false) internal class WasmByteArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET_S) @@ -55,7 +58,11 @@ internal class WasmByteArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmByteArray.copyTo(destination: WasmByteArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmByteArray.fill(size: Int, init: (Int) -> Byte) { @@ -64,8 +71,7 @@ internal inline fun WasmByteArray.fill(size: Int, init: (Int) -> Byte) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Char::class, isNullable = false) internal class WasmCharArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET_U) @@ -78,7 +84,11 @@ internal class WasmCharArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmCharArray.copyTo(destination: WasmCharArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmCharArray.fill(size: Int, init: (Int) -> Char) { @@ -87,8 +97,7 @@ internal inline fun WasmCharArray.fill(size: Int, init: (Int) -> Char) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Short::class, isNullable = false) internal class WasmShortArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET_S) @@ -101,7 +110,11 @@ internal class WasmShortArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmShortArray.copyTo(destination: WasmShortArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmShortArray.fill(size: Int, init: (Int) -> Short) { @@ -110,8 +123,7 @@ internal inline fun WasmShortArray.fill(size: Int, init: (Int) -> Short) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Int::class, isNullable = false) internal class WasmIntArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET) @@ -124,7 +136,11 @@ internal class WasmIntArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmIntArray.copyTo(destination: WasmIntArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmIntArray.fill(size: Int, init: (Int) -> Int) { @@ -133,8 +149,7 @@ internal inline fun WasmIntArray.fill(size: Int, init: (Int) -> Int) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Long::class, isNullable = false) internal class WasmLongArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET) @@ -147,7 +162,11 @@ internal class WasmLongArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmLongArray.copyTo(destination: WasmLongArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmLongArray.fill(size: Int, init: (Int) -> Long) { @@ -156,8 +175,7 @@ internal inline fun WasmLongArray.fill(size: Int, init: (Int) -> Long) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Float::class, isNullable = false) internal class WasmFloatArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET) @@ -170,7 +188,11 @@ internal class WasmFloatArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmFloatArray.copyTo(destination: WasmFloatArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmFloatArray.fill(size: Int, init: (Int) -> Float) { @@ -179,8 +201,7 @@ internal inline fun WasmFloatArray.fill(size: Int, init: (Int) -> Float) { set(i, init(i)) i++ } -} - +} @WasmArrayOf(Double::class, isNullable = false) internal class WasmDoubleArray(size: Int) { @WasmOp(WasmOp.ARRAY_GET) @@ -193,7 +214,11 @@ internal class WasmDoubleArray(size: Int) { @WasmOp(WasmOp.ARRAY_LEN) fun len(): Int = - implementedAsIntrinsic + implementedAsIntrinsic +} + +internal inline fun WasmDoubleArray.copyTo(destination: WasmDoubleArray, sourceIndex: Int, destinationIndex: Int, length: Int) { + wasm_array_copy(destination, destinationIndex, this, sourceIndex, length) } internal inline fun WasmDoubleArray.fill(size: Int, init: (Int) -> Double) { @@ -202,5 +227,4 @@ internal inline fun WasmDoubleArray.fill(size: Int, init: (Int) -> Double) { set(i, init(i)) i++ } -} - +} diff --git a/libraries/stdlib/wasm/src/generated/_WasmOp.kt b/libraries/stdlib/wasm/src/generated/_WasmOp.kt index c1bdf9d20b3..753e781b453 100644 --- a/libraries/stdlib/wasm/src/generated/_WasmOp.kt +++ b/libraries/stdlib/wasm/src/generated/_WasmOp.kt @@ -252,6 +252,7 @@ internal annotation class WasmOp(val name: String) { const val ARRAY_GET_U = "ARRAY_GET_U" const val ARRAY_SET = "ARRAY_SET" const val ARRAY_LEN = "ARRAY_LEN" + const val ARRAY_COPY = "ARRAY_COPY" const val I31_NEW = "I31_NEW" const val I31_GET_S = "I31_GET_S" const val I31_GET_U = "I31_GET_U" diff --git a/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt b/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt index 7143e735332..862b4c5df44 100644 --- a/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt +++ b/libraries/stdlib/wasm/src/kotlin/collections/ArraysWasm.kt @@ -5,6 +5,84 @@ package kotlin.collections +import kotlin.wasm.internal.copyTo + +private inline fun rangeCheck(index: Int, count: Int, arraySize: Int) { + if (index < 0 || count < 0 || index + count > arraySize) throw IndexOutOfBoundsException() +} + +internal inline fun arrayCopy(array: ByteArray, fromIndex: Int, destination: ByteArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: ShortArray, fromIndex: Int, destination: ShortArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: CharArray, fromIndex: Int, destination: CharArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: IntArray, fromIndex: Int, destination: IntArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: LongArray, fromIndex: Int, destination: LongArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: FloatArray, fromIndex: Int, destination: FloatArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: DoubleArray, fromIndex: Int, destination: DoubleArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: BooleanArray, fromIndex: Int, destination: BooleanArray, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + +internal inline fun arrayCopy(array: Array, fromIndex: Int, destination: Array, toIndex: Int, count: Int) { + val srcStorage = array.storage + rangeCheck(fromIndex, count, srcStorage.len()) + val dstStorage = destination.storage + rangeCheck(toIndex, count, dstStorage.len()) + srcStorage.copyTo(dstStorage, fromIndex, toIndex, count) +} + /** * Returns a *typed* array containing all of the elements of this collection. * diff --git a/libraries/stdlib/wasm/src/kotlin/text/StringBuilderWasm.kt b/libraries/stdlib/wasm/src/kotlin/text/StringBuilderWasm.kt index b5477b0bf4c..771d4dc50c3 100644 --- a/libraries/stdlib/wasm/src/kotlin/text/StringBuilderWasm.kt +++ b/libraries/stdlib/wasm/src/kotlin/text/StringBuilderWasm.kt @@ -7,18 +7,14 @@ package kotlin.text import kotlin.wasm.internal.* -internal fun insertString(array: CharArray, distIndex: Int, value: String, sourceIndex: Int, count: Int): Int { - var arrayIdx = distIndex - var stringIdx = sourceIndex - repeat(count) { - array[arrayIdx++] = value[stringIdx++] - } +internal fun insertString(array: CharArray, destinationIndex: Int, value: String, sourceIndex: Int, count: Int): Int { + value.chars.copyTo(array.storage, sourceIndex, destinationIndex, count) return count } internal fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int): String { val copy = WasmCharArray(size) - copy.fill(size) { array[it + start] } + array.storage.copyTo(copy, start, 0, size) return String.unsafeFromCharArray(copy) } diff --git a/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt b/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt index a5703862013..89fde9f7886 100644 --- a/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt +++ b/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt @@ -74,7 +74,7 @@ public actual fun String(chars: CharArray, offset: Int, length: Int): String { throw IndexOutOfBoundsException() val copy = WasmCharArray(length) - copy.fill(length) { chars[it + offset] } + chars.storage.copyTo(copy, offset, 0, length) return String.unsafeFromCharArray(copy) } @@ -84,8 +84,10 @@ public actual fun String(chars: CharArray, offset: Int, length: Int): String { @SinceKotlin("1.4") @WasExperimental(ExperimentalStdlibApi::class) public actual fun CharArray.concatToString(): String { - val copy = WasmCharArray(this.size) - copy.fill(this.size) { this[it] } + val thisStorage = this.storage + val thisLength = thisStorage.len() + val copy = WasmCharArray(thisLength) + this.storage.copyTo(copy, 0, 0, thisLength) return String.unsafeFromCharArray(copy) } @@ -106,7 +108,7 @@ public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = val length = endIndex - startIndex val copy = WasmCharArray(length) - copy.fill(length) { this[it + startIndex] } + this.storage.copyTo(copy, startIndex, 0, length) return String.unsafeFromCharArray(copy) } @@ -116,7 +118,11 @@ public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int = @SinceKotlin("1.4") @WasExperimental(ExperimentalStdlibApi::class) public actual fun String.toCharArray(): CharArray { - return CharArray(length) { get(it) } + val thisChars = this.chars + val thisLength = thisChars.len() + val newArray = CharArray(thisLength) + thisChars.copyTo(newArray.storage, 0, 0, thisLength) + return newArray } /** @@ -133,7 +139,10 @@ public actual fun String.toCharArray(): CharArray { @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") public actual fun String.toCharArray(startIndex: Int = 0, endIndex: Int = this.length): CharArray { AbstractList.checkBoundsIndexes(startIndex, endIndex, length) - return CharArray(endIndex - startIndex) { get(startIndex + it) } + val newLength = endIndex - startIndex + val newArray = CharArray(newLength) + this.chars.copyTo(newArray.storage, startIndex, 0, newLength) + return newArray } /** diff --git a/libraries/stdlib/wasm/src/kotlin/text/regex/DecompositionValues.kt b/libraries/stdlib/wasm/src/kotlin/text/regex/DecompositionValues.kt index 8051d863bdc..6fa169a9a95 100644 --- a/libraries/stdlib/wasm/src/kotlin/text/regex/DecompositionValues.kt +++ b/libraries/stdlib/wasm/src/kotlin/text/regex/DecompositionValues.kt @@ -350,9 +350,9 @@ private val decompositionValueIndex = shortArrayOf( ) internal fun getDecompositionByIndex(index: Int): IntArray { - val valueIndex = decompositionValueIndex[index] - val size = decompositionValueIndex[index + 1] - valueIndex - return IntArray(size) { shift -> - decompositionValues[valueIndex + shift] - } + val startIndex = decompositionValueIndex[index].toInt() + val endIndex = decompositionValueIndex[index + 1].toInt() + val result = IntArray(endIndex - startIndex) + decompositionValues.copyInto(result, startIndex = startIndex, endIndex = endIndex) + return result } \ No newline at end of file diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt index 04c797f6db5..96755d3791f 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Arrays.kt @@ -919,26 +919,6 @@ object ArrayOps : TemplateGroupBase() { """ } } - on(Backend.Wasm) { - body { - """ - AbstractList.checkRangeIndexes(startIndex, endIndex, this.size) - val rangeSize = endIndex - startIndex - AbstractList.checkRangeIndexes(destinationOffset, destinationOffset + rangeSize, destination.size) - - if (this !== destination || destinationOffset <= startIndex) { - for (index in 0 until rangeSize) { - destination[destinationOffset + index] = this[startIndex + index] - } - } else { - for (index in rangeSize - 1 downTo 0) { - destination[destinationOffset + index] = this[startIndex + index] - } - } - return destination - """ - } - } } } } diff --git a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt index 65270c5cb2d..eb3c31d6032 100644 --- a/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt +++ b/wasm/wasm.ir/src/org/jetbrains/kotlin/wasm/ir/Operators.kt @@ -354,6 +354,7 @@ enum class WasmOp( ARRAY_GET_U("array.get_u", 0xFB_15, listOf(STRUCT_TYPE_IDX)), ARRAY_SET("array.set", 0xFB_16, listOf(STRUCT_TYPE_IDX)), ARRAY_LEN("array.len", 0xFB_17, listOf(STRUCT_TYPE_IDX)), + ARRAY_COPY("array.copy", 0xFB_18, listOf(STRUCT_TYPE_IDX, STRUCT_TYPE_IDX)), I31_NEW("i31.new", 0xFB_20), I31_GET_S("i31.get_s", 0xFB_21),