[K/JS] Add ability to use is checks on external objects

This commit is contained in:
Artem Kobzar
2023-03-10 11:33:30 +00:00
committed by Space Team
parent e5523c196e
commit ab7b429298
9 changed files with 64 additions and 0 deletions
@@ -95,6 +95,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
val jsInstanceOf = getInternalFunction("jsInstanceOfIntrinsic")
val jsTypeOf = getInternalFunction("jsTypeOf")
val isExternalObject = getInternalFunction("isExternalObject")
// Number conversions:
@@ -53,6 +53,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
private val isObjectSymbol get() = context.intrinsics.isObjectSymbol
private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf
private val isExternalObjectSymbol = context.intrinsics.isExternalObject
private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf
private val jsClassIntrinsicSymbol = context.intrinsics.jsClass
@@ -270,6 +271,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
generateInterfaceCheck(argument, toType)
}
}
toType.isExternalObject() -> generateIsExternalObject(argument, toType)
else -> generateNativeInstanceOf(argument, toType)
}
}
@@ -360,6 +362,14 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
}
}
private fun generateIsExternalObject(argument: IrExpression, toType: IrType): IrExpression {
val irType = wrapTypeReference(toType)
return JsIrBuilder.buildCall(isExternalObjectSymbol).apply {
putValueArgument(0, argument)
putValueArgument(1, irType)
}
}
private fun generateNativeInstanceOf(argument: IrExpression, toType: IrType): IrExpression {
val irType = wrapTypeReference(toType)
return JsIrBuilder.buildCall(instanceOfIntrinsicSymbol).apply {
@@ -57,6 +57,8 @@ fun IrType.isTypeParameter() = classifierOrNull is IrTypeParameterSymbol
fun IrType.isInterface() = classOrNull?.owner?.kind == ClassKind.INTERFACE
fun IrType.isExternalObject() = classOrNull?.owner.let { it?.kind == ClassKind.OBJECT && it.isExternal }
fun IrType.isAnnotation() = classOrNull?.owner?.kind == ClassKind.ANNOTATION_CLASS
fun IrType.isFunctionOrKFunction() = isFunction() || isKFunction()
@@ -3824,6 +3824,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
public void testSimpleIsInterface() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
}
@Test
@TestMetadata("simpleIsObject.kt")
public void testSimpleIsObject() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt");
}
}
@Nested
@@ -4440,6 +4440,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
public void testSimpleIsInterface() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
}
@Test
@TestMetadata("simpleIsObject.kt")
public void testSimpleIsObject() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt");
}
}
@Nested
@@ -4546,6 +4546,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
public void testSimpleIsInterface() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
}
@Test
@TestMetadata("simpleIsObject.kt")
public void testSimpleIsObject() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt");
}
}
@Nested
@@ -4440,6 +4440,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
public void testSimpleIsInterface() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
}
@Test
@TestMetadata("simpleIsObject.kt")
public void testSimpleIsObject() throws Exception {
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt");
}
}
@Nested
@@ -0,0 +1,22 @@
// EXPECTED_REACHABLE_NODES: 1403
// IGNORE_BACKEND: JS
package foo
object RegularKotlinObject
@JsName("Array")
external object JsArray
@JsName("Math")
external object JsMath
fun box(): String {
if (js("[]") !is JsArray) return "fail1"
if (js("[]") is JsMath) return "fail2"
if (js("[]") is RegularKotlinObject) return "fail3"
if (JsMath !is JsMath) return "fail4"
if (RegularKotlinObject !is RegularKotlinObject) return "fail5"
return "OK"
}
@@ -239,3 +239,8 @@ internal fun isComparable(value: dynamic): Boolean {
@OptIn(JsIntrinsic::class)
internal fun isCharSequence(value: dynamic): Boolean =
jsTypeOf(value) == "string" || isInterface(value, jsClassIntrinsic<CharSequence>())
@OptIn(JsIntrinsic::class)
internal fun isExternalObject(value: dynamic, ktExternalObject: dynamic) =
jsEqeqeq(value, ktExternalObject) || (jsTypeOf(ktExternalObject) == "function" && jsInstanceOf(value, ktExternalObject))