[K/Wasm] Allow to export unsigned numbers

This commit is contained in:
Artem Kobzar
2023-12-08 09:06:19 +00:00
committed by Space Team
parent a3c1f4ea12
commit 2eb1e65bbf
19 changed files with 528 additions and 24 deletions
@@ -72,5 +72,5 @@ private fun isTypeSupportedInWasmInterop(
}
// Primitive numbers and Boolean are supported
return type.isPrimitive && !type.isChar
return (type.isPrimitive && !type.isChar) || type.isUnsignedType
}
@@ -122,7 +122,7 @@ private fun isTypeSupportedInJsInterop(
val nonNullable = type.withNullability(ConeNullability.NOT_NULL, session.typeContext)
if (nonNullable.isPrimitive || nonNullable.isString) {
if (nonNullable.isPrimitive || nonNullable.isUnsignedType || nonNullable.isString) {
return true
}
@@ -133,6 +133,12 @@ class WasmSymbols(
val voidType by lazy { voidClass.defaultType }
private val consumeAnyIntoVoid = getInternalFunction("consumeAnyIntoVoid")
val uByteType by lazy { getIrClass(FqName("kotlin.UByte")).defaultType }
val uShortType by lazy { getIrClass(FqName("kotlin.UShort")).defaultType }
val uIntType by lazy { getIrClass(FqName("kotlin.UInt")).defaultType }
val uLongType by lazy { getIrClass(FqName("kotlin.ULong")).defaultType }
private val consumePrimitiveIntoVoid = mapOf(
context.irBuiltIns.booleanType to getInternalFunction("consumeBooleanIntoVoid"),
context.irBuiltIns.byteType to getInternalFunction("consumeByteIntoVoid"),
@@ -325,6 +331,11 @@ class WasmSymbols(
val externRefToKotlinFloatAdapter = getInternalFunction("externRefToKotlinFloatAdapter")
val externRefToKotlinDoubleAdapter = getInternalFunction("externRefToKotlinDoubleAdapter")
val externRefToKotlinUByteAdapter = getInternalFunction("externRefToKotlinUByteAdapter")
val externRefToKotlinUShortAdapter = getInternalFunction("externRefToKotlinUShortAdapter")
val externRefToKotlinUIntAdapter = getInternalFunction("externRefToKotlinUIntAdapter")
val externRefToKotlinULongAdapter = getInternalFunction("externRefToKotlinULongAdapter")
val kotlinIntToExternRefAdapter = getInternalFunction("kotlinIntToExternRefAdapter")
val kotlinBooleanToExternRefAdapter = getInternalFunction("kotlinBooleanToExternRefAdapter")
val kotlinLongToExternRefAdapter = getInternalFunction("kotlinLongToExternRefAdapter")
@@ -333,6 +344,11 @@ class WasmSymbols(
val kotlinByteToExternRefAdapter = getInternalFunction("kotlinByteToExternRefAdapter")
val kotlinShortToExternRefAdapter = getInternalFunction("kotlinShortToExternRefAdapter")
val kotlinCharToExternRefAdapter = getInternalFunction("kotlinCharToExternRefAdapter")
val kotlinUByteToJsNumber = getInternalFunction("kotlinUByteToJsNumber")
val kotlinUShortToJsNumber = getInternalFunction("kotlinUShortToJsNumber")
val kotlinUIntToJsNumber = getInternalFunction("kotlinUIntToJsNumber")
val kotlinULongToJsBigInt = getInternalFunction("kotlinULongToJsBigInt")
}
inner class JsRelatedSymbols {
@@ -210,13 +210,19 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
val primitivesToExternRefAdapters: Map<IrType, InteropTypeAdapter> by lazy {
mapOf(
builtIns.byteType to adapters.kotlinByteToExternRefAdapter,
symbols.uByteType to adapters.kotlinUByteToJsNumber,
builtIns.shortType to adapters.kotlinShortToExternRefAdapter,
symbols.uShortType to adapters.kotlinUShortToJsNumber,
builtIns.charType to adapters.kotlinCharToExternRefAdapter,
builtIns.intType to adapters.kotlinIntToExternRefAdapter,
symbols.uIntType to adapters.kotlinUIntToJsNumber,
builtIns.longType to adapters.kotlinLongToExternRefAdapter,
symbols.uLongType to adapters.kotlinULongToJsBigInt,
builtIns.floatType to adapters.kotlinFloatToExternRefAdapter,
builtIns.doubleType to adapters.kotlinDoubleToExternRefAdapter,
).mapValues { FunctionBasedAdapter(it.value.owner) }
).mapValues {
FunctionBasedAdapter(it.value.owner)
}
}
private fun IrType.kotlinToJsAdapterIfNeeded(isReturn: Boolean): InteropTypeAdapter? {
@@ -263,6 +269,11 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
builtIns.anyType -> return FunctionBasedAdapter(adapters.kotlinToJsAnyAdapter.owner)
builtIns.numberType -> return FunctionBasedAdapter(adapters.numberToDoubleAdapter.owner)
symbols.uByteType -> return FunctionBasedAdapter(adapters.kotlinUByteToJsNumber.owner)
symbols.uShortType -> return FunctionBasedAdapter(adapters.kotlinUShortToJsNumber.owner)
symbols.uIntType -> return FunctionBasedAdapter(adapters.kotlinUIntToJsNumber.owner)
symbols.uLongType -> return FunctionBasedAdapter(adapters.kotlinULongToJsBigInt.owner)
builtIns.byteType,
builtIns.shortType,
builtIns.charType,
@@ -320,16 +331,28 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
return SendKotlinObjectToJsAdapter(this)
}
private fun createNullableAdapter(notNullType: IrType, isPrimitive: Boolean, valueAdapter: InteropTypeAdapter?): InteropTypeAdapter? {
return if (isPrimitive) { //nullable primitive should be checked and adapt to target type
private fun createNullableAdapter(
notNullType: IrType,
isPrimitiveOrUnsigned: Boolean,
valueAdapter: InteropTypeAdapter?
): InteropTypeAdapter? {
return if (isPrimitiveOrUnsigned) { //nullable primitive should be checked and adapt to target type
val externRefToPrimitiveAdapter = when (notNullType) {
builtIns.floatType -> adapters.externRefToKotlinFloatAdapter.owner
builtIns.doubleType -> adapters.externRefToKotlinDoubleAdapter.owner
builtIns.longType -> adapters.externRefToKotlinLongAdapter.owner
builtIns.booleanType -> adapters.externRefToKotlinBooleanAdapter.owner
symbols.uByteType -> adapters.externRefToKotlinUByteAdapter.owner
symbols.uShortType -> adapters.externRefToKotlinUShortAdapter.owner
symbols.uIntType -> adapters.externRefToKotlinUIntAdapter.owner
symbols.uLongType -> adapters.externRefToKotlinULongAdapter.owner
else -> adapters.externRefToKotlinIntAdapter.owner
}
val externalToPrimitiveAdapter = FunctionBasedAdapter(externRefToPrimitiveAdapter)
NullOrAdapter(
adapter = valueAdapter?.let { CombineAdapter(it, externalToPrimitiveAdapter) } ?: externalToPrimitiveAdapter
)
@@ -342,9 +365,13 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
}
}
private fun createNotNullAdapter(notNullType: IrType, isPrimitive: Boolean, valueAdapter: InteropTypeAdapter?): InteropTypeAdapter? {
private fun createNotNullAdapter(
notNullType: IrType,
isPrimitiveOrUnsigned: Boolean,
valueAdapter: InteropTypeAdapter?
): InteropTypeAdapter? {
// !nullable primitive checked by wasm signature
if (isPrimitive) return valueAdapter
if (isPrimitiveOrUnsigned) return valueAdapter
// !nullable reference should be null checked
// notNullAdapter((undefined -> null)!!)
@@ -367,12 +394,12 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
val notNullType = makeNotNull()
val valueAdapter = notNullType.jsToKotlinAdapterIfNeededNotNullable(isReturn)
val isPrimitive = valueAdapter?.fromType?.isPrimitiveType() ?: notNullType.isPrimitiveType()
val isPrimitiveOrUnsigned = (valueAdapter?.fromType ?: notNullType).let { it.isPrimitiveType() || it.isUnsigned() }
return if (isNullable())
createNullableAdapter(notNullType, isPrimitive, valueAdapter)
createNullableAdapter(notNullType, isPrimitiveOrUnsigned, valueAdapter)
else
createNotNullAdapter(notNullType, isPrimitive, valueAdapter)
createNotNullAdapter(notNullType, isPrimitiveOrUnsigned, valueAdapter)
}
private fun IrType.jsToKotlinAdapterIfNeededNotNullable(isReturn: Boolean): InteropTypeAdapter? {
@@ -386,12 +413,16 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
builtIns.shortType -> return FunctionBasedAdapter(adapters.jsToKotlinShortAdapter.owner)
builtIns.charType -> return FunctionBasedAdapter(adapters.jsToKotlinCharAdapter.owner)
symbols.uByteType,
symbols.uShortType,
symbols.uIntType,
symbols.uLongType,
builtIns.booleanType,
builtIns.intType,
builtIns.longType,
builtIns.floatType,
builtIns.doubleType,
context.wasmSymbols.voidType ->
symbols.voidType ->
return null
}
@@ -0,0 +1,150 @@
// TARGET_BACKEND: WASM
// FILE: externals.js
function provideUByte() { return -1 }
function provideNullableUByte(nullable) { return nullable ? null : - 1 }
function consumeUByte(x) { return x.toString() }
function consumeNullableUByte(x) { return x == null ? null : x.toString() }
function provideUShort() { return -1 }
function provideNullableUShort(nullable) { return nullable ? null : - 1 }
function consumeUShort(x) { return x.toString() }
function consumeNullableUShort(x) { return x == null ? null : x.toString() }
function provideUInt() { return -1 }
function provideNullableUInt(nullable) { return nullable ? null : - 1 }
function consumeUInt(x) { return x.toString() }
function consumeNullableUInt(x) { return x == null ? null : x.toString() }
function provideULong() { return -1n }
function provideNullableULong(nullable) { return nullable ? null : - 1n }
function consumeULong(x) { return x.toString() }
function consumeNullableULong(x) { return x == null ? null : x.toString() }
function consumeUByteVararg(x) { return x.toString() }
function consumeNullableUByteVararg(x) { return x == null ? null : x.toString() }
function consumeUShortVararg(x) { return x.toString() }
function consumeNullableUShortVararg(x) { return x == null ? null : x.toString() }
function consumeUIntVararg(x) { return x.toString() }
function consumeNullableUIntVararg(x) { return x == null ? null : x.toString() }
function consumeULongVararg(x) { return x.toString() }
function consumeNullableULongVararg(x) { return x == null ? null : x.toString() }
// FILE: externals.kt
external fun provideUByte(): UByte
external fun provideNullableUByte(nullable: Boolean): UByte?
external fun consumeUByte(x: UByte): String
external fun consumeNullableUByte(x: UByte?): String?
external fun provideUShort(): UShort
external fun provideNullableUShort(nullable: Boolean): UShort?
external fun consumeUShort(x: UShort): String
external fun consumeNullableUShort(x: UShort?): String?
external fun provideUInt(): UInt
external fun provideNullableUInt(nullable: Boolean): UInt?
external fun consumeUInt(x: UInt): String
external fun consumeNullableUInt(x: UInt?): String?
external fun provideULong(): ULong
external fun provideNullableULong(nullable: Boolean): ULong?
external fun consumeULong(x: ULong): String
external fun consumeNullableULong(x: ULong?): String?
external fun consumeUByteVararg(vararg shorts: UByte): String
external fun consumeNullableUByteVararg(vararg shorts: UByte?): String?
external fun consumeUShortVararg(vararg shorts: UShort): String
external fun consumeNullableUShortVararg(vararg shorts: UShort?): String?
external fun consumeUIntVararg(vararg ints: UInt): String
external fun consumeNullableUIntVararg(vararg ints: UInt?): String?
external fun consumeULongVararg(vararg ints: ULong): String
external fun consumeNullableULongVararg(vararg ints: ULong?): String?
fun box(): String {
if (provideUByte() != UByte.MAX_VALUE) return "Fail 1"
if (provideNullableUByte(false) != UByte.MAX_VALUE) return "Fail 2"
if (provideNullableUByte(true) != null) return "Fail 3"
if (provideUShort() != UShort.MAX_VALUE) return "Fail 4"
if (provideNullableUShort(false) != UShort.MAX_VALUE) return "Fail 5"
if (provideNullableUShort(true) != null) return "Fail 6"
if (provideUInt() != UInt.MAX_VALUE) return "Fail 7"
if (provideNullableUInt(false) != UInt.MAX_VALUE) return "Fail 8"
if (provideNullableUInt(true) != null) return "Fail 9"
if (provideULong() != ULong.MAX_VALUE) return "Fail 10"
if (provideNullableULong(false) != ULong.MAX_VALUE) return "Fail 11"
if (provideNullableULong(true) != null) return "Fail 12"
if (consumeUByte(UByte.MAX_VALUE) != "255") return "Fail 13"
if (consumeNullableUByte(UByte.MAX_VALUE) != "255") return "Fail 14"
if (consumeNullableUByte(null) != null) return "Fail 15"
if (consumeUShort(UShort.MAX_VALUE) != "65535") return "Fail 16"
if (consumeNullableUShort(UShort.MAX_VALUE) != "65535") return "Fail 17"
if (consumeNullableUShort(null) != null) return "Fail 18"
if (consumeUInt(UInt.MAX_VALUE) != "4294967295") return "Fail 19"
if (consumeNullableUInt(UInt.MAX_VALUE) != "4294967295") return "Fail 20"
if (consumeNullableUInt(null) != null) return "Fail 21"
if (consumeULong(ULong.MAX_VALUE) != "18446744073709551615") return "Fail 22"
if (consumeNullableULong(ULong.MAX_VALUE) != "18446744073709551615") return "Fail 23"
if (consumeNullableULong(null) != null) return "Fail 24"
if (provideUShort() != UShort.MAX_VALUE) return "Fail 25"
if (provideNullableUShort(false) != UShort.MAX_VALUE) return "Fail 26"
if (provideNullableUShort(true) != null) return "Fail 27"
if (provideUInt() != UInt.MAX_VALUE) return "Fail 28"
if (provideNullableUInt(false) != UInt.MAX_VALUE) return "Fail 29"
if (provideNullableUInt(true) != null) return "Fail 30"
if (provideULong() != ULong.MAX_VALUE) return "Fail 31"
if (provideNullableULong(false) != ULong.MAX_VALUE) return "Fail 32"
if (provideNullableULong(true) != null) return "Fail 33"
if (consumeUByteVararg(UByte.MAX_VALUE) != "255") return "Fail 34"
if (consumeNullableUByteVararg(UByte.MAX_VALUE) != "255") return "Fail 35"
if (consumeNullableUByteVararg(null) != null) return "Fail 36"
if (consumeUShortVararg(UShort.MAX_VALUE) != "65535") return "Fail 37"
if (consumeNullableUShortVararg(UShort.MAX_VALUE) != "65535") return "Fail 38"
if (consumeNullableUShortVararg(null) != null) return "Fail 39"
if (consumeUIntVararg(UInt.MAX_VALUE) != "4294967295") return "Fail 40"
if (consumeNullableUIntVararg(UInt.MAX_VALUE) != "4294967295") return "Fail 41"
if (consumeNullableUIntVararg(null) != null) return "Fail 42"
if (consumeULongVararg(ULong.MAX_VALUE) != "18446744073709551615") return "Fail 43"
if (consumeNullableULongVararg(ULong.MAX_VALUE) != "18446744073709551615") return "Fail 44"
if (consumeNullableULongVararg(null) != null) return "Fail 45"
return "OK"
}
+183 -1
View File
@@ -24,6 +24,54 @@ fun eiAsAny(ei: EI): JsReference<Any> = ei.toJsReference()
@JsExport
fun anyAsEI(any: JsReference<Any>): EI = any.get() as EI
@JsExport
fun provideUByte(): UByte = UByte.MAX_VALUE
@JsExport
fun provideNullableUByte(nullable: Boolean): UByte? = if (nullable) null else UByte.MAX_VALUE
@JsExport
fun consumeUByte(x: UByte) = x.toString()
@JsExport
fun consumeNullableUByte(x: UByte?) = x?.toString()
@JsExport
fun provideUShort(): UShort = UShort.MAX_VALUE
@JsExport
fun provideNullableUShort(nullable: Boolean): UShort? = if (nullable) null else UShort.MAX_VALUE
@JsExport
fun consumeUShort(x: UShort) = x.toString()
@JsExport
fun consumeNullableUShort(x: UShort?) = x?.toString()
@JsExport
fun provideUInt(): UInt = UInt.MAX_VALUE
@JsExport
fun provideNullableUInt(nullable: Boolean): UInt? = if (nullable) null else UInt.MAX_VALUE
@JsExport
fun consumeUInt(x: UInt) = x.toString()
@JsExport
fun consumeNullableUInt(x: UInt?) = x?.toString()
@JsExport
fun provideULong(): ULong = ULong.MAX_VALUE
@JsExport
fun provideNullableULong(nullable: Boolean): ULong? = if (nullable) null else ULong.MAX_VALUE
@JsExport
fun consumeULong(x: ULong) = x.toString()
@JsExport
fun consumeNullableULong(x: ULong?) = x?.toString()
fun box(): String = "OK"
// FILE: entry.mjs
@@ -45,4 +93,138 @@ if (main.isEven(31) !== false || main.isEven(10) !== true) {
if (main.anyAsEI(main.eiAsAny({x:10})).x !== 10) {
throw "Fail 4";
}
}
if (main.provideUByte() != 255) {
throw "Fail 5";
}
if (main.provideUShort() != 65535) {
throw "Fail 6";
}
if (main.provideUInt() != 4294967295) {
throw "Fail 7";
}
if (main.provideULong() != 18446744073709551615n) {
throw "Fail 8";
}
if (main.provideNullableUByte(false) != 255) {
throw "Fail 9";
}
if (main.provideNullableUByte(true) != null) {
throw "Fail 10";
}
if (main.provideNullableUShort(false) != 65535) {
throw "Fail 11";
}
if (main.provideNullableUShort(true) != null) {
throw "Fail 12";
}
if (main.provideNullableUInt(false) != 4294967295) {
throw "Fail 13";
}
if (main.provideNullableUInt(true) != null) {
throw "Fail 14";
}
if (main.provideNullableULong(false) != 18446744073709551615n) {
throw "Fail 15";
}
if (main.provideNullableULong(true) != null) {
throw "Fail 16";
}
if (main.consumeUByte(-1) != "255") {
throw "Fail 17";
}
if (main.consumeNullableUByte(-1) != "255") {
throw "Fail 18";
}
if (main.consumeNullableUByte(null) != null) {
throw "Fail 19";
}
if (main.consumeUShort(-1) != "65535") {
throw "Fail 20";
}
if (main.consumeNullableUShort(-1) != "65535") {
throw "Fail 21";
}
if (main.consumeNullableUShort(null) != null) {
throw "Fail 22";
}
if (main.consumeUInt(-1) != "4294967295") {
throw "Fail 23";
}
if (main.consumeNullableUInt(-1) != "4294967295") {
throw "Fail 24";
}
if (main.consumeNullableUInt(null) != null) {
throw "Fail 25";
}
if (main.consumeULong(-1n) != "18446744073709551615") {
throw "Fail 26";
}
if (main.consumeNullableULong(-1n) != "18446744073709551615") {
throw "Fail 27";
}
if (main.consumeNullableULong(null) != null) {
throw "Fail 28";
}
if (main.consumeUByte(255) != "255") {
throw "Fail 29";
}
if (main.consumeNullableUByte(255) != "255") {
throw "Fail 30";
}
if (main.consumeUShort(65535) != "65535") {
throw "Fail 31";
}
if (main.consumeNullableUShort(65535) != "65535") {
throw "Fail 32";
}
if (main.consumeUInt(4294967295) != "4294967295") {
throw "Fail 33";
}
if (main.consumeNullableUInt(4294967295) != "4294967295") {
throw "Fail 34";
}
if (main.consumeULong(18446744073709551615n) != "18446744073709551615") {
throw "Fail 35";
}
if (main.consumeNullableULong(18446744073709551615n) != "18446744073709551615") {
throw "Fail 36";
}
if (main.consumeUByte(256) != "0") {
throw "Fail 37";
}
if (main.consumeNullableUByte(256) != "0") {
throw "Fail 38";
}
if (main.consumeUShort(65536) != "0") {
throw "Fail 39";
}
if (main.consumeNullableUShort(65536) != "0") {
throw "Fail 40";
}
if (main.consumeUInt(4294967296) != "0") {
throw "Fail 41";
}
if (main.consumeNullableUInt(4294967296) != "0") {
throw "Fail 42";
}
if (main.consumeULong(18446744073709551616n) != "0") {
throw "Fail 43";
}
if (main.consumeNullableULong(18446744073709551616n) != "0") {
throw "Fail 44";
}
+24 -5
View File
@@ -2,18 +2,37 @@
import kotlin.wasm.WasmExport
@WasmExport
fun exportDefaultName(): Boolean = true
fun checkDefaultName(): Boolean = js("typeof wasmExports.exportDefaultName() !== 'object'")
@WasmExport("exportOverriddenName")
fun exportWithName(): Boolean = true
@WasmExport
fun exportDefaultName(): Boolean = true
@WasmExport
fun provideUByte(): UByte = UByte.MAX_VALUE
@WasmExport
fun provideUShort(): UShort = UShort.MAX_VALUE
@WasmExport
fun provideUInt(): UInt = UInt.MAX_VALUE
@WasmExport
fun provideULong(): ULong = ULong.MAX_VALUE
fun checkDefaultName(): Boolean = js("typeof wasmExports.exportDefaultName() !== 'object'")
fun checkOverriddenName(): Boolean = js("typeof wasmExports.exportOverriddenName() !== 'object'")
fun checkProvideUByte(): Boolean = js("wasmExports.provideUByte() === -1")
fun checkProvideUShort(): Boolean = js("wasmExports.provideUShort() === -1")
fun checkProvideUInt(): Boolean = js("wasmExports.provideUInt() === -1")
fun checkProvideULong(): Boolean = js("wasmExports.provideULong() === -1n")
fun box(): String {
if (!checkDefaultName()) return "checkDefaultName fail"
if (!checkOverriddenName()) return "checkOverriddenName fail"
if (!checkProvideUByte()) return "checkProvideUByte fail"
if (!checkProvideUShort()) return "checkProvideUShort fail"
if (!checkProvideUInt()) return "checkProvideUInt fail"
if (!checkProvideULong()) return "checkProvideULong fail"
return "OK"
}
@@ -20,6 +20,16 @@ export { sub as "" }
export { sub as "\n \r \t" }
export default sub;
// FILE: 3.mjs
export function provideUByte() { return -1 }
export function provideUShort() { return -1 }
export function provideUInt() { return -1 }
export function provideULong() { return -1n }
// FILE: wasmImport.kt
import kotlin.wasm.WasmImport
@@ -50,6 +60,18 @@ external fun sub5(x: Float, y: Float): Float
@WasmImport("./2.mjs", "default")
external fun sub6(x: Float, y: Float): Float
@WasmImport("./3.mjs", "provideUByte")
external fun provideUByte(): UByte
@WasmImport("./3.mjs", "provideUShort")
external fun provideUShort(): UShort
@WasmImport("./3.mjs", "provideUInt")
external fun provideUInt(): UInt
@WasmImport("./3.mjs", "provideULong")
external fun provideULong(): ULong
fun box(): String {
if (addImportRenamed(5, 6) != 11) return "Fail1"
if (add(5, 6) != 11) return "Fail1"
@@ -62,5 +84,10 @@ fun box(): String {
if (sub5(5f, 6f) != -1f) return "Fail6"
if (sub6(5f, 6f) != -1f) return "Fail7"
if (provideUByte() != UByte.MAX_VALUE) return "Fail9"
if (provideUShort() != UShort.MAX_VALUE) return "Fail10"
if (provideUInt() != UInt.MAX_VALUE) return "Fail11"
if (provideULong() != ULong.MAX_VALUE) return "Fail12"
return "OK"
}
@@ -24,3 +24,9 @@ class C2
<!WRONG_ANNOTATION_TARGET!>@JsExport<!>
var p2: Int = 1
@JsExport
fun fooUnsigned1(): UInt = 42u
@JsExport
fun fooUnsigned2(): UByte = 42u
@@ -58,3 +58,9 @@ fun fooDeafultAndVararg(
<!WASM_IMPORT_EXPORT_PARAMETER_DEFAULT_VALUE!>a: Int = <!CALL_TO_DEFINED_EXTERNALLY_FROM_NON_EXTERNAL_DECLARATION!>definedExternally<!><!>,
<!WASM_IMPORT_EXPORT_UNSUPPORTED_PARAMETER_TYPE, WASM_IMPORT_EXPORT_VARARG_PARAMETER!>vararg b: Int<!>
): Unit { b.toString() }
@WasmExport("a")
fun fooUnsigned1(): UInt = 42u
@WasmExport()
fun fooUnsigned2(): UByte = 42u
@@ -956,6 +956,10 @@ public abstract class KotlinBuiltIns {
return type != null && isNotNullConstructedFromGivenClass(type, FqNames.string);
}
public static boolean isUnsignedNumber(@Nullable KotlinType type) {
return type != null && (isUByte(type) || isUShort(type) || isUInt(type) || isULong(type));
}
public static boolean isCharSequenceOrNullableCharSequence(@Nullable KotlinType type) {
return type != null && isConstructedFromGivenClass(type, FqNames.charSequence);
}
@@ -27,7 +27,6 @@ object JsPlatformConfigurator : PlatformConfiguratorBase(
JsRuntimeAnnotationChecker,
JsDynamicDeclarationChecker,
JsExportAnnotationChecker,
JsExportDeclarationChecker
),
additionalCallCheckers = listOf(
JsModuleCallChecker,
@@ -52,6 +51,7 @@ object JsPlatformConfigurator : PlatformConfiguratorBase(
container.useInstance(ExtensionFunctionToExternalIsInlinable)
container.useInstance(JsQualifierChecker)
container.useInstance(JsNativeDiagnosticSuppressor)
container.useInstance(JsExportDeclarationChecker(includeUnsignedNumbers = false))
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
@@ -33,7 +33,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.isDynamic
import org.jetbrains.kotlin.types.typeUtil.*
object JsExportDeclarationChecker : DeclarationChecker {
class JsExportDeclarationChecker(private val includeUnsignedNumbers: Boolean) : DeclarationChecker {
override fun check(declaration: KtDeclaration, descriptor: DeclarationDescriptor, context: DeclarationCheckerContext) {
val trace = context.trace
val bindingContext = trace.bindingContext
@@ -190,8 +190,9 @@ object JsExportDeclarationChecker : DeclarationChecker {
KotlinBuiltIns.isString(nonNullable) ||
(nonNullable.isPrimitiveNumberOrNullableType() && !nonNullable.isLong()) ||
nonNullable.isNothingOrNullableNothing() ||
KotlinBuiltIns.isArray(this) ||
KotlinBuiltIns.isPrimitiveArray(this)
KotlinBuiltIns.isArray(nonNullable) ||
KotlinBuiltIns.isPrimitiveArray(nonNullable) ||
(includeUnsignedNumbers && KotlinBuiltIns.isUnsignedNumber(nonNullable))
if (isPrimitiveExportableType) return true
@@ -91,11 +91,23 @@ private external fun externrefHashCode(ref: ExternalInterfaceType): Int
private fun externrefToString(ref: ExternalInterfaceType): String =
js("String(ref)")
private fun externrefToUByte(ref: ExternalInterfaceType): UByte =
js("Number(ref)")
private fun externrefToUShort(ref: ExternalInterfaceType): UShort =
js("Number(ref)")
private fun externrefToUInt(ref: ExternalInterfaceType): UInt =
js("Number(ref)")
private fun externrefToULong(ref: ExternalInterfaceType): ULong =
js("BigInt(ref)")
private fun externrefToInt(ref: ExternalInterfaceType): Int =
js("Number(ref)")
private fun externrefToLong(ref: ExternalInterfaceType): Long =
js("Number(ref)")
js("BigInt(ref)")
private fun externrefToBoolean(ref: ExternalInterfaceType): Boolean =
js("Boolean(ref)")
@@ -314,6 +326,18 @@ internal fun jsToKotlinByteAdapter(x: Int): Byte = x.toByte()
internal fun jsToKotlinShortAdapter(x: Int): Short = x.toShort()
internal fun jsToKotlinCharAdapter(x: Int): Char = x.toChar()
internal fun externRefToKotlinUByteAdapter(x: ExternalInterfaceType): UByte =
externrefToUByte(x)
internal fun externRefToKotlinUShortAdapter(x: ExternalInterfaceType): UShort =
externrefToUShort(x)
internal fun externRefToKotlinUIntAdapter(x: ExternalInterfaceType): UInt =
externrefToUInt(x)
internal fun externRefToKotlinULongAdapter(x: ExternalInterfaceType): ULong =
externrefToULong(x)
internal fun externRefToKotlinIntAdapter(x: ExternalInterfaceType): Int =
externrefToInt(x)
@@ -335,6 +359,30 @@ internal fun kotlinIntToExternRefAdapter(x: Int): JsNumber =
internal fun kotlinBooleanToExternRefAdapter(x: Boolean): JsBoolean =
if (x) jsTrue else jsFalse
private fun kotlinUByteToJsNumberUnsafe(x: Int): JsNumber =
js("x & 0xFF")
private fun kotlinUShortToJsNumberUnsafe(x: Int): JsNumber =
js("x & 0xFFFF")
private fun kotlinUIntToJsNumberUnsafe(x: Int): JsNumber =
js("x >>> 0")
private fun kotlinULongToJsBigIntUnsafe(x: Long): ExternalInterfaceType =
js("x & 0xFFFFFFFFFFFFFFFFn")
internal fun kotlinUByteToJsNumber(x: UByte): JsNumber =
kotlinUByteToJsNumberUnsafe(x.toInt())
internal fun kotlinUShortToJsNumber(x: UShort): JsNumber =
kotlinUShortToJsNumberUnsafe(x.toInt())
internal fun kotlinUIntToJsNumber(x: UInt): JsNumber =
kotlinUIntToJsNumberUnsafe(x.toInt())
internal fun kotlinULongToJsBigInt(x: ULong): ExternalInterfaceType =
kotlinULongToJsBigIntUnsafe(x.toLong())
internal fun kotlinLongToExternRefAdapter(x: Long): ExternalInterfaceType =
longToExternref(x)
@@ -26,7 +26,6 @@ object WasmJsPlatformConfigurator : PlatformConfiguratorBase(
JsExternalChecker, WasmExternalInheritanceChecker,
JsRuntimeAnnotationChecker,
JsExportAnnotationChecker,
JsExportDeclarationChecker,
WasmExternalDeclarationChecker,
WasmImportAnnotationChecker,
WasmJsFunAnnotationChecker,
@@ -51,6 +50,7 @@ object WasmJsPlatformConfigurator : PlatformConfiguratorBase(
container.useInstance(ExtensionFunctionToExternalIsInlinable)
container.useInstance(JsQualifierChecker)
container.useInstance(WasmDiagnosticSuppressor)
container.useInstance(JsExportDeclarationChecker(includeUnsignedNumbers = true))
}
override fun configureModuleDependentCheckers(container: StorageComponentContainer) {
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.types.KotlinType
import org.jetbrains.kotlin.types.typeUtil.isBoolean
import org.jetbrains.kotlin.types.typeUtil.isPrimitiveNumberType
import org.jetbrains.kotlin.types.typeUtil.isUnit
import org.jetbrains.kotlin.types.typeUtil.isUnsignedNumberType
object WasmImportAnnotationChecker : DeclarationChecker {
private val wasmImportFqName = FqName("kotlin.wasm.WasmImport")
@@ -54,7 +55,7 @@ object WasmImportAnnotationChecker : DeclarationChecker {
}
private fun isParameterTypeSupported(type: KotlinType): Boolean =
type.isPrimitiveNumberType() || type.isBoolean()
type.isPrimitiveNumberType() || type.isUnsignedNumberType() || type.isBoolean()
private fun isReturnTypeSupported(type: KotlinType): Boolean =
isParameterTypeSupported(type) || type.isUnit()
@@ -113,6 +113,7 @@ private fun isTypeSupportedInJsInterop(
val nonNullable = type.makeNotNullable()
if (
KotlinBuiltIns.isPrimitiveType(nonNullable) ||
KotlinBuiltIns.isUnsignedNumber(nonNullable) ||
KotlinBuiltIns.isString(nonNullable)
) {
return true
@@ -49,6 +49,12 @@ public class FirWasmCodegenWasmJsInteropTestGenerated extends AbstractFirWasmCod
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
}
@Test
@TestMetadata("externalsWithUnsigned.kt")
public void testExternalsWithUnsigned() throws Exception {
runTest("compiler/testData/codegen/boxWasmJsInterop/externalsWithUnsigned.kt");
}
@Test
@TestMetadata("functionTypes.kt")
public void testFunctionTypes() throws Exception {
@@ -49,6 +49,12 @@ public class K1WasmCodegenWasmJsInteropTestGenerated extends AbstractK1WasmCodeg
runTest("compiler/testData/codegen/boxWasmJsInterop/externals.kt");
}
@Test
@TestMetadata("externalsWithUnsigned.kt")
public void testExternalsWithUnsigned() throws Exception {
runTest("compiler/testData/codegen/boxWasmJsInterop/externalsWithUnsigned.kt");
}
@Test
@TestMetadata("functionTypes.kt")
public void testFunctionTypes() throws Exception {