WASM: Implement wasm-native strings part 1
There are several changes here but they all required for at least one test to pass. - Implemented String class and several utility functions using built-in CharArray - Added new constant memory segment to hold string literals and required funcs to work with them - Added very crude mostly incorrect rudimentary ability to pass strings back to javascript
This commit is contained in:
committed by
TeamCityServer
parent
9ccdffe8ad
commit
0f84525bdc
@@ -154,7 +154,3 @@ public class Char private constructor(public val value: Char) : Comparable<Char>
|
||||
public const val SIZE_BITS: Int = 16
|
||||
}
|
||||
}
|
||||
|
||||
@WasmImport("runtime", "Char_toString")
|
||||
private fun charToString(c: Char): String = implementedAsIntrinsic
|
||||
|
||||
|
||||
@@ -341,7 +341,7 @@ public class Byte private constructor(public val value: Byte) : Number(), Compar
|
||||
wasm_i32_eq(this.toInt(), other.toInt())
|
||||
|
||||
public override fun toString(): String =
|
||||
byteToStringImpl(this)
|
||||
TODO("Wasm: string coercion")
|
||||
|
||||
public override inline fun hashCode(): Int =
|
||||
this.toInt()
|
||||
@@ -352,10 +352,6 @@ public class Byte private constructor(public val value: Byte) : Number(), Compar
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@WasmImport("runtime", "coerceToString")
|
||||
private fun byteToStringImpl(byte: Byte): String =
|
||||
implementedAsIntrinsic
|
||||
|
||||
/**
|
||||
* Represents a 16-bit signed integer.
|
||||
*/
|
||||
@@ -684,7 +680,8 @@ public class Short private constructor(public val value: Short) : Number(), Comp
|
||||
public override fun equals(other: Any?): Boolean =
|
||||
other is Short && wasm_i32_eq(this.toInt(), other.toInt())
|
||||
|
||||
public override fun toString(): String = shortToStringImpl(this)
|
||||
public override fun toString(): String =
|
||||
TODO("Wasm: string coercion")
|
||||
|
||||
public override inline fun hashCode(): Int =
|
||||
this.toInt()
|
||||
@@ -695,9 +692,6 @@ public class Short private constructor(public val value: Short) : Number(), Comp
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@WasmImport("runtime", "coerceToString")
|
||||
private fun shortToStringImpl(x: Short): String = implementedAsIntrinsic
|
||||
|
||||
/**
|
||||
* Represents a 32-bit signed integer.
|
||||
*/
|
||||
@@ -1066,7 +1060,7 @@ public class Int private constructor(val value: Int) : Number(), Comparable<Int>
|
||||
other is Int && wasm_i32_eq(this, other)
|
||||
|
||||
public override fun toString(): String =
|
||||
intToStringImpl(this)
|
||||
TODO("Wasm: string coercion")
|
||||
|
||||
public override inline fun hashCode(): Int =
|
||||
this
|
||||
@@ -1092,10 +1086,6 @@ public class Int private constructor(val value: Int) : Number(), Comparable<Int>
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@WasmImport("runtime", "coerceToString")
|
||||
private fun intToStringImpl(x: Int): String =
|
||||
implementedAsIntrinsic
|
||||
|
||||
/**
|
||||
* Represents a 64-bit signed integer.
|
||||
*/
|
||||
@@ -1462,9 +1452,8 @@ public class Long private constructor(val value: Long) : Number(), Comparable<Lo
|
||||
public override fun equals(other: Any?): Boolean =
|
||||
other is Long && wasm_i64_eq(this, other)
|
||||
|
||||
// TODO: Implement proper Long.toString
|
||||
public override fun toString(): String =
|
||||
toDouble().toString()
|
||||
TODO("Wasm: string coercion")
|
||||
|
||||
public override fun hashCode(): Int =
|
||||
((this ushr 32) xor this).toInt()
|
||||
@@ -1769,7 +1758,7 @@ public class Float private constructor(public val value: Float) : Number(), Comp
|
||||
other is Float && this.equals(other)
|
||||
|
||||
public override fun toString(): String =
|
||||
floatToStringImpl(this)
|
||||
TODO("Wasm: string coercion")
|
||||
|
||||
public override inline fun hashCode(): Int =
|
||||
bits()
|
||||
@@ -1779,10 +1768,6 @@ public class Float private constructor(public val value: Float) : Number(), Comp
|
||||
internal fun bits(): Int = implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@WasmImport("runtime", "coerceToString")
|
||||
private fun floatToStringImpl(x: Float): String =
|
||||
implementedAsIntrinsic
|
||||
|
||||
/**
|
||||
* Represents a double-precision 64-bit IEEE 754 floating point number.
|
||||
*/
|
||||
@@ -2086,7 +2071,7 @@ public class Double private constructor(public val value: Double) : Number(), Co
|
||||
other is Double && this.bits() == other.bits()
|
||||
|
||||
public override fun toString(): String =
|
||||
doubleToStringImpl(this)
|
||||
TODO("Wasm: string coercion")
|
||||
|
||||
public override inline fun hashCode(): Int = bits().hashCode()
|
||||
|
||||
@@ -2095,7 +2080,3 @@ public class Double private constructor(public val value: Double) : Number(), Co
|
||||
internal fun bits(): Long =
|
||||
implementedAsIntrinsic
|
||||
}
|
||||
|
||||
@WasmImport("runtime", "coerceToString")
|
||||
private fun doubleToStringImpl(x: Double): String =
|
||||
implementedAsIntrinsic
|
||||
|
||||
@@ -11,18 +11,17 @@ import kotlin.wasm.internal.*
|
||||
* The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
|
||||
* implemented as instances of this class.
|
||||
*/
|
||||
@WasmAutoboxed
|
||||
public class String constructor(public val string: String) : Comparable<String>, CharSequence {
|
||||
public class String internal constructor(internal val chars: CharArray) : Comparable<String>, CharSequence {
|
||||
public companion object;
|
||||
|
||||
/**
|
||||
* Returns a string obtained by concatenating this string with the string representation of the given [other] object.
|
||||
*/
|
||||
public operator fun plus(other: Any?): String =
|
||||
stringPlusImpl(this, other.toString())
|
||||
String(chars + other.toString().chars)
|
||||
|
||||
public override val length: Int
|
||||
get() = stringLengthImpl(this)
|
||||
get() = chars.size
|
||||
|
||||
/**
|
||||
* Returns the character of this string at the specified [index].
|
||||
@@ -30,43 +29,49 @@ public class String constructor(public val string: String) : Comparable<String>,
|
||||
* If the [index] is out of bounds of this string, throws an [IndexOutOfBoundsException] except in Kotlin/JS
|
||||
* where the behavior is unspecified.
|
||||
*/
|
||||
public override fun get(index: Int): Char =
|
||||
stringGetCharImpl(this, index)
|
||||
public override fun get(index: Int): Char = chars[index]
|
||||
|
||||
public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence =
|
||||
stringSubSequenceImpl(this, startIndex, endIndex)
|
||||
TODO("todo")
|
||||
|
||||
public override fun compareTo(other: String): Int =
|
||||
stringCompareToImpl(this, other)
|
||||
TODO("todo")
|
||||
|
||||
public override fun equals(other: Any?): Boolean {
|
||||
if (other is String)
|
||||
return this.compareTo(other) == 0
|
||||
return chars.contentEquals(other.chars)
|
||||
//return this.compareTo(other) == 0
|
||||
return false
|
||||
}
|
||||
|
||||
public override fun toString(): String = this
|
||||
|
||||
// TODO: Implement
|
||||
// TODO: this is not nice
|
||||
public override fun hashCode(): Int = 10
|
||||
}
|
||||
|
||||
@JsFun("(it, other) => it + String(other)")
|
||||
private fun stringPlusImpl(it: String, other: String): String =
|
||||
implementedAsIntrinsic
|
||||
internal fun stringLiteral(startAddr: Int, length: Int) = String(unsafeRawMemoryToCharArray(startAddr, length))
|
||||
|
||||
@JsFun("(it) => it.length")
|
||||
private fun stringLengthImpl(it: String): Int =
|
||||
implementedAsIntrinsic
|
||||
internal fun charToString(c: Char): String = String(charArrayOf(c))
|
||||
|
||||
@WasmImport("runtime", "String_getChar")
|
||||
private fun stringGetCharImpl(it: String, index: Int): Char =
|
||||
implementedAsIntrinsic
|
||||
internal fun wasm_string_eq(x: String, y: String): Boolean = x.equals(y)
|
||||
|
||||
@WasmImport("runtime", "String_compareTo")
|
||||
private fun stringCompareToImpl(it: String, other: String): Int =
|
||||
implementedAsIntrinsic
|
||||
|
||||
@WasmImport("runtime", "String_subsequence")
|
||||
private fun stringSubSequenceImpl(string: String, startIndex: Int, endIndex: Int): String =
|
||||
implementedAsIntrinsic
|
||||
//@JsFun("(it, other) => it + String(other)")
|
||||
//private fun stringPlusImpl(it: String, other: String): String =
|
||||
// implementedAsIntrinsic
|
||||
//
|
||||
//@JsFun("(it) => it.length")
|
||||
//private fun stringLengthImpl(it: String): Int =
|
||||
// implementedAsIntrinsic
|
||||
//
|
||||
//@WasmImport("runtime", "String_getChar")
|
||||
//private fun stringGetCharImpl(it: String, index: Int): Char =
|
||||
// implementedAsIntrinsic
|
||||
//
|
||||
//@WasmImport("runtime", "String_compareTo")
|
||||
//private fun stringCompareToImpl(it: String, other: String): Int =
|
||||
// implementedAsIntrinsic
|
||||
//
|
||||
//@WasmImport("runtime", "String_subsequence")
|
||||
//private fun stringSubSequenceImpl(string: String, startIndex: Int, endIndex: Int): String =
|
||||
// implementedAsIntrinsic
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright 2010-2021 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package kotlin.wasm.internal
|
||||
|
||||
// This is called when exported function returns a string. It writes [i32 length, [i16 chars ...]] into a temporary raw memory area and
|
||||
// returns pointer to the start of it.
|
||||
// Note: currently there is a single temporary raw memory area so it's not possible to export more than one string at a time.
|
||||
internal fun exportStringRet(src: String): Int {
|
||||
val retAddr = unsafeGetScratchRawMemory(INT_SIZE_BYTES + src.length * CHAR_SIZE_BYTES)
|
||||
wasm_i32_store(retAddr, src.length)
|
||||
unsafeCharArrayToRawMemory(src.toCharArray(), retAddr + INT_SIZE_BYTES)
|
||||
return retAddr
|
||||
}
|
||||
|
||||
// See importStringToJs for the JS-side import for strings
|
||||
@@ -5,10 +5,34 @@
|
||||
|
||||
package kotlin.wasm.internal
|
||||
|
||||
@WasmImport("runtime", "String_getLiteral")
|
||||
internal fun stringLiteral(index: Int): String =
|
||||
internal const val CHAR_SIZE_BYTES = 2
|
||||
internal const val INT_SIZE_BYTES = 4
|
||||
|
||||
internal fun unsafeRawMemoryToChar(addr: Int) = wasm_i32_load16_u(addr).toChar()
|
||||
|
||||
internal fun unsafeRawMemoryToCharArray(startAddr: Int, length: Int): CharArray {
|
||||
val ret = CharArray(length)
|
||||
for (i in 0 until length) {
|
||||
ret[i] = unsafeRawMemoryToChar(startAddr + i * CHAR_SIZE_BYTES)
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
// Returns a pointer into a temporary scratch segment in the raw wasm memory. Aligned by 4.
|
||||
// Note: currently there is single such segment for a whole wasm module, so use with care.
|
||||
@ExcludedFromCodegen
|
||||
internal fun unsafeGetScratchRawMemory(sizeBytes: Int): Int =
|
||||
implementedAsIntrinsic
|
||||
|
||||
// Assumes there is enough space at the destination, fails with wasm trap otherwise.
|
||||
internal fun unsafeCharArrayToRawMemory(src: CharArray, dstAddr: Int) {
|
||||
var curAddr = dstAddr
|
||||
for (i in src) {
|
||||
wasm_i32_store16(curAddr, i)
|
||||
curAddr += CHAR_SIZE_BYTES
|
||||
}
|
||||
}
|
||||
|
||||
@WasmReinterpret
|
||||
internal fun unsafeNotNull(x: Any?): Any =
|
||||
implementedAsIntrinsic
|
||||
|
||||
@@ -308,4 +308,13 @@ public external fun wasm_i64_trunc_sat_f32_s(a: Float): Long
|
||||
public external fun wasm_i64_trunc_sat_f64_s(a: Double): Long
|
||||
|
||||
@WasmOp(WasmOp.I32_LOAD)
|
||||
public external fun wasm_i32_load(x: Int): Int
|
||||
public external fun wasm_i32_load(x: Int): Int
|
||||
|
||||
@WasmOp(WasmOp.I32_LOAD16_U)
|
||||
public external fun wasm_i32_load16_u(x: Int): Int
|
||||
|
||||
@WasmOp(WasmOp.I32_STORE)
|
||||
public external fun wasm_i32_store(addr: Int, i: Int): Unit
|
||||
|
||||
@WasmOp(WasmOp.I32_STORE16)
|
||||
public external fun wasm_i32_store16(addr: Int, c: Char): Unit
|
||||
@@ -12,7 +12,3 @@ fun wasm_i32_compareTo(x: Int, y: Int): Int =
|
||||
|
||||
fun wasm_i64_compareTo(x: Long, y: Long): Int =
|
||||
wasm_i64_ge_s(x, y).toInt() - wasm_i64_le_s(x, y).toInt()
|
||||
|
||||
@WasmImport("runtime", "String_equals")
|
||||
fun wasm_string_eq(x: String, y: String): Boolean =
|
||||
implementedAsIntrinsic
|
||||
@@ -287,7 +287,7 @@ public actual fun CharArray.concatToString(startIndex: Int, endIndex: Int): Stri
|
||||
*/
|
||||
@SinceKotlin("1.3")
|
||||
@ExperimentalStdlibApi
|
||||
public actual fun String.toCharArray(): CharArray = TODO("Wasm stdlib: Text")
|
||||
public actual fun String.toCharArray(): CharArray = this.chars
|
||||
|
||||
/**
|
||||
* Returns a [CharArray] containing characters of this string or its substring.
|
||||
|
||||
Reference in New Issue
Block a user