diff --git a/js/js.libraries/src/js/rtti.js b/js/js.libraries/src/js/rtti.js index 369239f3d2e..1ee914a0648 100644 --- a/js/js.libraries/src/js/rtti.js +++ b/js/js.libraries/src/js/rtti.js @@ -52,22 +52,22 @@ Kotlin.callSetter = function (thisObject, klass, propertyName, value) { } }; -function isInheritanceFromInterface(metadata, iface) { - if (metadata == null) return false; +function isInheritanceFromInterface(ctor, iface) { + if (ctor === iface) return true; - var interfaces = metadata.interfaces; - var i; - for (i = 0; i < interfaces.length; i++) { - if (interfaces[i] === iface) { - return true; + var metadata = ctor.$metadata$; + if (metadata != null) { + var interfaces = metadata.interfaces; + for (var i = 0; i < interfaces.length; i++) { + if (isInheritanceFromInterface(interfaces[i], iface)) { + return true; + } } } - for (i = 0; i < interfaces.length; i++) { - if (isInheritanceFromInterface(interfaces[i].$metadata$, iface)) { - return true; - } - } - return false; + + var superPrototype = ctor.prototype != null ? Object.getPrototypeOf(ctor.prototype) : null; + var superConstructor = superPrototype != null ? superPrototype.constructor : null; + return superConstructor != null && isInheritanceFromInterface(superConstructor, iface); } /** @@ -114,10 +114,7 @@ Kotlin.isType = function (object, klass) { } if (klassMetadata.kind === Kotlin.Kind.INTERFACE && object.constructor != null) { - metadata = object.constructor.$metadata$; - if (metadata != null) { - return isInheritanceFromInterface(metadata, klass); - } + return isInheritanceFromInterface(object.constructor, klass); } return false; diff --git a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java index 51387ee1288..e48dcbc4952 100644 --- a/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java +++ b/js/js.tests/test/org/jetbrains/kotlin/js/test/semantics/BoxJsTestGenerated.java @@ -7667,6 +7667,12 @@ public class BoxJsTestGenerated extends AbstractBoxJsTest { doTest(fileName); } + @TestMetadata("prototypeUsedToFindInterface.kt") + public void testPrototypeUsedToFindInterface() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/rtti/prototypeUsedToFindInterface.kt"); + doTest(fileName); + } + @TestMetadata("rttiForClass.kt") public void testRttiForClass() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("js/js.translator/testData/box/rtti/rttiForClass.kt"); diff --git a/js/js.translator/testData/box/rtti/prototypeUsedToFindInterface.kt b/js/js.translator/testData/box/rtti/prototypeUsedToFindInterface.kt new file mode 100644 index 00000000000..7c8bb62a3af --- /dev/null +++ b/js/js.translator/testData/box/rtti/prototypeUsedToFindInterface.kt @@ -0,0 +1,25 @@ +// EXPECTED_REACHABLE_NODES: 1112 +interface A { + fun foo(): String +} + +class B : A { + override fun foo(): String = "OK" +} + +fun box(): String { + val b = B::class.js + val c = js(""" + function C() { + b.call(this); + }; + C.prototype = Object.create(b.prototype); + C.prototype.constructor = C; + new C(); + """) + + if (c !is B) return "fail: c !is B" + if (c !is A) return "fail: c !is A" + + return "OK" +}