[Wasm] Make Any hashCode and typeId fields generated by compiler. Replace property access with intrinsic functions. This makes them consistent with other compiler-generated fields, like v-table. Compiler treats internal visibility as possibly-public by friends and generates fake-override for get/set accessors in **every** subclass of Any, which causes unnecessary noise in IR and tools working with API visibility.
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 0d8c632..69c50e8 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
@@ -230,6 +230,9 @@ val startUnitTests = maybeGetFunction("startUnitTests", kotlinTestPackage) val wasmTypeId = getInternalFunction("wasmTypeId") + val wasmGetTypeIdField = getInternalFunction("wasmGetTypeIdField") + val wasmGetHashCodeField = getInternalFunction("wasmGetHashCodeField") + val wasmSetHashCodeField = getInternalFunction("wasmSetHashCodeField") val wasmIsInterface = getInternalFunction("wasmIsInterface")
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 f5d2cd2..b1ce226 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
@@ -338,10 +338,8 @@ body.buildIf("this_init") generateAnyParameters(parentClass.symbol, location) val irFields: List<IrField> = parentClass.allFields(backendContext.irBuiltIns) - irFields.forEachIndexed { index, field -> - if (index > 1) { - generateDefaultInitializerForType(context.transformType(field.type), body) - } + irFields.forEach { field -> + generateDefaultInitializerForType(context.transformType(field.type), body) } body.buildStructNew(context.referenceGcType(parentClass.symbol), location) body.buildSetLocal(thisParameter, location) @@ -435,7 +433,7 @@ //TODO: check why it could be needed generateRefCast(receiver.type, klass.defaultType, location) - body.buildStructGet(context.referenceGcType(klass.symbol), WasmSymbol(0), location) + body.buildStructGet(context.referenceGcType(klass.symbol), WasmSymbol(WasmStructLayout.VTABLE_FIELD_INDEX), location) body.buildStructGet(context.referenceVTableGcType(klass.symbol), WasmSymbol(vfSlot), location) body.buildInstr(WasmOp.CALL_REF, location, WasmImmediate.TypeIdx(context.referenceFunctionType(function.symbol))) body.commentGroupEnd() @@ -445,7 +443,7 @@ generateExpression(call.dispatchReceiver!!) body.commentGroupStart { "interface call: ${function.fqNameWhenAvailable}" } - body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1), location) + body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(WasmStructLayout.ITABLE_FIELD_INDEX), location) val classITableReference = context.referenceClassITableGcType(symbol) body.buildRefCastStatic(classITableReference, location) @@ -527,6 +525,18 @@ val location = call.getSourceLocation() when (function.symbol) { + wasmSymbols.wasmGetTypeIdField -> { + body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(WasmStructLayout.TYPE_ID_FIELD_INDEX), location) + } + + wasmSymbols.wasmGetHashCodeField -> { + body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(WasmStructLayout.HASH_CODE_FIELD_INDEX), location) + } + + wasmSymbols.wasmSetHashCodeField -> { + body.buildStructSet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(WasmStructLayout.HASH_CODE_FIELD_INDEX), location) + } + wasmSymbols.wasmTypeId -> { val klass = call.getTypeArgument(0)!!.getClass() ?: error("No class given for wasmTypeId intrinsic") @@ -544,7 +554,7 @@ body.buildBlock("isInterface", WasmI32) { outerLabel -> body.buildBlock("isInterface", WasmRefNullType(WasmHeapType.Simple.Struct)) { innerLabel -> body.buildGetLocal(parameterLocal, location) - body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(1), location) + body.buildStructGet(context.referenceGcType(irBuiltIns.anyClass), WasmSymbol(WasmStructLayout.ITABLE_FIELD_INDEX), location) body.buildBrOnCastInstr( WasmOp.BR_ON_CAST_FAIL,
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt index ce66cdb..ec4fa47 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/DeclarationGenerator.kt
@@ -377,6 +377,9 @@ val fields = mutableListOf<WasmStructFieldDeclaration>() fields.add(WasmStructFieldDeclaration("vtable", vtableRefGcType, false)) fields.add(WasmStructFieldDeclaration("itable", classITableRefGcType, false)) + fields.add(WasmStructFieldDeclaration("typeId", WasmI32, false)) + fields.add(WasmStructFieldDeclaration("_hashCode", WasmI32, true)) + declaration.allFields(irBuiltIns).mapTo(fields) { WasmStructFieldDeclaration( name = it.name.toString(),
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt index 7e9e84e..f9ded86 100644 --- a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmModuleCodegenContext.kt
@@ -183,7 +183,7 @@ fun getStructFieldRef(field: IrField): WasmSymbol<Int> { val klass = field.parentAsClass val metadata = getClassMetadata(klass.symbol) - val fieldId = metadata.fields.indexOf(field) + 2 //Implicit vtable and vtable field + val fieldId = metadata.fields.indexOf(field) + WasmStructLayout.NUMBER_OF_IMPLICIT_FIELDS return WasmSymbol(fieldId) }
diff --git a/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmStructLayout.kt b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmStructLayout.kt new file mode 100644 index 0000000..ab9f482 --- /dev/null +++ b/compiler/ir/backend.wasm/src/org/jetbrains/kotlin/backend/wasm/ir2wasm/WasmStructLayout.kt
@@ -0,0 +1,15 @@ +/* + * Copyright 2010-2023 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 org.jetbrains.kotlin.backend.wasm.ir2wasm + +object WasmStructLayout { + const val VTABLE_FIELD_INDEX = 0 // GC reference + const val ITABLE_FIELD_INDEX = 1 // GC reference + const val TYPE_ID_FIELD_INDEX = 2 // i32 + const val HASH_CODE_FIELD_INDEX = 3 // i32 + + const val NUMBER_OF_IMPLICIT_FIELDS = 4 +} \ No newline at end of file
diff --git a/libraries/stdlib/wasm/builtins/kotlin/Any.kt b/libraries/stdlib/wasm/builtins/kotlin/Any.kt index c756874..3c35d41 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/Any.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/Any.kt
@@ -13,11 +13,6 @@ * The root of the Kotlin class hierarchy. Every Kotlin class has [Any] as a superclass. */ public open class Any @WasmPrimitiveConstructor constructor() { - // Pointer to runtime type info - // Initialized by a compiler - @Suppress("MUST_BE_INITIALIZED_OR_BE_ABSTRACT") - internal var typeInfo: Int - /** * Indicates whether some other object is "equal to" this one. Implementations must fulfil the following * requirements: @@ -39,7 +34,6 @@ * * Whenever it is invoked on the same object more than once, the `hashCode` method must consistently return the same integer, provided no information used in `equals` comparisons on the object is modified. * * If two objects are equal according to the `equals()` method, then calling the `hashCode` method on each of the two objects must produce the same integer result. */ - internal var _hashCode: Int = 0 public open fun hashCode(): Int { return identityHashCode() } @@ -48,7 +42,7 @@ * Returns a string representation of the object. */ public open fun toString(): String { - val typeInfoPtr = this.typeInfo + val typeInfoPtr = wasmGetTypeIdField(this) val packageName = getPackageName(typeInfoPtr) val simpleName = getSimpleName(typeInfoPtr) val qualifiedName = if (packageName.isEmpty()) simpleName else "$packageName.$simpleName" @@ -60,7 +54,8 @@ // Don't inline it into usages, specifically to `hashCode`. // It was extracted to remove `toString`'s dependency on `hashCode`, which improves output size when DCE is involved. private fun Any.identityHashCode(): Int { - if (_hashCode == 0) - _hashCode = Random.nextInt(1, Int.MAX_VALUE) - return _hashCode + if (wasmGetHashCodeField(this) == 0) { + wasmSetHashCodeField(this, Random.nextInt(1, Int.MAX_VALUE)) + } + return wasmGetHashCodeField(this) }
diff --git a/libraries/stdlib/wasm/builtins/kotlin/String.kt b/libraries/stdlib/wasm/builtins/kotlin/String.kt index 91df1ad..7e61714 100644 --- a/libraries/stdlib/wasm/builtins/kotlin/String.kt +++ b/libraries/stdlib/wasm/builtins/kotlin/String.kt
@@ -103,8 +103,8 @@ val otherLength = otherString.length if (thisLength != otherLength) return false - val thisHash = this._hashCode - val otherHash = other._hashCode + val thisHash = wasmGetHashCodeField(this) + val otherHash = wasmGetHashCodeField(other) if (thisHash != otherHash && thisHash != 0 && otherHash != 0) return false val thisChars = this.chars @@ -119,7 +119,7 @@ public override fun toString(): String = this public override fun hashCode(): Int { - if (_hashCode != 0) return _hashCode + if (wasmGetHashCodeField(this) != 0) return wasmGetHashCodeField(this) val thisLength = this.length if (thisLength == 0) return 0 @@ -128,8 +128,8 @@ repeat(thisLength) { hash = (hash shl 5) - hash + thisChars.get(it).code } - _hashCode = hash - return _hashCode + wasmSetHashCodeField(this, hash) + return wasmGetHashCodeField(this) } }
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt index 227cb12..929b28c 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/Runtime.kt
@@ -7,6 +7,21 @@ package kotlin.wasm.internal +/** Set hidden built-in hashCode field */ +@ExcludedFromCodegen +internal fun wasmSetHashCodeField(obj: Any, value: Int): Unit = + implementedAsIntrinsic + +/** Get hidden built-in hashCode field */ +@ExcludedFromCodegen +internal fun wasmGetHashCodeField(obj: Any): Int = + implementedAsIntrinsic + +/** Get hidden built-in typeID field */ +@ExcludedFromCodegen +internal fun wasmGetTypeIdField(obj: Any): Int = + implementedAsIntrinsic + internal const val CHAR_SIZE_BYTES = 2 internal fun unsafeRawMemoryToWasmCharArray(srcAddr: Int, dstOffset: Int, dstLength: Int, dst: WasmCharArray) {
diff --git a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt index a093b14..2feff1c 100644 --- a/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt +++ b/libraries/stdlib/wasm/internal/kotlin/wasm/internal/TypeInfo.kt
@@ -55,8 +55,8 @@ wasm_i32_load(typeInfoPtr + TYPE_INFO_SUPER_TYPE_OFFSET) internal fun isInterfaceById(obj: Any, interfaceId: Int): Boolean { - val interfaceListSize = wasm_i32_load(obj.typeInfo + TYPE_INFO_ITABLE_SIZE_OFFSET) - val interfaceListPtr = obj.typeInfo + TYPE_INFO_ITABLE_OFFSET + val interfaceListSize = wasm_i32_load(wasmGetTypeIdField(obj) + TYPE_INFO_ITABLE_SIZE_OFFSET) + val interfaceListPtr = wasmGetTypeIdField(obj) + TYPE_INFO_ITABLE_OFFSET var interfaceSlot = 0 while (interfaceSlot < interfaceListSize) {
diff --git a/libraries/stdlib/wasm/js/builtins/kotlin/Throwable.kt b/libraries/stdlib/wasm/js/builtins/kotlin/Throwable.kt index 31a8597..49f03d0 100644 --- a/libraries/stdlib/wasm/js/builtins/kotlin/Throwable.kt +++ b/libraries/stdlib/wasm/js/builtins/kotlin/Throwable.kt
@@ -8,6 +8,7 @@ import kotlin.wasm.internal.ExternalInterfaceType import kotlin.wasm.internal.getSimpleName import kotlin.wasm.internal.jsToKotlinStringAdapter +import kotlin.wasm.internal.wasmGetTypeIdField /** * The base class for all errors and exceptions. Only instances of this class can be thrown or caught. @@ -43,7 +44,7 @@ * followed by the exception message if it is not null. */ public override fun toString(): String { - val s = getSimpleName(this.typeInfo) + val s = getSimpleName(wasmGetTypeIdField(this)) return if (message != null) s + ": " + message.toString() else s } }
diff --git a/libraries/stdlib/wasm/js/internal/ExceptionHelpers.kt b/libraries/stdlib/wasm/js/internal/ExceptionHelpers.kt index 241d9a2..c5a676f 100644 --- a/libraries/stdlib/wasm/js/internal/ExceptionHelpers.kt +++ b/libraries/stdlib/wasm/js/internal/ExceptionHelpers.kt
@@ -17,5 +17,5 @@ } internal fun throwAsJsException(t: Throwable): Nothing { - throwJsError(t.message, getSimpleName(t.typeInfo), t.jsStack) + throwJsError(t.message, getSimpleName(wasmGetTypeIdField(t)), t.jsStack) } \ No newline at end of file
diff --git a/libraries/stdlib/wasm/js/internal/ExternalWrapper.kt b/libraries/stdlib/wasm/js/internal/ExternalWrapper.kt index 85002f1..e0d3a9c 100644 --- a/libraries/stdlib/wasm/js/internal/ExternalWrapper.kt +++ b/libraries/stdlib/wasm/js/internal/ExternalWrapper.kt
@@ -25,10 +25,10 @@ } override fun hashCode(): Int { - var hashCode = _hashCode + var hashCode = wasmGetHashCodeField(this) if (hashCode != 0) return hashCode hashCode = externrefHashCode(ref) - _hashCode = hashCode + wasmSetHashCodeField(this, hashCode) return hashCode } }
diff --git a/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt b/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt index 593800e..f78b572 100644 --- a/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt +++ b/libraries/stdlib/wasm/src/kotlin/reflect/KClassImpl.kt
@@ -5,6 +5,7 @@ package kotlin.reflect.wasm.internal import kotlin.reflect.* +import kotlin.wasm.internal.* import kotlin.wasm.internal.TypeInfoData import kotlin.wasm.internal.getSuperTypeId import kotlin.wasm.internal.isInterfaceById @@ -36,7 +37,7 @@ if (typeData.packageName.isEmpty()) typeData.typeName else "${typeData.packageName}.${typeData.typeName}" private fun checkSuperTypeInstance(obj: Any): Boolean { - var typeId = obj.typeInfo + var typeId = wasmGetTypeIdField(obj) while (typeId != -1) { if (typeData.typeId == typeId) return true typeId = getSuperTypeId(typeId)
diff --git a/libraries/stdlib/wasm/src/kotlin/reflect/reflection.kt b/libraries/stdlib/wasm/src/kotlin/reflect/reflection.kt index 5d1ebd6..e101313 100644 --- a/libraries/stdlib/wasm/src/kotlin/reflect/reflection.kt +++ b/libraries/stdlib/wasm/src/kotlin/reflect/reflection.kt
@@ -34,7 +34,7 @@ is DoubleArray -> PrimitiveClasses.doubleArrayClass is KClass<*> -> KClass::class is Array<*> -> PrimitiveClasses.arrayClass - else -> getKClass(getTypeInfoTypeDataByPtr(e.typeInfo)) + else -> getKClass(getTypeInfoTypeDataByPtr(wasmGetTypeIdField(e))) } as KClass<T> @Suppress("REIFIED_TYPE_PARAMETER_NO_INLINE")
diff --git a/libraries/stdlib/wasm/wasi/builtins/kotlin/Throwable.kt b/libraries/stdlib/wasm/wasi/builtins/kotlin/Throwable.kt index baddf1a..b56c6aa 100644 --- a/libraries/stdlib/wasm/wasi/builtins/kotlin/Throwable.kt +++ b/libraries/stdlib/wasm/wasi/builtins/kotlin/Throwable.kt
@@ -6,6 +6,7 @@ package kotlin import kotlin.wasm.internal.getSimpleName +import kotlin.wasm.internal.wasmGetTypeIdField /** * The base class for all errors and exceptions. Only instances of this class can be thrown or caught. @@ -30,7 +31,7 @@ * followed by the exception message if it is not null. */ public override fun toString(): String { - val s = getSimpleName(this.typeInfo) + val s = getSimpleName(wasmGetTypeIdField(this)) return if (message != null) s + ": " + message.toString() else s } } \ No newline at end of file