diff --git a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt index cadb11e7f58..d72731cd804 100644 --- a/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt +++ b/js/js.dart-ast/src/com/google/dart/compiler/backend/js/ast/metadata/metadataProperties.kt @@ -62,7 +62,8 @@ enum class TypeCheck { INSTANCEOF, OR_NULL, IS_ANY, - AND_PREDICATE + AND_PREDICATE, + SAME_AS } enum class SideEffectKind { diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/RttiTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/RttiTestGenerated.java index d5b29b39de7..d32f67bba52 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/RttiTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/RttiTestGenerated.java @@ -59,6 +59,12 @@ public class RttiTestGenerated extends AbstractRttiTest { doTest(fileName); } + @TestMetadata("isReifiedObject.kt") + public void testIsReifiedObject() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/rtti/cases/isReifiedObject.kt"); + doTest(fileName); + } + @TestMetadata("isSameClass.kt") public void testIsSameClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/rtti/cases/isSameClass.kt"); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java index 501c4911d5f..97133e193ab 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/context/Namer.java @@ -400,6 +400,11 @@ public final class Namer { return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.INSTANCEOF); } + @NotNull + public JsExpression isInstanceOfObject(@NotNull JsExpression type) { + return invokeFunctionAndSetTypeCheckMetadata("isInstanceOf", type, TypeCheck.SAME_AS); + } + @NotNull public JsExpression orNull(@NotNull JsExpression callable) { return invokeFunctionAndSetTypeCheckMetadata("orNull", callable, TypeCheck.OR_NULL); diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java index db65a6edefa..17c1635e901 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/expression/PatternTranslator.java @@ -23,10 +23,7 @@ import org.jetbrains.kotlin.KtNodeTypes; import org.jetbrains.kotlin.builtins.KotlinBuiltIns; import org.jetbrains.kotlin.builtins.PrimitiveType; import org.jetbrains.kotlin.builtins.ReflectionTypes; -import org.jetbrains.kotlin.descriptors.CallableDescriptor; -import org.jetbrains.kotlin.descriptors.ClassDescriptor; -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor; -import org.jetbrains.kotlin.descriptors.TypeParameterDescriptor; +import org.jetbrains.kotlin.descriptors.*; import org.jetbrains.kotlin.js.patterns.NamePredicate; import org.jetbrains.kotlin.js.patterns.typePredicates.TypePredicatesKt; import org.jetbrains.kotlin.js.translate.context.Namer; @@ -170,8 +167,10 @@ public final class PatternTranslator extends AbstractTranslator { return result; } - JsNameRef typeName = getClassNameReference(type); - return namer().isInstanceOf(typeName); + + ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type); + JsNameRef typeName = context().getQualifiedReference(referencedClass); + return referencedClass.getKind() != ClassKind.OBJECT ? namer().isInstanceOf(typeName) : namer().isInstanceOfObject(typeName); } @Nullable @@ -235,12 +234,6 @@ public final class PatternTranslator extends AbstractTranslator { return alias; } - @NotNull - private JsNameRef getClassNameReference(@NotNull KotlinType type) { - ClassDescriptor referencedClass = DescriptorUtils.getClassDescriptorForType(type); - return context().getQualifiedReference(referencedClass); - } - @NotNull public JsExpression translateExpressionPattern( @NotNull KotlinType type, diff --git a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt index 3f75150aca2..47f5f75057e 100644 --- a/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt +++ b/js/js.translator/src/org/jetbrains/kotlin/js/translate/utils/expandIsCalls.kt @@ -86,6 +86,12 @@ private class TypeCheckRewritingVisitor(private val context: TranslationContext) if (calleeArguments.size == 1) context.namer().isInstanceOf(argument, calleeArguments[0]) else null } + TypeCheck.SAME_AS -> { + // `Kotlin.isInstanceOf(calleeArgument)(argument)` -> `argument === calleeArgument` + // when it's known at compile time that `argument` represents an object type + if (calleeArguments.size == 1) JsAstUtils.equality(argument, calleeArguments[0]) else null + } + TypeCheck.OR_NULL -> { // `Kotlin.orNull(calleeArgument)(argument)` -> `(tmp = argument) == null || calleeArgument(tmp)` if (calleeArguments.size == 1) getReplacementForOrNull(argument, calleeArguments[0]) else null diff --git a/js/js.translator/testData/kotlin_lib_ecma5.js b/js/js.translator/testData/kotlin_lib_ecma5.js index 2d7f51e09bc..9086413bb39 100644 --- a/js/js.translator/testData/kotlin_lib_ecma5.js +++ b/js/js.translator/testData/kotlin_lib_ecma5.js @@ -381,6 +381,15 @@ return object instanceof klass; } + var proto = Object.getPrototypeOf(klass); + var constructor = proto != null ? proto.constructor : null; + if (constructor != null && "$metadata$" in constructor) { + var metadata = constructor.$metadata$; + if (metadata.type === Kotlin.TYPE.OBJECT) { + return object === klass; + } + } + // In WebKit (JavaScriptCore) for some interfaces from DOM typeof returns "object", nevertheless they can be used in RHS of instanceof if (isNativeClass(klass)) { return object instanceof klass; diff --git a/js/js.translator/testData/rtti/cases/isObject.kt b/js/js.translator/testData/rtti/cases/isObject.kt index b76176bc171..ecd15369f13 100644 --- a/js/js.translator/testData/rtti/cases/isObject.kt +++ b/js/js.translator/testData/rtti/cases/isObject.kt @@ -5,8 +5,7 @@ object Obj fun box(): String { val r: Any = Obj -// TODO: fix KT-13465 -// if (r !is Obj) return "r !is Obj" + if (r !is Obj) return "r !is Obj" return "OK" } diff --git a/js/js.translator/testData/rtti/cases/isReifiedObject.kt b/js/js.translator/testData/rtti/cases/isReifiedObject.kt new file mode 100644 index 00000000000..3583b5e6d8a --- /dev/null +++ b/js/js.translator/testData/rtti/cases/isReifiedObject.kt @@ -0,0 +1,13 @@ +// NO_INLINE +package foo + +object Obj + +private inline fun check(a: Any): String { + return if (a is T) "OK" else "fail" +} + +fun box(): String { + var x: Any = Obj + return check(x) +}