~~~ [Wasm]: String related hacks
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 6231a0e..cf24909 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
@@ -193,6 +193,7 @@
val refCastNull = getInternalFunction("wasm_ref_cast_null")
val wasmArrayCopy = getInternalFunction("wasm_array_copy")
val wasmArrayNewData0 = getInternalFunction("array_new_data0")
+ val wasmArrayNewFiexd1 = getInternalFunction("array_new_fixed1")
val primitiveTypeToCreateTypedArray = mapOf(
context.irBuiltIns.arrayClass to getFunction("createAnyArray", kotlinTopLevelPackage),
@@ -215,6 +216,7 @@
val unboxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("unboxIntrinsic")
val stringGetLiteral = getFunction("stringLiteral", builtInsPackage)
+ val streqeq = getFunction("streqeq", builtInsPackage)
val stringGetPoolSize = getInternalFunction("stringGetPoolSize")
val testFun = maybeGetFunction("test", kotlinTestPackage)
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 d8781e7..369bd21 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
@@ -629,6 +629,14 @@
body.buildInstr(WasmOp.ARRAY_NEW_DATA, location, arrayGcType, WasmImmediate.DataIdx(0))
}
+ wasmSymbols.wasmArrayNewFiexd1 -> {
+ val arrayGcType = WasmImmediate.GcType(
+ context.referenceGcType(call.type.getRuntimeClass(irBuiltIns).symbol)
+ )
+
+ body.buildInstr(WasmOp.ARRAY_NEW_FIXED, location, arrayGcType, WasmImmediate.ConstI32(1))
+ }
+
else -> {
return false
}
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt
index 32a33e9..53aab84 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/BuiltInsLowering.kt
@@ -84,6 +84,9 @@
// For eqeqSymbol use overridden `Any.equals(Any?)` if there is any.
if (call.symbol === irBuiltins.eqeqSymbol && !lhsType.isNullable()) {
+ if (lhsType.isString() && rhsType.isString()) {
+ return irCall(call, symbols.streqeq)
+ }
return irCall(call, lhsType.findEqualsMethod().symbol, argumentsAsReceivers = true)
}
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmStringSwitchOptimizerLowering.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmStringSwitchOptimizerLowering.kt
index 5da1db5..62af9e4 100644
--- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmStringSwitchOptimizerLowering.kt
+++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/lower/WasmStringSwitchOptimizerLowering.kt
@@ -11,6 +11,7 @@
import org.jetbrains.kotlin.backend.common.lower.createIrBuilder
import org.jetbrains.kotlin.backend.wasm.WasmBackendContext
import org.jetbrains.kotlin.ir.IrStatement
+import org.jetbrains.kotlin.ir.backend.js.utils.valueArguments
import org.jetbrains.kotlin.ir.builders.*
import org.jetbrains.kotlin.ir.builders.declarations.buildVariable
import org.jetbrains.kotlin.ir.declarations.*
@@ -18,7 +19,10 @@
import org.jetbrains.kotlin.ir.interpreter.toIrConst
import org.jetbrains.kotlin.ir.types.IrType
import org.jetbrains.kotlin.ir.types.isNullable
+import org.jetbrains.kotlin.ir.types.makeNotNull
+import org.jetbrains.kotlin.ir.util.copyTypeAndValueArgumentsFrom
import org.jetbrains.kotlin.ir.util.getSimpleFunction
+import org.jetbrains.kotlin.ir.util.irCall
import org.jetbrains.kotlin.ir.util.isElseBranch
import org.jetbrains.kotlin.ir.visitors.transformChildrenVoid
import org.jetbrains.kotlin.name.Name
@@ -46,7 +50,7 @@
private fun asEqCall(expression: IrExpression): IrCall? =
(expression as? IrCall)?.takeIf { it.symbol == context.irBuiltIns.eqeqSymbol }
- private class MatchedCase(val condition: IrCall, val branchIndex: Int)
+ private class MatchedCase(var condition: IrCall, val branchIndex: Int)
private class BucketSelector(val hashCode: Int, val selector: IrExpression)
override fun lower(irFile: IrFile) {
@@ -262,6 +266,14 @@
irBlock(resultType = visitedWhen.type) {
val tempIntVariable = addHashCodeVariable(firstEqCall!!)
+ stringConstantToMatchedCase.forEach {(_, c) ->
+ if (c.condition.symbol == context.irBuiltIns.eqeqSymbol) {
+ val nc = irCall(c.condition, symbols.streqeq)
+ c.condition = nc
+ }
+ c.condition.valueArguments[0]?.let { it.type = it.type.makeNotNull() }
+ }
+
val buckets = stringConstantToMatchedCase.keys.groupBy { it.hashCode() }
if (isSimpleWhen) {
diff --git a/libraries/stdlib/wasm/builtins/kotlin/String.kt b/libraries/stdlib/wasm/builtins/kotlin/String.kt
index 85d08d8..66096a5 100644
--- a/libraries/stdlib/wasm/builtins/kotlin/String.kt
+++ b/libraries/stdlib/wasm/builtins/kotlin/String.kt
@@ -6,7 +6,6 @@
package kotlin
import kotlin.wasm.internal.*
-import kotlin.math.min
/**
* The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
@@ -16,7 +15,7 @@
private var leftIfInSum: String?,
@kotlin.internal.IntrinsicConstEvaluation
public override val length: Int,
- private var _chars: WasmCharArray,
+ internal var _chars: WasmCharArray,
) : Comparable<String>, CharSequence {
public companion object {}
@@ -26,6 +25,8 @@
@kotlin.internal.IntrinsicConstEvaluation
public operator fun plus(other: Any?): String {
val right = other.toString()
+ if (right.isEmpty()) return this
+ if (this.isEmpty()) return right
return String(this, this.length + right.length, right.chars)
}
@@ -66,14 +67,28 @@
return _chars
}
- public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
- val actualStartIndex = startIndex.coerceAtLeast(0)
- val actualEndIndex = endIndex.coerceAtMost(this.length)
- val newLength = actualEndIndex - actualStartIndex
- if (newLength <= 0) return ""
+ public override fun subSequence(startIndex: Int, endIndex: Int): String {
+// val actualStartIndex = if (startIndex < 0) 0 else startIndex
+// val length1 = this.length
+// val actualEndIndex = if (endIndex > length1) length1 else endIndex
+// val newLength = actualEndIndex - actualStartIndex
+ if (startIndex < 0) error("start")
+ if (endIndex > length) error("end")
+
+ val newLength = endIndex - startIndex
+
+ when (newLength) {
+ 0 -> return EMPTY_STRING
+ 1 -> {
+ return String(null, 1, array_new_fixed1(chars.get(startIndex)))
+ }
+ }
+
+ if (newLength < 0) error("")
+
val newChars = WasmCharArray(newLength)
- copyWasmArray(chars, newChars, actualStartIndex, 0, newLength)
- return newChars.createString()
+ copyWasmArray(chars, newChars, startIndex, 0, newLength)
+ return String(null, newLength, newChars)
}
@kotlin.internal.IntrinsicConstEvaluation
@@ -93,24 +108,30 @@
return thisLength - otherLength
}
+
@kotlin.internal.IntrinsicConstEvaluation
public override fun equals(other: Any?): Boolean {
- if (other == null) return false
+ if (other == null) return false //
if (other === this) return true
- val otherString = other as? String ?: return false
+ if (other !is String) return false //
+
+ val otherString: String = other //
val thisLength = this.length
- val otherLength = otherString.length
- if (thisLength != otherLength) return false
+ if (thisLength != otherString.length) return false
val thisHash = this._hashCode
- val otherHash = other._hashCode
- if (thisHash != otherHash && thisHash != 0 && otherHash != 0) return false
+ if (thisHash != 0) {
+ val otherHash = otherString._hashCode
+ if (otherHash != 0 && thisHash != otherHash) return false
+ }
- val thisChars = this.chars
- val otherChars = other.chars
- repeat(thisLength) {
- if (thisChars.get(it) != otherChars.get(it)) return false
+ val thisChars = this.chars // _chars
+ val otherChars = otherString.chars // _chars
+ var index = 0
+ while (index < thisLength) {
+ if (thisChars.get(index) != otherChars.get(index)) return false
+ index = index + 1
}
return true
}
@@ -147,4 +168,25 @@
val newString = String(null, length, chars)
stringPool[poolId] = newString
return newString
-}
\ No newline at end of file
+}
+
+internal fun streqeq(this_: String, other: String): Boolean {
+ if (other === this_) return true
+
+ val thisChars = this_.chars
+ val otherChars = other.chars
+
+ if (otherChars === thisChars) return true
+
+ val thisLength = thisChars.len()
+ val otherLength = otherChars.len()
+
+ if (thisLength != otherLength) return false
+
+ var index = 0
+ while (index < thisLength) {
+ if (thisChars.get(index) != otherChars.get(index)) return false
+ index = index + 1
+ }
+ return true
+}
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt
index fb8bd73..e1ccd95 100644
--- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt
@@ -47,6 +47,21 @@
if (wasm_ref_is_null(lhs))
return wasm_ref_is_null(rhs)
return unsafeNotNull(lhs).equals(rhs)
+ /*
+ block {
+ if (l is null) {
+ r is null
+ br
+ }
+
+ if (r is null) {
+ 0
+ br
+ }
+
+ l.eqauls(r)
+ }
+ */
}
internal fun anyNtoString(x: Any?): String = x.toString()
@@ -119,4 +134,20 @@
// This initializer is a special case in FieldInitializersLowering
@Suppress("DEPRECATION")
@EagerInitialization
-internal val stringPool: Array<String?> = Array(stringGetPoolSize())
\ No newline at end of file
+internal val stringPool: WasmStringArray/*<String?>*/ = WasmStringArray(stringGetPoolSize())
+internal val EMPTY_STRING = ""
+
+@WasmArrayOf(String::class, isNullable = true)
+internal class WasmStringArray(size: Int) {
+ @WasmOp(WasmOp.ARRAY_GET)
+ operator fun get(index: Int): String? =
+ implementedAsIntrinsic
+
+ @WasmOp(WasmOp.ARRAY_SET)
+ operator fun set(index: Int, value: String?): Unit =
+ implementedAsIntrinsic
+
+ @WasmOp(WasmOp.ARRAY_LEN)
+ fun length(): Int =
+ implementedAsIntrinsic
+}
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
index ab1fb81..ffec793 100644
--- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
+++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/WasmInstructions.kt
@@ -34,6 +34,9 @@
internal fun <T> array_new_data0(address: Int, length: Int): T =
implementedAsIntrinsic
+internal fun array_new_fixed1(c: Char): WasmCharArray =
+ implementedAsIntrinsic
+
@WasmOp(WasmOp.I32_EQ)
internal external fun wasm_i32_eq(a: Int, b: Int): Boolean
diff --git a/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt b/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt
index 753439f..7e0622c 100644
--- a/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt
+++ b/libraries/stdlib/wasm/src/kotlin/text/StringsWasm.kt
@@ -217,7 +217,7 @@
* Returns a substring of this string that starts at the specified [startIndex] and continues to the end of the string.
*/
public actual fun String.substring(startIndex: Int): String =
- subSequence(startIndex, this.length) as String
+ subSequence(startIndex, this.length)// as String
/**
* Returns the substring of this string starting at the [startIndex] and ending right before the [endIndex].
@@ -226,7 +226,7 @@
* @param endIndex the end index (exclusive).
*/
public actual fun String.substring(startIndex: Int, endIndex: Int): String =
- subSequence(startIndex, endIndex) as String
+ subSequence(startIndex, endIndex)// as String
/**
* Returns a copy of this string converted to upper case using the rules of the default locale.