[WASM] Caching string literals in global pool

This commit is contained in:
Igor Yakovlev
2022-10-07 19:03:04 +02:00
committed by teamcity
parent f95bfccf59
commit 033e2c45f1
21 changed files with 206 additions and 126 deletions
@@ -63,7 +63,9 @@ class WasmBackendContext(
override val internalPackageFqn = FqName("kotlin.wasm")
private val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(builtIns.builtInsModule, FqName("kotlin.wasm.internal"))
val kotlinWasmInternalPackageFqn = internalPackageFqn.child(Name.identifier("internal"))
private val internalPackageFragmentDescriptor = EmptyPackageFragmentDescriptor(builtIns.builtInsModule, kotlinWasmInternalPackageFqn)
// TODO: Merge with JS IR Backend context lazy file
val internalPackageFragment by lazy {
IrFileImpl(object : IrFileEntry {
@@ -178,6 +178,7 @@ class WasmSymbols(
val unboxIntrinsic: IrSimpleFunctionSymbol = getInternalFunction("unboxIntrinsic")
val stringGetLiteral = getFunction("stringLiteral", builtInsPackage)
val stringGetPoolSize = getInternalFunction("stringGetPoolSize")
val testFun = maybeGetFunction("test", kotlinTestPackage)
val suiteFun = maybeGetFunction("suite", kotlinTestPackage)
@@ -44,6 +44,10 @@ class BodyGenerator(
buildInstr(WasmOp.GET_UNIT, WasmImmediate.FuncIdx(context.referenceFunction(unitGetInstance.symbol)))
}
private val anyConstructor by lazy {
wasmSymbols.any.constructors.first { it.owner.valueParameters.isEmpty() }
}
// Generates code for the given IR element. Leaves something on the stack unless expression was of the type Void.
internal fun generateExpression(elem: IrElement) {
assert(elem is IrExpression || elem is IrVariable) { "Unsupported statement kind" }
@@ -187,12 +191,13 @@ class BodyGenerator(
override fun visitConstructorCall(expression: IrConstructorCall) {
val klass: IrClass = expression.symbol.owner.parentAsClass
val klassSymbol: IrClassSymbol = klass.symbol
require(!backendContext.inlineClassesUtils.isClassInlineLike(klass)) {
"All inline class constructor calls must be lowered to static function calls"
}
val wasmGcType: WasmSymbol<WasmTypeDeclaration> = context.referenceGcType(klass.symbol)
val wasmGcType: WasmSymbol<WasmTypeDeclaration> = context.referenceGcType(klassSymbol)
if (klass.getWasmArrayAnnotation() != null) {
require(expression.valueArgumentsCount == 1) { "@WasmArrayOf constructs must have exactly one argument" }
@@ -204,29 +209,40 @@ class BodyGenerator(
return
}
//ClassITable and VTable load
body.buildGetGlobal(context.referenceGlobalVTable(klass.symbol))
if (klass.hasInterfaceSuperClass()) {
body.buildGetGlobal(context.referenceGlobalClassITable(klass.symbol))
} else {
body.buildRefNull(WasmHeapType.Simple.Data)
generateAnyParameters(klassSymbol)
if (expression.symbol.owner.hasWasmPrimitiveConstructorAnnotation()) {
for (i in 0 until expression.valueArgumentsCount) {
generateExpression(expression.getValueArgument(i)!!)
}
body.buildStructNew(wasmGcType)
return
}
val wasmClassId = context.referenceClassId(klass.symbol)
val irFields: List<IrField> = klass.allFields(backendContext.irBuiltIns)
irFields.forEachIndexed { index, field ->
if (index == 0)
body.buildConstI32Symbol(wasmClassId)
else
if (index > 1) {
generateDefaultInitializerForType(context.transformType(field.type), body)
}
}
body.buildStructNew(wasmGcType)
generateCall(expression)
}
private fun generateAnyParameters(klassSymbol: IrClassSymbol) {
//ClassITable and VTable load
body.buildGetGlobal(context.referenceGlobalVTable(klassSymbol))
if (klassSymbol.owner.hasInterfaceSuperClass()) {
body.buildGetGlobal(context.referenceGlobalClassITable(klassSymbol))
} else {
body.buildRefNull(WasmHeapType.Simple.Data)
}
body.buildConstI32Symbol(context.referenceClassId(klassSymbol))
body.buildConstI32(0) // Any::_hashCode
}
override fun visitDelegatingConstructorCall(expression: IrDelegatingConstructorCall) {
val klass = context.irFunction.parentAsClass
@@ -244,19 +260,8 @@ class BodyGenerator(
// Box intrinsic has an additional klass ID argument.
// Processing it separately
if (call.symbol == wasmSymbols.boxIntrinsic) {
val klass = call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns)
val klassSymbol = klass.symbol
//ClassITable and VTable load
body.buildGetGlobal(context.referenceGlobalVTable(klassSymbol))
if (klass.hasInterfaceSuperClass()) {
body.buildGetGlobal(context.referenceGlobalClassITable(klassSymbol))
} else {
body.buildRefNull(WasmHeapType.Simple.Data)
}
body.buildConstI32Symbol(context.referenceClassId(klassSymbol))
body.buildConstI32(0) // Any::_hashCode
val klassSymbol = call.getTypeArgument(0)!!.getRuntimeClass(irBuiltIns).symbol
generateAnyParameters(klassSymbol)
generateExpression(call.getValueArgument(0)!!)
body.buildStructNew(context.referenceGcType(klassSymbol))
return
@@ -301,6 +306,9 @@ class BodyGenerator(
return
}
// We skip now calling any ctor because it is empty
if (function.symbol.owner.hasWasmPrimitiveConstructorAnnotation()) return
val isSuperCall = call is IrCall && call.superQualifierSymbol != null
if (function is IrSimpleFunction && function.isOverridable && !isSuperCall) {
// Generating index for indirect call
@@ -483,6 +491,10 @@ class BodyGenerator(
body.buildInstr(WasmOp.ARRAY_COPY, immediate, immediate)
}
wasmSymbols.stringGetPoolSize -> {
body.buildConstI32Symbol(context.stringPoolSize)
}
else -> {
return false
}
@@ -372,13 +372,19 @@ class DeclarationGenerator(
//TODO("FqName for inner classes could be invalid due to topping it out from outer class")
val packageName = if (fqnShouldBeEmitted) classMetadata.klass.kotlinFqName.parentOrNull()?.asString() ?: "" else ""
val simpleName = classMetadata.klass.kotlinFqName.shortName().asString()
val (packageNameAddress, packageNamePoolId) = context.referenceStringLiteralAddressAndId(packageName)
val (simpleNameAddress, simpleNamePoolId) = context.referenceStringLiteralAddressAndId(simpleName)
val typeInfo = ConstantDataStruct(
name = "TypeInfo",
elements = listOf(
ConstantDataIntField("TypePackageNameLength", packageName.length),
ConstantDataIntField("TypePackageNamePtr", context.referenceStringLiteral(packageName)),
ConstantDataIntField("TypePackageNameId", packageNamePoolId),
ConstantDataIntField("TypePackageNamePtr", packageNameAddress),
ConstantDataIntField("TypeNameLength", simpleName.length),
ConstantDataIntField("TypeNamePtr", context.referenceStringLiteral(simpleName))
ConstantDataIntField("TypeNameId", simpleNamePoolId),
ConstantDataIntField("TypeNamePtr", simpleNameAddress)
)
)
@@ -424,8 +430,8 @@ class DeclarationGenerator(
val initValue: IrExpression? = declaration.initializer?.expression
if (initValue != null) {
check(initValue is IrConst<*> && initValue.kind !is IrConstKind.String && initValue.kind !is IrConstKind.Null) {
"Static field initializer should be non-string const or null"
check(initValue is IrConst<*> && initValue.kind !is IrConstKind.String) {
"Static field initializer should be string or const"
}
generateConstExpression(initValue, wasmExpressionGenerator, context)
} else {
@@ -481,8 +487,12 @@ fun generateConstExpression(expression: IrConst<*>, body: WasmExpressionBuilder,
is IrConstKind.Float -> body.buildConstF32(kind.valueOf(expression))
is IrConstKind.Double -> body.buildConstF64(kind.valueOf(expression))
is IrConstKind.String -> {
body.buildConstI32Symbol(context.referenceStringLiteral(kind.valueOf(expression)))
body.buildConstI32(kind.valueOf(expression).length)
val stringValue = kind.valueOf(expression)
val (literalAddress, literalPoolId) = context.referenceStringLiteralAddressAndId(stringValue)
body.buildConstI32Symbol(literalPoolId)
body.buildConstI32Symbol(literalAddress)
body.buildConstI32(stringValue.length)
body.buildConstI32(stringValue.hashCode())
body.buildCall(context.referenceFunction(context.backendContext.wasmSymbols.stringGetLiteral))
}
else -> error("Unknown constant kind")
@@ -18,6 +18,8 @@ interface WasmBaseCodegenContext {
val scratchMemAddr: WasmSymbol<Int>
val scratchMemSizeInBytes: Int
val stringPoolSize: WasmSymbol<Int>
fun referenceFunction(irFunction: IrFunctionSymbol): WasmSymbol<WasmFunction>
fun referenceGlobalField(irField: IrFieldSymbol): WasmSymbol<WasmGlobal>
fun referenceGlobalVTable(irClass: IrClassSymbol): WasmSymbol<WasmGlobal>
@@ -34,7 +36,7 @@ interface WasmBaseCodegenContext {
fun referenceClassId(irClass: IrClassSymbol): WasmSymbol<Int>
fun referenceInterfaceId(irInterface: IrClassSymbol): WasmSymbol<Int>
fun referenceStringLiteral(string: String): WasmSymbol<Int>
fun referenceStringLiteralAddressAndId(string: String): Pair<WasmSymbol<Int>, WasmSymbol<Int>>
fun transformType(irType: IrType): WasmType
fun transformFieldType(irType: IrType): WasmType
@@ -36,7 +36,9 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
ReferencableElements<IrClassSymbol, Int>()
val interfaceId =
ReferencableElements<IrClassSymbol, Int>()
val stringLiteralId =
val stringLiteralAddress =
ReferencableElements<String, Int>()
val stringLiteralPoolId =
ReferencableElements<String, Int>()
private val tagFuncType = WasmFunctionType(
@@ -62,6 +64,8 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val scratchMemAddr = WasmSymbol<Int>()
val scratchMemSizeInBytes = 65_536
val stringPoolSize = WasmSymbol<Int>()
open class ReferencableElements<Ir, Wasm : Any> {
val unbound = mutableMapOf<Ir, WasmSymbol<Wasm>>()
fun reference(ir: Ir): WasmSymbol<Wasm> {
@@ -92,7 +96,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val wasmToIr = mutableMapOf<Wasm, Ir>()
}
@OptIn(ExperimentalUnsignedTypes::class)
fun linkWasmCompiledFragments(): WasmModule {
bind(functions.unbound, functions.defined)
bind(globalFields.unbound, globalFields.defined)
@@ -123,13 +126,16 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
val stringDataSectionStart = currentDataSectionAddress
val stringDataSectionBytes = mutableListOf<Byte>()
val stringAddrs = mutableMapOf<String, Int>()
for (str in stringLiteralId.unbound.keys) {
val constData = ConstantDataCharArray("string_literal", str.toCharArray())
var stringLiteralCount = 0
for ((string, symbol) in stringLiteralAddress.unbound) {
val constData = ConstantDataCharArray("string_literal", string.toCharArray())
stringDataSectionBytes += constData.toBytes().toList()
stringAddrs[str] = currentDataSectionAddress
symbol.bind(currentDataSectionAddress)
stringLiteralPoolId.reference(string).bind(stringLiteralCount)
currentDataSectionAddress += constData.sizeInBytes
stringLiteralCount++
}
stringPoolSize.bind(stringLiteralCount)
// Reserve some memory to pass complex exported types (like strings). It's going to be accessible through 'unsafeGetScratchRawMemory'
// runtime call from stdlib.
@@ -138,7 +144,6 @@ class WasmCompiledModuleFragment(val irBuiltIns: IrBuiltIns) {
currentDataSectionAddress += scratchMemSizeInBytes
bind(classIds.unbound, klassIds)
bind(stringLiteralId.unbound, stringAddrs)
interfaceId.unbound.onEachIndexed { index, entry -> entry.value.bind(index) }
val data = typeInfo.buildData(address = { klassIds.getValue(it) }) +
@@ -25,6 +25,9 @@ class WasmModuleCodegenContextImpl(
override val scratchMemAddr: WasmSymbol<Int>
get() = wasmFragment.scratchMemAddr
override val stringPoolSize: WasmSymbol<Int>
get() = wasmFragment.stringPoolSize
override val scratchMemSizeInBytes: Int
get() = wasmFragment.scratchMemSizeInBytes
@@ -58,8 +61,10 @@ class WasmModuleCodegenContextImpl(
return with(typeTransformer) { irType.toWasmBlockResultType() }
}
override fun referenceStringLiteral(string: String): WasmSymbol<Int> {
return wasmFragment.stringLiteralId.reference(string)
override fun referenceStringLiteralAddressAndId(string: String): Pair<WasmSymbol<Int>, WasmSymbol<Int>> {
val address = wasmFragment.stringLiteralAddress.reference(string)
val id = wasmFragment.stringLiteralPoolId.reference(string)
return address to id
}
override fun generateTypeInfo(irClass: IrClassSymbol, typeInfo: ConstantDataElement) {
@@ -17,19 +17,20 @@ import org.jetbrains.kotlin.ir.expressions.IrBlockBody
import org.jetbrains.kotlin.ir.expressions.IrConst
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.ir.expressions.IrExpression
import org.jetbrains.kotlin.ir.util.fqNameWhenAvailable
import org.jetbrains.kotlin.ir.visitors.IrElementVisitorVoid
import org.jetbrains.kotlin.ir.visitors.acceptChildrenVoid
import org.jetbrains.kotlin.name.Name
/**
* Move initialization of global fields to start function.
*
* WebAssembly allows only constant expressions to be used directly in
* field initializers.
*
* TODO: Don't move null and nullable constant initializers
* TODO: Make field initialization lazy. Needs design.
*/
class FieldInitializersLowering(val context: WasmBackendContext) : FileLoweringPass {
private val stringPoolFqName = context.kotlinWasmInternalPackageFqn.child(Name.identifier("stringPool"))
override fun lower(irFile: IrFile) {
val builder = context.createIrBuilder(context.fieldInitFunction.symbol)
val startFunctionBody = context.fieldInitFunction.body as IrBlockBody
@@ -48,11 +49,18 @@ class FieldInitializersLowering(val context: WasmBackendContext) : FileLoweringP
if (!declaration.isStatic) return
val initValue: IrExpression = declaration.initializer?.expression ?: return
// Constant primitive initializers without implicit casting can be processed by native wasm initializers
if (initValue.type == declaration.type && initValue is IrConst<*> && initValue.kind !is IrConstKind.String && initValue.kind !is IrConstKind.Null) return
if (initValue is IrConst<*>) {
if (initValue.kind is IrConstKind.Null) return
if (initValue.type == declaration.type && initValue.kind !is IrConstKind.String) return
}
val initializerStatement = builder.at(initValue).irSetField(null, declaration, initValue)
when (declaration.fqNameWhenAvailable) {
stringPoolFqName -> startFunctionBody.statements.add(0, initializerStatement)
else -> startFunctionBody.statements.add(initializerStatement)
}
startFunctionBody.statements.add(
builder.at(initValue).irSetField(null, declaration, initValue)
)
// Replace initializer with default one
declaration.initializer = null
}
@@ -29,6 +29,9 @@ fun IrAnnotationContainer.hasWasmNoOpCastAnnotation(): Boolean =
fun IrAnnotationContainer.hasWasmAutoboxedAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.WasmAutoboxed"))
fun IrAnnotationContainer.hasWasmPrimitiveConstructorAnnotation(): Boolean =
hasAnnotation(FqName("kotlin.wasm.internal.WasmPrimitiveConstructor"))
class WasmArrayInfo(val klass: IrClass, val isNullable: Boolean) {
val type = klass.defaultType.let { if (isNullable) it.makeNullable() else it }
}
-3
View File
@@ -1,6 +1,3 @@
// IGNORE_BACKEND: WASM
// WASM_MUTE_REASON: MINOR: CONST_EQUIVALENCE
object A {
const val a: String = "$"
const val b = "1234$a"
@@ -1,4 +1,3 @@
// IGNORE_BACKEND: WASM
// FILE: 1.kt
package test
+1 -1
View File
@@ -12,7 +12,7 @@ import kotlin.random.*
/**
* The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass.
*/
public open class Any {
public open class Any @WasmPrimitiveConstructor constructor() {
// Pointer to runtime type info
// Initialized by a compiler
@Suppress("MUST_BE_INITIALIZED_OR_BE_ABSTRACT")
@@ -102,8 +102,11 @@ public class Char private constructor(public val value: Char) : Comparable<Char>
public inline fun toDouble(): Double =
this.toInt().toDouble()
override fun toString(): String =
String(WasmCharArray(1).also { it.set(0, this) })
override fun toString(): String {
val array = WasmCharArray(1)
array.set(0, this)
return array.createString()
}
override fun hashCode(): Int =
this.toInt().hashCode()
+57 -50
View File
@@ -12,30 +12,19 @@ import kotlin.math.min
* The `String` class represents character strings. All string literals in Kotlin programs, such as `"abc"`, are
* implemented as instances of this class.
*/
public class String private constructor(
public class String internal @WasmPrimitiveConstructor constructor(
private var leftIfInSum: String?,
public override val length: Int,
private var _chars: WasmCharArray,
) : Comparable<String>, CharSequence {
public companion object {}
internal constructor(chars: WasmCharArray) : this(null, chars) { }
/**
* 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 right = if (other is String) other else other.toString()
return String(this, right.chars)
}
public override val length: Int get() {
var currentLeftString = leftIfInSum
var currentLength = _chars.len()
while (currentLeftString != null) {
currentLength += currentLeftString._chars.len()
currentLeftString = currentLeftString.leftIfInSum
}
return currentLength
val right = other.toString()
return String(this, this.length + right.length, right.chars)
}
/**
@@ -45,11 +34,8 @@ public class String private constructor(
* where the behavior is unspecified.
*/
public override fun get(index: Int): Char {
if (index < 0) throw IndexOutOfBoundsException()
val folded = chars
val length = folded.len()
if (index >= length) throw IndexOutOfBoundsException()
return folded.get(index)
rangeCheck(index, this.length)
return chars.get(index)
}
internal fun foldChars() {
@@ -79,59 +65,80 @@ public class String private constructor(
public override fun subSequence(startIndex: Int, endIndex: Int): CharSequence {
val actualStartIndex = startIndex.coerceAtLeast(0)
val thisChars = chars
val actualEndIndex = endIndex.coerceAtMost(thisChars.len())
val newCharsLen = actualEndIndex - actualStartIndex
val newChars = WasmCharArray(newCharsLen)
copyWasmArray(thisChars, newChars, actualStartIndex, 0, newCharsLen)
return String(newChars)
val actualEndIndex = endIndex.coerceAtMost(this.length)
val newLength = actualEndIndex - actualStartIndex
if (newLength <= 0) return ""
val newChars = WasmCharArray(newLength)
copyWasmArray(chars, newChars, actualStartIndex, 0, newLength)
return newChars.createString()
}
public override fun compareTo(other: String): Int {
if (this === other) return 0
val thisChars = this.chars
val otherChars = other.chars
val thisLength = thisChars.len()
val otherLength = otherChars.len()
val len = min(thisLength, otherLength)
val minimumLength = if (thisLength < otherLength) thisLength else otherLength
for (i in 0 until len) {
val l = thisChars.get(i)
val r = otherChars.get(i)
if (l != r)
return l - r
repeat(minimumLength) {
val l = thisChars.get(it)
val r = otherChars.get(it)
if (l != r) return l - r
}
return thisLength - otherLength
}
public override fun equals(other: Any?): Boolean =
other != null &&
other is String &&
(this.length == other.length) &&
this.compareTo(other) == 0
public override fun equals(other: Any?): Boolean {
if (other == null) return false
if (other === this) return true
val otherString = other as? String ?: return false
val thisLength = this.length
val otherLength = otherString.length
if (thisLength != otherLength) return false
val thisHash = this._hashCode
val otherHash = other._hashCode
if (thisHash != otherHash && thisHash != 0 && otherHash != 0) return false
val thisChars = this.chars
val otherChars = other.chars
repeat(thisLength) {
if (thisChars.get(it) != otherChars.get(it)) return false
}
return true
}
public override fun toString(): String = this
public override fun hashCode(): Int {
if (_hashCode != 0) return _hashCode
val thisChars = chars
val thisLength = thisChars.len()
val thisLength = this.length
if (thisLength == 0) return 0
val thisChars = chars
var hash = 0
var i = 0
while (i < thisLength) {
hash = 31 * hash + thisChars.get(i).toInt()
i++
repeat(thisLength) {
hash = (hash shl 5) - hash + thisChars.get(it).toInt()
}
_hashCode = hash
return _hashCode
}
}
internal fun stringLiteral(startAddr: Int, length: Int): String {
val array = WasmCharArray(length)
unsafeRawMemoryToWasmCharArray(startAddr, 0, length, array)
return String(array)
}
internal inline fun WasmCharArray.createString(): String =
String(null, this.len(), this)
internal fun stringLiteral(poolId: Int, startAddress: Int, length: Int, hashCode: Int): String {
val cached = stringPool[poolId]
if (cached !== null) {
return cached
}
val chars = WasmCharArray(length)
unsafeRawMemoryToWasmCharArray(startAddress, 0, length, chars)
val newString = String(null, length, chars)
newString._hashCode = hashCode
stringPool[poolId] = newString
return newString
}
@@ -9,7 +9,7 @@ import kotlin.wasm.internal.reftypes.anyref
internal external interface ExternalInterfaceType
internal class JsExternalBox(val ref: ExternalInterfaceType) {
internal class JsExternalBox @WasmPrimitiveConstructor constructor(val ref: ExternalInterfaceType) {
override fun toString(): String =
externrefToString(ref)
@@ -20,8 +20,13 @@ internal class JsExternalBox(val ref: ExternalInterfaceType) {
false
}
override fun hashCode(): Int =
externrefHashCode(ref)
override fun hashCode(): Int {
var hashCode = _hashCode
if (hashCode != 0) return hashCode
hashCode = externrefHashCode(ref)
_hashCode = hashCode
return hashCode
}
}
//language=js
@@ -218,7 +223,7 @@ internal fun jsToKotlinStringAdapter(x: ExternalInterfaceType): String {
val stringLength = stringLength(x)
val dstArray = WasmCharArray(stringLength)
if (stringLength == 0) {
return String(dstArray)
return dstArray.createString()
}
val maxStringLength = unsafeGetScratchRawMemorySize() / CHAR_SIZE_BYTES
@@ -233,7 +238,7 @@ internal fun jsToKotlinStringAdapter(x: ExternalInterfaceType): String {
jsExportStringToWasm(x, srcStartIndex, stringLength - srcStartIndex, memBuffer)
unsafeRawMemoryToWasmCharArray(memBuffer, srcStartIndex, stringLength - srcStartIndex, dstArray)
return String(dstArray)
return dstArray.createString()
}
@@ -64,7 +64,7 @@ internal fun itoa32(inputValue: Int, radix: Int): String {
if (sign == 1)
buf.set(0, CharCodes.MINUS.code.toChar())
return String(buf)
return buf.createString()
}
private fun utoaDecSimple(buffer: WasmCharArray, numInput: Int, offsetInput: Int) {
@@ -144,7 +144,7 @@ internal fun itoa64(inputValue: Long, radix: Int): String {
if (sign == 1)
buf.set(0, CharCodes.MINUS.code.toChar())
return String(buf)
return buf.createString()
}
// Count number of decimals for u64 values
@@ -178,7 +178,7 @@ internal fun dtoa(value: Double): String {
val size = dtoaCore(buf, value)
val ret = WasmCharArray(size)
buf.copyInto(ret, 0, 0, size)
return String(ret)
return ret.createString()
}
private fun dtoaCore(buffer: WasmCharArray, valueInp: Double): Int {
@@ -80,4 +80,12 @@ internal class Void private constructor()
// This is the only way to introduce Void type.
@WasmOp(WasmOp.DROP)
internal fun consumeAnyIntoVoid(a: Any?): Void =
implementedAsIntrinsic
implementedAsIntrinsic
@ExcludedFromCodegen
internal fun stringGetPoolSize(): Int =
implementedAsIntrinsic
// This initializer is a special case in FieldInitializersLowering
@EagerInitialization
internal val stringPool: Array<String?> = arrayOfNulls(stringGetPoolSize())
@@ -10,9 +10,11 @@ package kotlin.wasm.internal
internal const val TYPE_INFO_ELEMENT_SIZE = 4
internal const val TYPE_INFO_TYPE_PACKAGE_NAME_LENGTH_OFFSET = 0
internal const val TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET = TYPE_INFO_TYPE_PACKAGE_NAME_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_TYPE_PACKAGE_NAME_ID_OFFSET = TYPE_INFO_TYPE_PACKAGE_NAME_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET = TYPE_INFO_TYPE_PACKAGE_NAME_ID_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET = TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_TYPE_SIMPLE_NAME_ID_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_ID_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_SUPER_TYPE_OFFSET = TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_ITABLE_SIZE_OFFSET = TYPE_INFO_SUPER_TYPE_OFFSET + TYPE_INFO_ELEMENT_SIZE
internal const val TYPE_INFO_ITABLE_OFFSET = TYPE_INFO_ITABLE_SIZE_OFFSET + TYPE_INFO_ELEMENT_SIZE
@@ -21,11 +23,13 @@ internal class TypeInfoData(val typeId: Int, val isInterface: Boolean, val packa
internal fun getTypeInfoTypeDataByPtr(typeInfoPtr: Int): TypeInfoData {
val fqNameLength = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_PACKAGE_NAME_LENGTH_OFFSET)
val fqNameLengthPtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET)
val fqNameId = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_PACKAGE_NAME_ID_OFFSET)
val fqNamePtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_PACKAGE_NAME_PRT_OFFSET)
val simpleNameLength = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_LENGTH_OFFSET)
val simpleNameId = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_ID_OFFSET)
val simpleNamePtr = wasm_i32_load(typeInfoPtr + TYPE_INFO_TYPE_SIMPLE_NAME_PRT_OFFSET)
val packageName = stringLiteral(fqNameLengthPtr, fqNameLength)
val simpleName = stringLiteral(simpleNamePtr, simpleNameLength)
val packageName = stringLiteral(fqNameId, fqNamePtr, fqNameLength, 0)
val simpleName = stringLiteral(simpleNameId, simpleNamePtr, simpleNameLength, 0)
return TypeInfoData(typeInfoPtr, isInterface = false, packageName, simpleName)
}
@@ -48,4 +48,13 @@ internal annotation class WasmAutoboxed
@ExcludedFromCodegen
internal val implementedAsIntrinsic: Nothing
get() = null!!
get() = null!!
/**
* Indicates that annotated constructor is primitive
* i.e. has direct layout from it's parameters to object fields (except any's fields) and has no code
* In this case no need to call it during an object creation.
*/
@Target(AnnotationTarget.CONSTRUCTOR)
@Retention(AnnotationRetention.BINARY)
internal annotation class WasmPrimitiveConstructor
@@ -15,7 +15,7 @@ internal fun insertString(array: CharArray, destinationIndex: Int, value: String
internal fun unsafeStringFromCharArray(array: CharArray, start: Int, size: Int): String {
val copy = WasmCharArray(size)
copyWasmArray(array.storage, copy, start, 0, size)
return String(copy)
return copy.createString()
}
internal fun insertInt(array: CharArray, start: Int, value: Int): Int {
@@ -75,7 +75,7 @@ public actual fun String(chars: CharArray, offset: Int, length: Int): String {
val copy = WasmCharArray(length)
copyWasmArray(chars.storage, copy, offset, 0, length)
return String(copy)
return copy.createString()
}
/**
@@ -88,7 +88,7 @@ public actual fun CharArray.concatToString(): String {
val thisLength = thisStorage.len()
val copy = WasmCharArray(thisLength)
copyWasmArray(this.storage, copy, 0, 0, thisLength)
return String(copy)
return copy.createString()
}
/**
@@ -109,7 +109,7 @@ public actual fun CharArray.concatToString(startIndex: Int = 0, endIndex: Int =
val length = endIndex - startIndex
val copy = WasmCharArray(length)
copyWasmArray(this.storage, copy, startIndex, 0, length)
return String(copy)
return copy.createString()
}
/**