Fixes some issue related instanceof operations on JsTypes.

A check for a concrete Java type should be a real java cast
check regardless of what JsType interfaces are implemented
and prototypes.

There are other subtleties and potential future behavior
changes (e.g. handling of JsTypes without prototypes) but that
is not covered in this patch.

Change-Id: Ic3a9edd0b60a861bc01ed8f83351a14f00a7f427
diff --git a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
index 4a0bf15..5a0dce0 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/ast/JTypeOracle.java
@@ -509,33 +509,23 @@
    * True if the type is a JSO or interface implemented by JSO or a JsType without prototype.
    */
   public boolean canCrossCastLikeJso(JType type) {
-    JDeclaredType dtype = getNearestJsType(type, false);
-    return canBeJavaScriptObject(type) || (dtype instanceof JInterfaceType
-        && isOrExtendsJsType(type, false) && !isOrExtendsJsType(type, true));
+    return canBeJavaScriptObject(type) || isJsTypeInterfaceWithoutPrototype(type);
   }
 
-  /**
-   * True if the type is a JSO or JSO Interface that is not dually implemented, or is a JsType
-   * without the prototype that is not implemented by a Java class.
-   */
-  public boolean willCrossCastLikeJso(JType type) {
-    return isEffectivelyJavaScriptObject(type) || canCrossCastLikeJso(type)
-        && type instanceof JInterfaceType && !hasLiveImplementors(type);
+  public boolean isJsTypeInterfaceWithoutPrototype(JType type) {
+    return isJsTypeInterface(type, false);
   }
 
-  public boolean hasLiveImplementors(JType type) {
-    if (!optimize) {
-      // Assume the worst case, that the provided type does have live implementors.
-      return true;
+  public boolean isJsTypeInterfaceWithPrototype(JType type) {
+    return isJsTypeInterface(type, true);
+  }
+
+  private boolean isJsTypeInterface(JType type, boolean hasPrototype) {
+    if (!type.isJsType() || !(type instanceof JInterfaceType)) {
+      return false;
     }
-    if (type instanceof JInterfaceType) {
-      for (JReferenceType impl : getTypes(classesByImplementingInterface.get(type.getName()))) {
-        if (isInstantiatedType((JClassType) impl)) {
-          return true;
-        }
-      }
-    }
-    return false;
+    String prototype = ((JInterfaceType) type).getJsPrototype();
+    return hasPrototype ? prototype != null : prototype == null;
   }
 
   public boolean castFailsTrivially(JReferenceType fromType, JReferenceType toType) {
@@ -883,9 +873,6 @@
       Iterables.addAll(castableDestinationTypes,
           getTypes(implementedInterfacesByClass.get(type.getName())));
     }
-    if (willCrossCastLikeJso(type)) {
-      ensureTypeExistsAndAppend(JProgram.JAVASCRIPTOBJECT, castableDestinationTypes);
-    }
     // Do not add itself if it is a JavaScriptObject subclass, add JavaScriptObject.
     if (type.isJsoType()) {
       ensureTypeExistsAndAppend(JProgram.JAVASCRIPTOBJECT, castableDestinationTypes);
@@ -1009,14 +996,6 @@
   }
 
   /**
-   * Whether the type or any supertypes is a JS type, optionally, only return true if
-   * one of the types has a js prototype.
-   */
-  public boolean isOrExtendsJsType(JType type, boolean mustHavePrototype) {
-    return getNearestJsType(type, mustHavePrototype) != null;
-  }
-
-  /**
    * Returns true if possibleSubType is a subclass of type, directly or indirectly.
    */
   public boolean isSubClass(JClassType type, JClassType possibleSubType) {
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
index 378736c..0c10cb1 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ArrayNormalizer.java
@@ -106,7 +106,7 @@
         elementType = JReferenceType.NULL_TYPE;
       }
 
-      if (program.typeOracle.willCrossCastLikeJso(elementType)) {
+      if (program.typeOracle.isEffectivelyJavaScriptObject(elementType)) {
         /*
          * treat types that are effectively JSO's as JSO's, for the purpose of
          * castability checking
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
index dc4d509..8e38b09 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/ImplementCastsAndTypeChecks.java
@@ -23,6 +23,7 @@
 import com.google.gwt.dev.jjs.ast.JCastOperation;
 import com.google.gwt.dev.jjs.ast.JExpression;
 import com.google.gwt.dev.jjs.ast.JInstanceOf;
+import com.google.gwt.dev.jjs.ast.JInterfaceType;
 import com.google.gwt.dev.jjs.ast.JMethod;
 import com.google.gwt.dev.jjs.ast.JMethodCall;
 import com.google.gwt.dev.jjs.ast.JModVisitor;
@@ -96,8 +97,8 @@
           // just remove the cast
           ctx.replaceMe(curExpr);
           return;
-        } else if (program.typeOracle.willCrossCastLikeJso(argType)
-            && program.typeOracle.willCrossCastLikeJso(refType)) {
+        } else if (program.typeOracle.isEffectivelyJavaScriptObject(argType)
+            && program.typeOracle.isEffectivelyJavaScriptObject(refType)) {
           // leave the cast instance for Pruner/CFA, remove in GenJSAST
           return;
         }
@@ -200,8 +201,8 @@
 
       boolean isTrivialCast = program.typeOracle.castSucceedsTrivially(argType, toType)
           // don't depend on type-tightener having run
-          || (program.typeOracle.willCrossCastLikeJso(argType)
-              && program.typeOracle.willCrossCastLikeJso(toType));
+          || (program.typeOracle.isEffectivelyJavaScriptObject(argType)
+              && program.typeOracle.isEffectivelyJavaScriptObject(toType));
       if (pruneTrivialCasts && isTrivialCast) {
         // trivially true if non-null; replace with a null test
         JBinaryOperation eq =
@@ -225,7 +226,7 @@
 
     assert EnumSet.of(TypeCategory.TYPE_JSO, TypeCategory.TYPE_JAVA_OBJECT_OR_JSO,
         TypeCategory.TYPE_JAVA_LANG_OBJECT, TypeCategory.TYPE_JAVA_LANG_STRING,
-        TypeCategory.TYPE_JAVA_OBJECT, TypeCategory.TYPE_JS_INTERFACE,
+        TypeCategory.TYPE_JAVA_OBJECT, TypeCategory.TYPE_JS_PROTOTYPE,
         TypeCategory.TYPE_JS_FUNCTION).contains(typeCategory);
 
     return typeCategory;
@@ -240,6 +241,7 @@
 
     TypeCategory targetTypeCategory = determineTypeCategoryForType(targetType);
     JMethod method = targetMethodByTypeCategory.get(targetTypeCategory);
+    assert method != null;
     JMethodCall call;
     if (overrideReturnType) {
       // Create a method call overriding the return type so that operations like Cast.dynamicCast
@@ -256,9 +258,9 @@
     }
     if (method.getParams().size() == 3) {
 
-     assert targetTypeCategory == TypeCategory.TYPE_JS_INTERFACE;
+     assert targetTypeCategory == TypeCategory.TYPE_JS_PROTOTYPE;
      call.addArg(program.getStringLiteral(sourceInfo,
-         program.typeOracle.getNearestJsType(targetType, true).getJsPrototype()));
+         ((JInterfaceType) targetType).getJsPrototype()));
     }
     return call;
   }
@@ -300,7 +302,7 @@
     this.instanceOfMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JAVA_LANG_STRING, program.getIndexedMethod("Cast.isJavaString"));
     this.instanceOfMethodsByTargetTypeCategory.put(
-        TypeCategory.TYPE_JS_INTERFACE, program.getIndexedMethod("Cast.instanceOfJsType"));
+        TypeCategory.TYPE_JS_PROTOTYPE, program.getIndexedMethod("Cast.instanceOfJsPrototype"));
     this.instanceOfMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JS_FUNCTION, program.getIndexedMethod("Cast.instanceOfJsFunction"));
 
@@ -316,7 +318,7 @@
     this.dynamicCastMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JAVA_LANG_STRING, program.getIndexedMethod("Cast.dynamicCastToString"));
     this.dynamicCastMethodsByTargetTypeCategory.put(
-        TypeCategory.TYPE_JS_INTERFACE, program.getIndexedMethod("Cast.dynamicCastWithPrototype"));
+        TypeCategory.TYPE_JS_PROTOTYPE, program.getIndexedMethod("Cast.dynamicCastWithPrototype"));
     this.dynamicCastMethodsByTargetTypeCategory.put(
         TypeCategory.TYPE_JS_FUNCTION, program.getIndexedMethod("Cast.dynamicCastToJsFunction"));
   }
diff --git a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
index 55cf8bc..5d25699 100644
--- a/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
+++ b/dev/core/src/com/google/gwt/dev/jjs/impl/TypeCategory.java
@@ -26,9 +26,18 @@
  * These are used in Cast checking and array implementation.
  */
 public  enum TypeCategory {
-    TYPE_JAVA_OBJECT, TYPE_JAVA_OBJECT_OR_JSO, TYPE_JSO, TYPE_JAVA_LANG_OBJECT,
-    TYPE_JAVA_LANG_STRING, TYPE_JS_INTERFACE, TYPE_PRIMITIVE_LONG, TYPE_PRIMITIVE_NUMBER,
-    TYPE_PRIMITIVE_BOOLEAN, TYPE_JS_FUNCTION;
+  /* Make sure this list is kept in sync with the one in Array.java */
+
+  TYPE_JAVA_OBJECT,
+  TYPE_JAVA_OBJECT_OR_JSO,
+  TYPE_JSO,
+  TYPE_JAVA_LANG_OBJECT,
+  TYPE_JAVA_LANG_STRING,
+  TYPE_JS_PROTOTYPE,
+  TYPE_JS_FUNCTION,
+  TYPE_PRIMITIVE_LONG,
+  TYPE_PRIMITIVE_NUMBER,
+  TYPE_PRIMITIVE_BOOLEAN;
 
   /**
    * Determines the type category for a specific type.
@@ -50,14 +59,13 @@
       return TypeCategory.TYPE_JAVA_LANG_OBJECT;
     } else if (type == program.getTypeJavaLangString()) {
       return TypeCategory.TYPE_JAVA_LANG_STRING;
-    } else if (program.typeOracle.willCrossCastLikeJso(type)) {
+    } else if (program.typeOracle.isEffectivelyJavaScriptObject(type)) {
       return TypeCategory.TYPE_JSO;
-    } else if (program.typeOracle.isDualJsoInterface(type) ||
-        program.typeOracle.isOrExtendsJsType(type, false) &&
-        !program.typeOracle.isOrExtendsJsType(type, true)) {
+    } else if (program.typeOracle.isDualJsoInterface(type)
+        || program.typeOracle.isJsTypeInterfaceWithoutPrototype(type)) {
       return TypeCategory.TYPE_JAVA_OBJECT_OR_JSO;
-    } else if (program.typeOracle.isOrExtendsJsType(type, true)) {
-      return TypeCategory.TYPE_JS_INTERFACE;
+    } else if (program.typeOracle.isJsTypeInterfaceWithPrototype(type)) {
+      return TypeCategory.TYPE_JS_PROTOTYPE;
     } else if (type.isJsFunction()) {
       return TypeCategory.TYPE_JS_FUNCTION;
     }
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
index 2fa6a19..54247a3 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Array.java
@@ -28,16 +28,17 @@
  * This class should contain only static methods or fields.
  */
 public final class Array {
-  // Array element type classes
+  // Array element type classes. Needs to be in sync with enums in TypeCategory.java.
   private static final int TYPE_JAVA_OBJECT = 0;
   private static final int TYPE_JAVA_OBJECT_OR_JSO = 1;
   private static final int TYPE_JSO = 2;
   private static final int TYPE_JAVA_LANG_OBJECT = 3;
   private static final int TYPE_JAVA_LANG_STRING = 4;
-  private static final int TYPE_JS_INTERFACE = 5;
-  private static final int TYPE_PRIMITIVE_LONG = 6;
-  private static final int TYPE_PRIMITIVE_NUMBER = 7;
-  private static final int TYPE_PRIMITIVE_BOOLEAN = 8;
+  private static final int TYPE_JS_PROTOTYPE = 5;
+  private static final int TYPE_JS_FUNCTION = 6;
+  private static final int TYPE_PRIMITIVE_LONG = 7;
+  private static final int TYPE_PRIMITIVE_NUMBER = 8;
+  private static final int TYPE_PRIMITIVE_BOOLEAN = 9;
 
   /**
    * Creates a copy of a subrange of the specified array.
diff --git a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
index a9466ad..b41383e 100644
--- a/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
+++ b/dev/core/super/com/google/gwt/dev/jjs/intrinsic/com/google/gwt/lang/Cast.java
@@ -106,7 +106,7 @@
     return (src != null) && canCast(src, dstId);
   }
 
-  static boolean instanceOfJsType(Object src, JavaScriptObject dstId, String jsType) {
+  static boolean instanceOfJsPrototype(Object src, JavaScriptObject dstId, String jsType) {
     return instanceOf(src, dstId) || jsInstanceOf(src, jsType);
   }
 
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
index 4918524..613af57 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -96,7 +96,7 @@
           "  public static boolean isJavaScriptObject(Object o) { return true; }",
           "  public static boolean instanceOfOrJso(Object src, int dst) { return false;}",
           "  public static boolean instanceOfJso(Object src) { return false;}",
-          "  public static boolean instanceOfJsType(Object src, JavaScriptObject dstId, String jsType)  { return false;}",
+          "  public static boolean instanceOfJsPrototype(Object src, JavaScriptObject dstId, String jsType)  { return false;}",
           "  public static boolean instanceOfJsFunction(Object src) { return false; }",
           "  public static native boolean isNull(Object a) /*-{ }-*/;",
           "  public static native boolean isNotNull(Object a) /*-{ }-*/;",
diff --git a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
index 808875a..f4ac73f 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JjsTypeTest.java
@@ -260,8 +260,8 @@
     assertCastableDestinationTypes(intfCloneable, intfCloneable, classObject);
     assertCastableDestinationTypes(intfIBase, intfIBase, classObject);
     assertCastableDestinationTypes(intfI, intfI, intfIBase, classObject);
-    assertCastableDestinationTypes(intfJ, intfJ, classJso, classObject);
-    assertCastableDestinationTypes(intfK, intfK, classJso, classObject);
+    assertCastableDestinationTypes(intfJ, intfJ, classObject);
+    assertCastableDestinationTypes(intfK, intfK, classObject);
     assertCastableDestinationTypes(classBase, classBase, classObject);
     assertCastableDestinationTypes(classA, classA, classObject, classBase);
     assertCastableDestinationTypes(classB, classB, classObject, classBase, intfIBase, intfI);
@@ -574,8 +574,8 @@
     // assertShouldSucceedTrivially(thatType, thisType));
     assertShouldNotFailTrivially(thisType, thatType);
     assertShouldNotFailTrivially(thatType, thisType);
-    assertTrue(typeOracle.canCrossCastLikeJso(thisType));
-    assertTrue(typeOracle.canCrossCastLikeJso(thatType));
+    assertTrue(typeOracle.canBeJavaScriptObject(thisType));
+    assertTrue(typeOracle.canBeJavaScriptObject(thatType));
   }
 
   private void assertShouldNotFailTrivially(JReferenceType thisType, JReferenceType thatType) {
diff --git a/user/test/com/google/gwt/core/client/interop/ElementLikeJsInterfaceImpl.java b/user/test/com/google/gwt/core/client/interop/ElementLikeJsInterfaceImpl.java
new file mode 100644
index 0000000..7a9d81a
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/ElementLikeJsInterfaceImpl.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.interop;
+
+/**
+ * Implements ElementLikeJsInterface.
+ */
+public class ElementLikeJsInterfaceImpl implements ElementLikeJsInterface {
+  @Override
+  public String getTagName() {
+    return "mytag";
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
index e76de02..27638e4 100644
--- a/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
+++ b/user/test/com/google/gwt/core/client/interop/JsTypeTest.java
@@ -172,11 +172,11 @@
   }-*/;
 
   public void testCasts() {
-    MyJsInterface myClass;
-    assertNotNull(myClass = (MyJsInterface) createMyJsInterface());
+    MyJsInterfaceWithPrototype myClass;
+    assertNotNull(myClass = (MyJsInterfaceWithPrototype) createMyJsInterface());
 
     try {
-      assertNotNull(myClass = (MyJsInterface) createNativeButton());
+      assertNotNull(myClass = (MyJsInterfaceWithPrototype) createNativeButton());
       fail();
     } catch (ClassCastException cce) {
       // Expected.
@@ -194,7 +194,7 @@
     assertNotNull(button);
   }
 
-  public void testInstanceOf_jsoWithSyntheticProto() {
+  public void testInstanceOf_jsoWithProto() {
     Object object = createMyJsInterface();
 
     assertTrue(object instanceof Object);
@@ -202,13 +202,16 @@
     assertFalse(object instanceof HTMLButtonElement);
     assertFalse(object instanceof HTMLElement);
     assertFalse(object instanceof Iterator);
-    assertTrue(object instanceof MyJsInterface);
+    assertTrue(object instanceof MyJsInterfaceWithPrototype);
+    assertFalse(object instanceof MyJsInterfaceWithPrototypeImpl);
     assertTrue(object instanceof ElementLikeJsInterface);
+    assertFalse(object instanceof ElementLikeJsInterfaceImpl);
     assertTrue(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
     assertFalse(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
+    assertFalse(object instanceof ConcreteJsType);
   }
 
-  public void testInstanceOf_jsoSansProto() {
+  public void testInstanceOf_jsoWithoutProto() {
     Object object = JavaScriptObject.createObject();
 
     assertTrue(object instanceof Object);
@@ -216,10 +219,13 @@
     assertFalse(object instanceof HTMLButtonElement);
     assertFalse(object instanceof HTMLElement);
     assertFalse(object instanceof Iterator);
-    assertFalse(object instanceof MyJsInterface);
+    assertFalse(object instanceof MyJsInterfaceWithPrototype);
+    assertFalse(object instanceof MyJsInterfaceWithPrototypeImpl);
     assertTrue(object instanceof ElementLikeJsInterface);
+    assertFalse(object instanceof ElementLikeJsInterfaceImpl);
     assertTrue(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
     assertFalse(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
+    assertFalse(object instanceof ConcreteJsType);
   }
 
   public void testInstanceOf_jsoWithNativeButtonProto() {
@@ -230,13 +236,70 @@
     assertTrue(object instanceof HTMLButtonElement);
     assertTrue(object instanceof HTMLElement);
     assertFalse(object instanceof Iterator);
-    assertFalse(object instanceof MyJsInterface);
+    assertFalse(object instanceof MyJsInterfaceWithPrototype);
+    assertFalse(object instanceof MyJsInterfaceWithPrototypeImpl);
     assertTrue(object instanceof ElementLikeJsInterface);
+    assertFalse(object instanceof ElementLikeJsInterfaceImpl);
     assertTrue(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
     assertTrue(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
+    assertFalse(object instanceof ConcreteJsType);
   }
 
-  public void testInstanceOf_javaImplementorOfInterfaceWithProto() {
+  public void testInstanceOf_implementsJsType() {
+    // Foils type tightening.
+    Object object = alwaysTrue() ? new ElementLikeJsInterfaceImpl() : new Object();
+
+    assertTrue(object instanceof Object);
+    assertFalse(object instanceof HTMLAnotherElement);
+    assertFalse(object instanceof HTMLButtonElement);
+    assertFalse(object instanceof HTMLElement);
+    assertFalse(object instanceof Iterator);
+    assertFalse(object instanceof MyJsInterfaceWithPrototype);
+    assertFalse(object instanceof MyJsInterfaceWithPrototypeImpl);
+    assertTrue(object instanceof ElementLikeJsInterface);
+    assertTrue(object instanceof ElementLikeJsInterfaceImpl);
+    assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
+    assertFalse(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
+    assertFalse(object instanceof ConcreteJsType);
+  }
+
+  public void testInstanceOf_implementsJsTypeWithPrototype() {
+    // Foils type tightening.
+    Object object = alwaysTrue() ? new MyJsInterfaceWithPrototypeImpl() : new Object();
+
+    assertTrue(object instanceof Object);
+    assertFalse(object instanceof HTMLAnotherElement);
+    assertFalse(object instanceof HTMLButtonElement);
+    assertFalse(object instanceof HTMLElement);
+    assertFalse(object instanceof Iterator);
+    assertTrue(object instanceof MyJsInterfaceWithPrototype);
+    assertTrue(object instanceof MyJsInterfaceWithPrototypeImpl);
+    assertFalse(object instanceof ElementLikeJsInterface);
+    assertFalse(object instanceof ElementLikeJsInterfaceImpl);
+    assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
+    assertFalse(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
+    assertFalse(object instanceof ConcreteJsType);
+  }
+
+  public void testInstanceOf_concreteJsType() {
+    // Foils type tightening.
+    Object object = alwaysTrue() ? new ConcreteJsType() : new Object();
+
+    assertTrue(object instanceof Object);
+    assertFalse(object instanceof HTMLAnotherElement);
+    assertFalse(object instanceof HTMLButtonElement);
+    assertFalse(object instanceof HTMLElement);
+    assertFalse(object instanceof Iterator);
+    assertFalse(object instanceof MyJsInterfaceWithPrototype);
+    assertFalse(object instanceof MyJsInterfaceWithPrototypeImpl);
+    assertFalse(object instanceof ElementLikeJsInterface);
+    assertFalse(object instanceof ElementLikeJsInterfaceImpl);
+    assertFalse(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
+    assertFalse(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
+    assertTrue(object instanceof ConcreteJsType);
+  }
+
+  public void testInstanceOf_extendsJsTypeWithProto() {
     // Foils type tightening.
     Object object = alwaysTrue() ? new MyCustomHtmlButtonWithIterator() : new Object();
 
@@ -251,7 +314,7 @@
      * the spec decides, fix JTypeOracle so that canTheoreticallyCast returns the appropriate
      * result, as well as add a test here that can be type-tightened.
      */
-    assertFalse(object instanceof MyJsInterface);
+    assertFalse(object instanceof MyJsInterfaceWithPrototype);
     assertTrue(object instanceof ElementLikeJsInterface);
     assertTrue(object instanceof MyJsInterfaceWithOnlyInstanceofReference);
     assertTrue(object instanceof MyJsPrototypeWithOnlyInstanceofReference);
@@ -262,7 +325,7 @@
     Object obj2 = createMyWrongNamespacedJsInterface();
 
     assertTrue(obj1 instanceof MyNamespacedJsInterface);
-    assertFalse(obj1 instanceof MyJsInterface);
+    assertFalse(obj1 instanceof MyJsInterfaceWithPrototype);
 
     assertFalse(obj2 instanceof MyNamespacedJsInterface);
   }
diff --git a/user/test/com/google/gwt/core/client/interop/MyClassExtendsJsPrototype.java b/user/test/com/google/gwt/core/client/interop/MyClassExtendsJsPrototype.java
index f2c0123..22c6543 100644
--- a/user/test/com/google/gwt/core/client/interop/MyClassExtendsJsPrototype.java
+++ b/user/test/com/google/gwt/core/client/interop/MyClassExtendsJsPrototype.java
@@ -15,7 +15,7 @@
  */
 package com.google.gwt.core.client.interop;
 
-class MyClassExtendsJsPrototype extends MyJsInterface.Prototype {
+class MyClassExtendsJsPrototype extends MyJsInterfaceWithPrototype.Prototype {
 
   MyClassExtendsJsPrototype() {
     setX(42);
diff --git a/user/test/com/google/gwt/core/client/interop/MyJsInterface.java b/user/test/com/google/gwt/core/client/interop/MyJsInterfaceWithPrototype.java
similarity index 91%
rename from user/test/com/google/gwt/core/client/interop/MyJsInterface.java
rename to user/test/com/google/gwt/core/client/interop/MyJsInterfaceWithPrototype.java
index 693aec4..e710eea 100644
--- a/user/test/com/google/gwt/core/client/interop/MyJsInterface.java
+++ b/user/test/com/google/gwt/core/client/interop/MyJsInterfaceWithPrototype.java
@@ -20,7 +20,7 @@
 import com.google.gwt.core.client.js.impl.PrototypeOfJsType;
 
 @JsType(prototype = "MyJsInterface")
-interface MyJsInterface {
+interface MyJsInterfaceWithPrototype {
 
   @JsProperty
   int getX();
@@ -31,7 +31,7 @@
   int sum(int bias);
 
   @PrototypeOfJsType
-  static class Prototype implements MyJsInterface {
+  static class Prototype implements MyJsInterfaceWithPrototype {
 
     @Override
     public int getX() {
diff --git a/user/test/com/google/gwt/core/client/interop/MyJsInterfaceWithPrototypeImpl.java b/user/test/com/google/gwt/core/client/interop/MyJsInterfaceWithPrototypeImpl.java
new file mode 100644
index 0000000..4ad44a9
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/interop/MyJsInterfaceWithPrototypeImpl.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2015 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package com.google.gwt.core.client.interop;
+
+/**
+ * Implements MyJsInterface.
+ */
+public class MyJsInterfaceWithPrototypeImpl implements MyJsInterfaceWithPrototype {
+  private int x;
+
+  @Override
+  public int getX() {
+    return x;
+  }
+
+  @Override
+  public void setX(int x) {
+    this.x = x;
+  }
+
+  @Override
+  public int sum(int bias) {
+    return bias;
+  }
+}