[K/JS] Add ability to use is checks on external objects
This commit is contained in:
@@ -95,6 +95,7 @@ class JsIntrinsics(private val irBuiltIns: IrBuiltIns, val context: JsIrBackendC
|
|||||||
|
|
||||||
val jsInstanceOf = getInternalFunction("jsInstanceOfIntrinsic")
|
val jsInstanceOf = getInternalFunction("jsInstanceOfIntrinsic")
|
||||||
val jsTypeOf = getInternalFunction("jsTypeOf")
|
val jsTypeOf = getInternalFunction("jsTypeOf")
|
||||||
|
val isExternalObject = getInternalFunction("isExternalObject")
|
||||||
|
|
||||||
// Number conversions:
|
// Number conversions:
|
||||||
|
|
||||||
|
|||||||
+10
@@ -53,6 +53,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
|||||||
private val isObjectSymbol get() = context.intrinsics.isObjectSymbol
|
private val isObjectSymbol get() = context.intrinsics.isObjectSymbol
|
||||||
|
|
||||||
private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf
|
private val instanceOfIntrinsicSymbol = context.intrinsics.jsInstanceOf
|
||||||
|
private val isExternalObjectSymbol = context.intrinsics.isExternalObject
|
||||||
private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf
|
private val typeOfIntrinsicSymbol = context.intrinsics.jsTypeOf
|
||||||
private val jsClassIntrinsicSymbol = context.intrinsics.jsClass
|
private val jsClassIntrinsicSymbol = context.intrinsics.jsClass
|
||||||
|
|
||||||
@@ -270,6 +271,7 @@ class TypeOperatorLowering(val context: JsIrBackendContext) : BodyLoweringPass {
|
|||||||
generateInterfaceCheck(argument, toType)
|
generateInterfaceCheck(argument, toType)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
toType.isExternalObject() -> generateIsExternalObject(argument, toType)
|
||||||
else -> generateNativeInstanceOf(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 {
|
private fun generateNativeInstanceOf(argument: IrExpression, toType: IrType): IrExpression {
|
||||||
val irType = wrapTypeReference(toType)
|
val irType = wrapTypeReference(toType)
|
||||||
return JsIrBuilder.buildCall(instanceOfIntrinsicSymbol).apply {
|
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.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.isAnnotation() = classOrNull?.owner?.kind == ClassKind.ANNOTATION_CLASS
|
||||||
|
|
||||||
fun IrType.isFunctionOrKFunction() = isFunction() || isKFunction()
|
fun IrType.isFunctionOrKFunction() = isFunction() || isKFunction()
|
||||||
|
|||||||
@@ -3824,6 +3824,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest {
|
|||||||
public void testSimpleIsInterface() throws Exception {
|
public void testSimpleIsInterface() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+6
@@ -4440,6 +4440,12 @@ public class FirJsBoxTestGenerated extends AbstractFirJsBoxTest {
|
|||||||
public void testSimpleIsInterface() throws Exception {
|
public void testSimpleIsInterface() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+6
@@ -4546,6 +4546,12 @@ public class IrBoxJsES6TestGenerated extends AbstractIrBoxJsES6Test {
|
|||||||
public void testSimpleIsInterface() throws Exception {
|
public void testSimpleIsInterface() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
|
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
|
@Nested
|
||||||
|
|||||||
+6
@@ -4440,6 +4440,12 @@ public class IrBoxJsTestGenerated extends AbstractIrBoxJsTest {
|
|||||||
public void testSimpleIsInterface() throws Exception {
|
public void testSimpleIsInterface() throws Exception {
|
||||||
runTest("js/js.translator/testData/box/expression/typeCheck/simpleIsInterface.kt");
|
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
|
@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)
|
@OptIn(JsIntrinsic::class)
|
||||||
internal fun isCharSequence(value: dynamic): Boolean =
|
internal fun isCharSequence(value: dynamic): Boolean =
|
||||||
jsTypeOf(value) == "string" || isInterface(value, jsClassIntrinsic<CharSequence>())
|
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))
|
||||||
Reference in New Issue
Block a user