[WASM] Support nullable types for external functions
This commit is contained in:
@@ -271,13 +271,29 @@ class WasmSymbols(
|
|||||||
|
|
||||||
inner class JsInteropAdapters {
|
inner class JsInteropAdapters {
|
||||||
val kotlinToJsStringAdapter = getInternalFunction("kotlinToJsStringAdapter")
|
val kotlinToJsStringAdapter = getInternalFunction("kotlinToJsStringAdapter")
|
||||||
val kotlinToJsBooleanAdapter = getInternalFunction("kotlinToJsBooleanAdapter")
|
|
||||||
val kotlinToJsAnyAdapter = getInternalFunction("kotlinToJsAnyAdapter")
|
val kotlinToJsAnyAdapter = getInternalFunction("kotlinToJsAnyAdapter")
|
||||||
val jsToKotlinAnyAdapter = getInternalFunction("jsToKotlinAnyAdapter")
|
|
||||||
val jsToKotlinStringAdapter = getInternalFunction("jsToKotlinStringAdapter")
|
val jsToKotlinStringAdapter = getInternalFunction("jsToKotlinStringAdapter")
|
||||||
|
val jsToKotlinAnyAdapter = getInternalFunction("jsToKotlinAnyAdapter")
|
||||||
|
|
||||||
val jsToKotlinByteAdapter = getInternalFunction("jsToKotlinByteAdapter")
|
val jsToKotlinByteAdapter = getInternalFunction("jsToKotlinByteAdapter")
|
||||||
val jsToKotlinShortAdapter = getInternalFunction("jsToKotlinShortAdapter")
|
val jsToKotlinShortAdapter = getInternalFunction("jsToKotlinShortAdapter")
|
||||||
val jsToKotlinCharAdapter = getInternalFunction("jsToKotlinCharAdapter")
|
val jsToKotlinCharAdapter = getInternalFunction("jsToKotlinCharAdapter")
|
||||||
|
|
||||||
|
val externRefToKotlinIntAdapter = getInternalFunction("externRefToKotlinIntAdapter")
|
||||||
|
val externRefToKotlinBooleanAdapter = getInternalFunction("externRefToKotlinBooleanAdapter")
|
||||||
|
val externRefToKotlinLongAdapter = getInternalFunction("externRefToKotlinLongAdapter")
|
||||||
|
val externRefToKotlinFloatAdapter = getInternalFunction("externRefToKotlinFloatAdapter")
|
||||||
|
val externRefToKotlinDoubleAdapter = getInternalFunction("externRefToKotlinDoubleAdapter")
|
||||||
|
|
||||||
|
val kotlinIntToExternRefAdapter = getInternalFunction("kotlinIntToExternRefAdapter")
|
||||||
|
val kotlinBooleanToExternRefAdapter = getInternalFunction("kotlinBooleanToExternRefAdapter")
|
||||||
|
val kotlinLongToExternRefAdapter = getInternalFunction("kotlinLongToExternRefAdapter")
|
||||||
|
val kotlinFloatToExternRefAdapter = getInternalFunction("kotlinFloatToExternRefAdapter")
|
||||||
|
val kotlinDoubleToExternRefAdapter = getInternalFunction("kotlinDoubleToExternRefAdapter")
|
||||||
|
val kotlinByteToExternRefAdapter = getInternalFunction("kotlinByteToExternRefAdapter")
|
||||||
|
val kotlinShortToExternRefAdapter = getInternalFunction("kotlinShortToExternRefAdapter")
|
||||||
|
val kotlinCharToExternRefAdapter = getInternalFunction("kotlinCharToExternRefAdapter")
|
||||||
}
|
}
|
||||||
|
|
||||||
val jsInteropAdapters = JsInteropAdapters()
|
val jsInteropAdapters = JsInteropAdapters()
|
||||||
|
|||||||
+110
-14
@@ -191,10 +191,42 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
|||||||
if (isReturn && this == builtIns.unitType)
|
if (isReturn && this == builtIns.unitType)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
if (this == builtIns.nothingType)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (!isNullable()) {
|
||||||
|
return kotlinToJsAdapterIfNeededNotNullable(isReturn)
|
||||||
|
}
|
||||||
|
|
||||||
|
val notNullType = makeNotNull()
|
||||||
|
val primitiveToExternRefAdapter = when (notNullType) {
|
||||||
|
builtIns.byteType -> adapters.kotlinByteToExternRefAdapter.owner
|
||||||
|
builtIns.shortType -> adapters.kotlinShortToExternRefAdapter.owner
|
||||||
|
builtIns.charType -> adapters.kotlinCharToExternRefAdapter.owner
|
||||||
|
builtIns.intType -> adapters.kotlinIntToExternRefAdapter.owner
|
||||||
|
builtIns.longType -> adapters.kotlinLongToExternRefAdapter.owner
|
||||||
|
builtIns.floatType -> adapters.kotlinFloatToExternRefAdapter.owner
|
||||||
|
builtIns.doubleType -> adapters.kotlinDoubleToExternRefAdapter.owner
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
val typeAdapter = primitiveToExternRefAdapter?.let(::FunctionBasedAdapter)
|
||||||
|
?: notNullType.kotlinToJsAdapterIfNeededNotNullable(isReturn)
|
||||||
|
?: return null
|
||||||
|
|
||||||
|
return NullOrAdapter(typeAdapter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrType.kotlinToJsAdapterIfNeededNotNullable(isReturn: Boolean): InteropTypeAdapter? {
|
||||||
|
if (isReturn && this == builtIns.unitType)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (this == builtIns.nothingType)
|
||||||
|
return null
|
||||||
|
|
||||||
when (this) {
|
when (this) {
|
||||||
builtIns.stringType -> return FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner)
|
builtIns.stringType -> return FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner)
|
||||||
builtIns.stringType.makeNullable() -> return NullOrAdapter(FunctionBasedAdapter(adapters.kotlinToJsStringAdapter.owner))
|
builtIns.booleanType -> return FunctionBasedAdapter(adapters.kotlinBooleanToExternRefAdapter.owner)
|
||||||
builtIns.booleanType -> return FunctionBasedAdapter(adapters.kotlinToJsBooleanAdapter.owner)
|
|
||||||
builtIns.anyType -> return FunctionBasedAdapter(adapters.kotlinToJsAnyAdapter.owner)
|
builtIns.anyType -> return FunctionBasedAdapter(adapters.kotlinToJsAnyAdapter.owner)
|
||||||
|
|
||||||
builtIns.byteType,
|
builtIns.byteType,
|
||||||
@@ -258,9 +290,42 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
|||||||
if (isReturn && this == builtIns.unitType)
|
if (isReturn && this == builtIns.unitType)
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
val notNullType = makeNotNull()
|
||||||
|
val notNullAdapter = notNullType.jsToKotlinAdapterIfNeededNotNullable(isReturn)
|
||||||
|
val isPrimitive = notNullAdapter?.fromType?.isPrimitiveType() ?: notNullType.isPrimitiveType()
|
||||||
|
|
||||||
|
return if (isNullable()) {
|
||||||
|
if (isPrimitive) { //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
|
||||||
|
else -> adapters.externRefToKotlinIntAdapter.owner
|
||||||
|
}
|
||||||
|
val externalToPrimitiveAdapter = FunctionBasedAdapter(externRefToPrimitiveAdapter)
|
||||||
|
NullOrAdapter(
|
||||||
|
adapter = notNullAdapter?.let { CombineAdapter(it, externalToPrimitiveAdapter) } ?: externalToPrimitiveAdapter
|
||||||
|
)
|
||||||
|
} else { //nullable reference should not be checked
|
||||||
|
notNullAdapter?.let(::NullOrAdapter)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (isPrimitive) { // !nullable primitive checked by wasm signature
|
||||||
|
notNullAdapter
|
||||||
|
} else { // !nullable reference should be null checked
|
||||||
|
notNullAdapter?.let(::CheckNotNullAndAdapter)
|
||||||
|
?: CheckNotNullNoAdapter(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun IrType.jsToKotlinAdapterIfNeededNotNullable(isReturn: Boolean): InteropTypeAdapter? {
|
||||||
|
if (isReturn && (this == builtIns.unitType || this == builtIns.nothingType))
|
||||||
|
return null
|
||||||
|
|
||||||
when (this) {
|
when (this) {
|
||||||
builtIns.stringType -> return CheckNotNullAndAdapter(FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner))
|
builtIns.stringType -> return FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner)
|
||||||
builtIns.stringType.makeNullable() -> return NullOrAdapter(FunctionBasedAdapter(adapters.jsToKotlinStringAdapter.owner))
|
|
||||||
builtIns.anyType -> return FunctionBasedAdapter(adapters.jsToKotlinAnyAdapter.owner)
|
builtIns.anyType -> return FunctionBasedAdapter(adapters.jsToKotlinAnyAdapter.owner)
|
||||||
builtIns.byteType -> return FunctionBasedAdapter(adapters.jsToKotlinByteAdapter.owner)
|
builtIns.byteType -> return FunctionBasedAdapter(adapters.jsToKotlinByteAdapter.owner)
|
||||||
builtIns.shortType -> return FunctionBasedAdapter(adapters.jsToKotlinShortAdapter.owner)
|
builtIns.shortType -> return FunctionBasedAdapter(adapters.jsToKotlinShortAdapter.owner)
|
||||||
@@ -561,6 +626,17 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CombineAdapter(
|
||||||
|
private val outerAdapter: InteropTypeAdapter,
|
||||||
|
private val innerAdapter: InteropTypeAdapter,
|
||||||
|
) : InteropTypeAdapter {
|
||||||
|
override val fromType = innerAdapter.fromType
|
||||||
|
override val toType = outerAdapter.toType
|
||||||
|
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
|
||||||
|
return outerAdapter.adapt(innerAdapter.adapt(expression, builder), builder)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Current V8 Wasm GC mandates dataref type instead of structs and arrays
|
* Current V8 Wasm GC mandates dataref type instead of structs and arrays
|
||||||
*/
|
*/
|
||||||
@@ -588,18 +664,37 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Current V8 Wasm GC mandates dataref type instead of structs and arrays
|
||||||
|
*/
|
||||||
|
inner class CheckNotNullNoAdapter(type: IrType) : InteropTypeAdapter {
|
||||||
|
override val fromType: IrType = type.makeNullable()
|
||||||
|
override val toType: IrType = type.makeNotNull()
|
||||||
|
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
|
||||||
|
return builder.irCall(context.irBuiltIns.checkNotNullSymbol).also {
|
||||||
|
it.putValueArgument(0, expression)
|
||||||
|
it.putTypeArgument(0, fromType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Effectively `value?.let { adapter(it) }`
|
* Effectively `value?.let { adapter(it) }`
|
||||||
*/
|
*/
|
||||||
inner class NullOrAdapter(
|
inner class NullOrAdapter(
|
||||||
val adapter: InteropTypeAdapter
|
private val adapter: InteropTypeAdapter
|
||||||
) : InteropTypeAdapter {
|
) : InteropTypeAdapter {
|
||||||
override val fromType: IrType = adapter.fromType.makeNullable()
|
override val fromType: IrType = adapter.fromType.makeNullable()
|
||||||
override val toType: IrType = adapter.toType.makeNullable()
|
override val toType: IrType = adapter.toType.makeNullable()
|
||||||
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
|
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
|
||||||
return builder.irComposite {
|
return builder.irComposite {
|
||||||
val tmp = irTemporary(expression)
|
val tmp = irTemporary(expression)
|
||||||
+irIfNull(toType, irGet(tmp), irNull(toType), irImplicitCast(adapter.adapt(irGet(tmp), builder), toType))
|
+irIfNull(
|
||||||
|
type = toType,
|
||||||
|
subject = irGet(tmp),
|
||||||
|
thenPart = irNull(toType),
|
||||||
|
elsePart = irImplicitCast(adapter.adapt(irGet(tmp), builder), toType)
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -608,18 +703,19 @@ class JsInteropFunctionsLowering(val context: WasmBackendContext) : DeclarationT
|
|||||||
* Effectively `adapter(value!!)`
|
* Effectively `adapter(value!!)`
|
||||||
*/
|
*/
|
||||||
inner class CheckNotNullAndAdapter(
|
inner class CheckNotNullAndAdapter(
|
||||||
val adapter: InteropTypeAdapter
|
private val adapter: InteropTypeAdapter
|
||||||
) : InteropTypeAdapter {
|
) : InteropTypeAdapter {
|
||||||
override val fromType: IrType = adapter.fromType.makeNullable()
|
override val fromType: IrType = adapter.fromType.makeNullable()
|
||||||
override val toType: IrType = adapter.toType.makeNotNull()
|
override val toType: IrType = adapter.toType
|
||||||
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
|
override fun adapt(expression: IrExpression, builder: IrBuilderWithScope): IrExpression {
|
||||||
return builder.irComposite {
|
return builder.irComposite {
|
||||||
val tmp = irTemporary(expression)
|
val temp = irTemporary(expression)
|
||||||
+irCall(context.irBuiltIns.checkNotNullSymbol).also {
|
+irIfNull(
|
||||||
it.putValueArgument(0, irGet(tmp))
|
type = toType,
|
||||||
it.putTypeArgument(0, fromType)
|
subject = irGet(temp),
|
||||||
}
|
thenPart = irCall(this@JsInteropFunctionsLowering.context.wasmSymbols.throwNullPointerException),
|
||||||
+adapter.adapt(irGet(tmp), builder)
|
elsePart = adapter.adapt(irImplicitCast(irGet(temp), adapter.fromType.makeNotNull()), builder),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
// IGNORE_BACKEND: JS_IR, JS
|
||||||
|
|
||||||
|
inline fun checkNPE(body: () -> Unit) {
|
||||||
|
var throwed = false
|
||||||
|
try {
|
||||||
|
body()
|
||||||
|
} catch (e: NullPointerException) {
|
||||||
|
throwed = true
|
||||||
|
}
|
||||||
|
check(throwed)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("() => 'abc'")
|
||||||
|
external fun notNullString(): String
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun notNull2String(): String
|
||||||
|
|
||||||
|
@JsFun("() => 'abc'")
|
||||||
|
external fun nullString(): String?
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun null2String(): String?
|
||||||
|
|
||||||
|
fun testString() {
|
||||||
|
check(notNullString() == "abc")
|
||||||
|
checkNPE { notNull2String() }
|
||||||
|
check(nullString() == "abc")
|
||||||
|
check(null2String() == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface ExternRef
|
||||||
|
|
||||||
|
@JsFun("() => 'abc'")
|
||||||
|
external fun notNullExternRef(): ExternRef
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun notNull2ExternRef(): ExternRef
|
||||||
|
|
||||||
|
@JsFun("() => 'abc'")
|
||||||
|
external fun nullExternRef(): ExternRef?
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun null2ExternRef(): ExternRef?
|
||||||
|
|
||||||
|
|
||||||
|
fun testExterRef() {
|
||||||
|
check(notNullExternRef() != null)
|
||||||
|
checkNPE { notNull2ExternRef() }
|
||||||
|
check(nullExternRef() != null)
|
||||||
|
check(null2ExternRef() == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataRef
|
||||||
|
|
||||||
|
@JsFun("(x) => x")
|
||||||
|
external fun notNullDataRef(x: DataRef): DataRef
|
||||||
|
|
||||||
|
@JsFun("(x) => null")
|
||||||
|
external fun notNull2DataRef(x: DataRef): DataRef
|
||||||
|
|
||||||
|
@JsFun("(x) => x")
|
||||||
|
external fun nullDataRef(x: DataRef): DataRef?
|
||||||
|
|
||||||
|
@JsFun("(x) => null")
|
||||||
|
external fun null2DataRef(x: DataRef): DataRef?
|
||||||
|
|
||||||
|
fun testDataRef() {
|
||||||
|
val dataRef = DataRef()
|
||||||
|
check(notNullDataRef(dataRef) == dataRef)
|
||||||
|
checkNPE { notNull2DataRef(dataRef) }
|
||||||
|
check (nullDataRef(dataRef) == dataRef)
|
||||||
|
check (null2DataRef(dataRef) == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("() => 123")
|
||||||
|
external fun notNullInt(): Int
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun notNull2Int(): Int
|
||||||
|
|
||||||
|
@JsFun("() => 123")
|
||||||
|
external fun nullInt(): Int?
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun null2Int(): Int?
|
||||||
|
|
||||||
|
fun testInt() {
|
||||||
|
check(notNullInt() == 123)
|
||||||
|
check(notNull2Int() == 0)
|
||||||
|
check(nullInt() == 123)
|
||||||
|
check(null2Int() == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("() => true")
|
||||||
|
external fun notNullBoolean(): Boolean
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun notNull2Boolean(): Boolean
|
||||||
|
|
||||||
|
@JsFun("() => true")
|
||||||
|
external fun nullBoolean(): Boolean?
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun null2Boolean(): Boolean?
|
||||||
|
|
||||||
|
fun testBoolean() {
|
||||||
|
check(notNullBoolean() == true)
|
||||||
|
check(notNull2Boolean() == false)
|
||||||
|
check(nullBoolean() == true)
|
||||||
|
check(null2Boolean() == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("() => 123")
|
||||||
|
external fun notNullShort(): Short
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun notNull2Short(): Short
|
||||||
|
|
||||||
|
@JsFun("() => 123")
|
||||||
|
external fun nullShort(): Short?
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun null2Short(): Short?
|
||||||
|
|
||||||
|
fun testShort() {
|
||||||
|
check(notNullShort() == 123.toShort())
|
||||||
|
check(notNull2Short() == 0.toShort())
|
||||||
|
check(nullShort() == 123.toShort())
|
||||||
|
check(null2Short() == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("() => 123.5")
|
||||||
|
external fun notNullFloat(): Float
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun notNull2Float(): Float
|
||||||
|
|
||||||
|
@JsFun("() => 123.5")
|
||||||
|
external fun nullFloat(): Float?
|
||||||
|
|
||||||
|
@JsFun("() => null")
|
||||||
|
external fun null2Float(): Float?
|
||||||
|
|
||||||
|
fun testFloat() {
|
||||||
|
check(notNullFloat() == 123.5f)
|
||||||
|
check(notNull2Float() == 0.0f)
|
||||||
|
check(nullFloat() == 123.5f)
|
||||||
|
check(null2Float() == null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
testString()
|
||||||
|
testExterRef()
|
||||||
|
testDataRef()
|
||||||
|
testInt()
|
||||||
|
testBoolean()
|
||||||
|
testShort()
|
||||||
|
testFloat()
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -0,0 +1,126 @@
|
|||||||
|
// IGNORE_BACKEND: JS_IR, JS
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 'abc') throw 'error' }")
|
||||||
|
external fun notNullString(x: String)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 'abc') throw 'error' }")
|
||||||
|
external fun nullString(x: String?)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2String(x: String?)
|
||||||
|
|
||||||
|
fun testString() {
|
||||||
|
notNullString("abc")
|
||||||
|
nullString("abc")
|
||||||
|
null2String(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
external interface ExternRef
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 'abc') throw 'error' }")
|
||||||
|
external fun notNullExternRef(x: ExternRef)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 'abc') throw 'error' }")
|
||||||
|
external fun nullExternRef(x: ExternRef?)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2ExternRef(x: ExternRef?)
|
||||||
|
|
||||||
|
@JsFun("() => 'abc'")
|
||||||
|
external fun getExternRef(): ExternRef
|
||||||
|
|
||||||
|
fun testExterRef() {
|
||||||
|
val externRef = getExternRef()
|
||||||
|
notNullExternRef(externRef)
|
||||||
|
nullExternRef(externRef)
|
||||||
|
null2ExternRef(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataRef
|
||||||
|
|
||||||
|
@JsFun("(x, y) => { if (x === null) throw 'error' }")
|
||||||
|
external fun notNullDataRef(x: DataRef)
|
||||||
|
|
||||||
|
@JsFun("(x, y) => { if (x === null) throw 'error' }")
|
||||||
|
external fun nullDataRef(x: DataRef?)
|
||||||
|
|
||||||
|
@JsFun("(x, y) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2DataRef(x: DataRef?)
|
||||||
|
|
||||||
|
fun testDataRef() {
|
||||||
|
val dataRef = DataRef()
|
||||||
|
notNullDataRef(dataRef)
|
||||||
|
nullDataRef(dataRef)
|
||||||
|
null2DataRef(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 123) throw 'error' }")
|
||||||
|
external fun notNullInt(x: Int)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 123) throw 'error' }")
|
||||||
|
external fun nullInt(x: Int?)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2Int(x: Int?)
|
||||||
|
|
||||||
|
fun testInt() {
|
||||||
|
notNullInt(123)
|
||||||
|
nullInt(123)
|
||||||
|
null2Int(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== true) throw 'error' }")
|
||||||
|
external fun notNullBoolean(x: Boolean)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== true) throw 'error' }")
|
||||||
|
external fun nullBoolean(x: Boolean?)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2Boolean(x: Boolean?)
|
||||||
|
|
||||||
|
fun testBoolean() {
|
||||||
|
notNullBoolean(true)
|
||||||
|
nullBoolean(true)
|
||||||
|
null2Boolean(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("(x) => { x == 123 }")
|
||||||
|
external fun notNullShort(x: Short)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 123) throw 'error' }")
|
||||||
|
external fun nullShort(x: Short?)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2Short(x: Short?)
|
||||||
|
|
||||||
|
fun testShort() {
|
||||||
|
notNullShort(123.toShort())
|
||||||
|
nullShort(123.toShort())
|
||||||
|
null2Short(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 123.5) throw 'error' }")
|
||||||
|
external fun notNullFloat(x: Float)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== 123.5) throw 'error' }")
|
||||||
|
external fun nullFloat(x: Float?)
|
||||||
|
|
||||||
|
@JsFun("(x) => { if (x !== null) throw 'error' }")
|
||||||
|
external fun null2Float(x: Float?)
|
||||||
|
|
||||||
|
fun testFloat() {
|
||||||
|
notNullFloat(123.5f)
|
||||||
|
nullFloat(123.5f)
|
||||||
|
null2Float(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun box(): String {
|
||||||
|
testString()
|
||||||
|
testExterRef()
|
||||||
|
testDataRef()
|
||||||
|
testInt()
|
||||||
|
testBoolean()
|
||||||
|
testShort()
|
||||||
|
testFloat()
|
||||||
|
return "OK"
|
||||||
|
}
|
||||||
@@ -37,6 +37,16 @@ external fun getJsNullAsNonNullable(): EI
|
|||||||
@JsFun("() => undefined")
|
@JsFun("() => undefined")
|
||||||
external fun getJsUndefinedAsNonNullable(): EI
|
external fun getJsUndefinedAsNonNullable(): EI
|
||||||
|
|
||||||
|
inline fun checkNPE(body: () -> Unit) {
|
||||||
|
var throwed = false
|
||||||
|
try {
|
||||||
|
body()
|
||||||
|
} catch (e: NullPointerException) {
|
||||||
|
throwed = true
|
||||||
|
}
|
||||||
|
assertTrue(throwed)
|
||||||
|
}
|
||||||
|
|
||||||
fun box(): String {
|
fun box(): String {
|
||||||
val jsNull = getNull()
|
val jsNull = getNull()
|
||||||
val jsUndefined = getUndefined()
|
val jsUndefined = getUndefined()
|
||||||
@@ -55,11 +65,8 @@ fun box(): String {
|
|||||||
assertFalse(isJsUndefined(null))
|
assertFalse(isJsUndefined(null))
|
||||||
assertTrue(isJsUndefined(jsUndefined))
|
assertTrue(isJsUndefined(jsUndefined))
|
||||||
|
|
||||||
// TODO: Should these fail?
|
checkNPE(::getJsNullAsNonNullable)
|
||||||
val n2 = getJsNullAsNonNullable()
|
checkNPE(::getJsUndefinedAsNonNullable)
|
||||||
val ud2 = getJsUndefinedAsNonNullable()
|
|
||||||
assertTrue(isJsNull(n2))
|
|
||||||
assertTrue(isJsUndefined(ud2))
|
|
||||||
|
|
||||||
return "OK"
|
return "OK"
|
||||||
}
|
}
|
||||||
Generated
+12
@@ -55,6 +55,18 @@ public class IrCodegenWasmJsInteropJsTestGenerated extends AbstractIrCodegenWasm
|
|||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("jsToKotlinAdapters.kt")
|
||||||
|
public void testJsToKotlinAdapters() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@TestMetadata("kotlinToJsAdapters.kt")
|
||||||
|
public void testKotlinToJsAdapters() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@TestMetadata("longStrings.kt")
|
@TestMetadata("longStrings.kt")
|
||||||
public void testLongStrings() throws Exception {
|
public void testLongStrings() throws Exception {
|
||||||
|
|||||||
+10
@@ -55,6 +55,16 @@ public class IrCodegenWasmJsInteropWasmTestGenerated extends AbstractIrCodegenWa
|
|||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsExport.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("jsToKotlinAdapters.kt")
|
||||||
|
public void testJsToKotlinAdapters() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxWasmJsInterop/jsToKotlinAdapters.kt");
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("kotlinToJsAdapters.kt")
|
||||||
|
public void testKotlinToJsAdapters() throws Exception {
|
||||||
|
runTest("compiler/testData/codegen/boxWasmJsInterop/kotlinToJsAdapters.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("longStrings.kt")
|
@TestMetadata("longStrings.kt")
|
||||||
public void testLongStrings() throws Exception {
|
public void testLongStrings() throws Exception {
|
||||||
runTest("compiler/testData/codegen/boxWasmJsInterop/longStrings.kt");
|
runTest("compiler/testData/codegen/boxWasmJsInterop/longStrings.kt");
|
||||||
|
|||||||
@@ -81,6 +81,36 @@ private external fun externrefHashCode(ref: ExternalInterfaceType): Int
|
|||||||
@JsFun("ref => String(ref)")
|
@JsFun("ref => String(ref)")
|
||||||
private external fun externrefToString(ref: ExternalInterfaceType): String
|
private external fun externrefToString(ref: ExternalInterfaceType): String
|
||||||
|
|
||||||
|
@JsFun("ref => Number(ref)")
|
||||||
|
private external fun externrefToInt(ref: ExternalInterfaceType): Int
|
||||||
|
|
||||||
|
@JsFun("ref => Number(ref)")
|
||||||
|
private external fun externrefToLong(ref: ExternalInterfaceType): Long
|
||||||
|
|
||||||
|
@JsFun("ref => Boolean(ref)")
|
||||||
|
private external fun externrefToBoolean(ref: ExternalInterfaceType): Boolean
|
||||||
|
|
||||||
|
@JsFun("ref => Number(ref)")
|
||||||
|
private external fun externrefToFloat(ref: ExternalInterfaceType): Float
|
||||||
|
|
||||||
|
@JsFun("ref => Number(ref)")
|
||||||
|
private external fun externrefToDouble(ref: ExternalInterfaceType): Double
|
||||||
|
|
||||||
|
@JsFun("x => x")
|
||||||
|
private external fun intToExternref(x: Int): ExternalInterfaceType
|
||||||
|
|
||||||
|
@JsFun("x => x")
|
||||||
|
private external fun longToExternref(x: Long): ExternalInterfaceType
|
||||||
|
|
||||||
|
@JsFun("x => x")
|
||||||
|
private external fun booleanToExternref(x: Boolean): ExternalInterfaceType
|
||||||
|
|
||||||
|
@JsFun("x => x")
|
||||||
|
private external fun floatToExternref(x: Float): ExternalInterfaceType
|
||||||
|
|
||||||
|
@JsFun("x => x")
|
||||||
|
private external fun doubleToExternref(x: Double): ExternalInterfaceType
|
||||||
|
|
||||||
@JsFun("(lhs, rhs) => lhs === rhs")
|
@JsFun("(lhs, rhs) => lhs === rhs")
|
||||||
private external fun externrefEquals(lhs: ExternalInterfaceType, rhs: ExternalInterfaceType): Boolean
|
private external fun externrefEquals(lhs: ExternalInterfaceType, rhs: ExternalInterfaceType): Boolean
|
||||||
|
|
||||||
@@ -222,9 +252,6 @@ internal external fun jsTrue(): ExternalInterfaceType
|
|||||||
@JsFun("() => false")
|
@JsFun("() => false")
|
||||||
internal external fun jsFalse(): ExternalInterfaceType
|
internal external fun jsFalse(): ExternalInterfaceType
|
||||||
|
|
||||||
internal fun kotlinToJsBooleanAdapter(x: Boolean): ExternalInterfaceType =
|
|
||||||
if (x) jsTrue() else jsFalse()
|
|
||||||
|
|
||||||
internal fun kotlinToJsAnyAdapter(x: Any): ExternalInterfaceType =
|
internal fun kotlinToJsAnyAdapter(x: Any): ExternalInterfaceType =
|
||||||
anyToExternRef(x)
|
anyToExternRef(x)
|
||||||
|
|
||||||
@@ -234,3 +261,43 @@ internal fun jsToKotlinAnyAdapter(x: ExternalInterfaceType): Any? =
|
|||||||
internal fun jsToKotlinByteAdapter(x: Int): Byte = x.toByte()
|
internal fun jsToKotlinByteAdapter(x: Int): Byte = x.toByte()
|
||||||
internal fun jsToKotlinShortAdapter(x: Int): Short = x.toShort()
|
internal fun jsToKotlinShortAdapter(x: Int): Short = x.toShort()
|
||||||
internal fun jsToKotlinCharAdapter(x: Int): Char = x.toChar()
|
internal fun jsToKotlinCharAdapter(x: Int): Char = x.toChar()
|
||||||
|
|
||||||
|
internal fun externRefToKotlinIntAdapter(x: ExternalInterfaceType): Int =
|
||||||
|
externrefToInt(x)
|
||||||
|
|
||||||
|
internal fun externRefToKotlinBooleanAdapter(x: ExternalInterfaceType): Boolean =
|
||||||
|
externrefToBoolean(x)
|
||||||
|
|
||||||
|
internal fun externRefToKotlinLongAdapter(x: ExternalInterfaceType): Long =
|
||||||
|
externrefToLong(x)
|
||||||
|
|
||||||
|
internal fun externRefToKotlinFloatAdapter(x: ExternalInterfaceType): Float =
|
||||||
|
externrefToFloat(x)
|
||||||
|
|
||||||
|
internal fun externRefToKotlinDoubleAdapter(x: ExternalInterfaceType): Double =
|
||||||
|
externrefToDouble(x)
|
||||||
|
|
||||||
|
|
||||||
|
internal fun kotlinIntToExternRefAdapter(x: Int): ExternalInterfaceType =
|
||||||
|
intToExternref(x)
|
||||||
|
|
||||||
|
internal fun kotlinBooleanToExternRefAdapter(x: Boolean): ExternalInterfaceType =
|
||||||
|
if (x) jsTrue() else jsFalse()
|
||||||
|
|
||||||
|
internal fun kotlinLongToExternRefAdapter(x: Long): ExternalInterfaceType =
|
||||||
|
longToExternref(x)
|
||||||
|
|
||||||
|
internal fun kotlinFloatToExternRefAdapter(x: Float): ExternalInterfaceType =
|
||||||
|
floatToExternref(x)
|
||||||
|
|
||||||
|
internal fun kotlinDoubleToExternRefAdapter(x: Double): ExternalInterfaceType =
|
||||||
|
doubleToExternref(x)
|
||||||
|
|
||||||
|
internal fun kotlinByteToExternRefAdapter(x: Byte): ExternalInterfaceType =
|
||||||
|
intToExternref(x.toInt())
|
||||||
|
|
||||||
|
internal fun kotlinShortToExternRefAdapter(x: Short): ExternalInterfaceType =
|
||||||
|
intToExternref(x.toInt())
|
||||||
|
|
||||||
|
internal fun kotlinCharToExternRefAdapter(x: Char): ExternalInterfaceType =
|
||||||
|
intToExternref(x.toInt())
|
||||||
|
|||||||
Reference in New Issue
Block a user