diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt index d21568cdb66..4beb280805c 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/JsIntrinsics.kt @@ -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: diff --git a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt index a7afe4846c6..ede22505224 100644 --- a/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt +++ b/compiler/ir/backend.js/src/org/jetbrains/kotlin/ir/backend/js/lower/TypeOperatorLowering.kt @@ -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 { diff --git a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt index caeb26ee320..9c5c45cb9a2 100644 --- a/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt +++ b/compiler/ir/ir.tree/src/org/jetbrains/kotlin/ir/util/IrTypeUtils.kt @@ -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() diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java index 93cd8793d4d..a0700835543 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/BoxJsTestGenerated.java @@ -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 diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java index 1ded5fc71b1..9dde912ed9c 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/fir/FirJsBoxTestGenerated.java @@ -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 diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java index cf492125cdf..a4786bc2199 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsES6TestGenerated.java @@ -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 diff --git a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java index c0df910ec4c..0ecf1ff8901 100644 --- a/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java +++ b/js/js.tests/tests-gen/org/jetbrains/kotlin/js/test/ir/IrBoxJsTestGenerated.java @@ -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 diff --git a/js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt b/js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt new file mode 100644 index 00000000000..09af2879f18 --- /dev/null +++ b/js/js.translator/testData/box/expression/typeCheck/simpleIsObject.kt @@ -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" +} \ No newline at end of file diff --git a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt index 7870947e3f9..4f49eb37811 100644 --- a/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt +++ b/libraries/stdlib/js-ir/runtime/typeCheckUtils.kt @@ -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()) + + +@OptIn(JsIntrinsic::class) +internal fun isExternalObject(value: dynamic, ktExternalObject: dynamic) = + jsEqeqeq(value, ktExternalObject) || (jsTypeOf(ktExternalObject) == "function" && jsInstanceOf(value, ktExternalObject)) \ No newline at end of file