Add tests for casts to special Native types.

And make the cast/instanceof GLOBAL.Object be typeof == "object"
instead of noop.

Change-Id: I02948db24c104fb42616a986b50d9b1b87d9b6d5
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 65c5f47..2c4831a 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
@@ -44,7 +44,6 @@
   TYPE_JAVA_OBJECT("", true, false),
   TYPE_JAVA_OBJECT_OR_JSO("AllowJso", true, false),
   TYPE_JSO("Jso"),
-  TYPE_NATIVE_ARRAY("NativeArray"),
   TYPE_ARRAY("Array"),
   TYPE_JSO_ARRAY("JsoArray", true, false),
   TYPE_JAVA_LANG_OBJECT("AllowJso", true, false),
@@ -54,6 +53,9 @@
   TYPE_JS_NATIVE("Native", false, true),
   TYPE_JS_UNKNOWN_NATIVE("UnknownNative"),
   TYPE_JS_FUNCTION("Function"),
+  TYPE_JS_OBJECT("JsObject"),
+  TYPE_JS_ARRAY("JsArray"),
+  // Primitive types are meant to be consecutive.
   TYPE_PRIMITIVE_LONG,
   TYPE_PRIMITIVE_NUMBER,
   TYPE_PRIMITIVE_BOOLEAN;
@@ -111,7 +113,7 @@
     } else if (getJsSpecialType(type) != null) {
       return getJsSpecialType(type);
     } else if (program.isUntypedArrayType(type)) {
-      return TypeCategory.TYPE_NATIVE_ARRAY;
+      return TypeCategory.TYPE_JS_ARRAY;
     } else if (type == program.getTypeJavaLangObject()) {
       return TypeCategory.TYPE_JAVA_LANG_OBJECT;
     } else if (program.getRepresentedAsNativeTypesDispatchMap().containsKey(type)) {
@@ -142,11 +144,11 @@
 
     switch (classType.getJsName()) {
       case "Object":
-        return TypeCategory.TYPE_JAVA_LANG_OBJECT;
+        return TypeCategory.TYPE_JS_OBJECT;
       case "Function":
         return TypeCategory.TYPE_JS_FUNCTION;
       case "Array":
-        return TypeCategory.TYPE_NATIVE_ARRAY;
+        return TypeCategory.TYPE_JS_ARRAY;
       case "Number":
         return TypeCategory.TYPE_JAVA_LANG_DOUBLE;
       case "String":
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 c16a522..0b7244a 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
@@ -33,19 +33,21 @@
   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_NATIVE_ARRAY = 3;
-  private static final int TYPE_ARRAY = 4;
-  private static final int TYPE_JSO_ARRAY = 5;
-  private static final int TYPE_JAVA_LANG_OBJECT = 6;
-  private static final int TYPE_JAVA_LANG_STRING = 7;
-  private static final int TYPE_JAVA_LANG_DOUBLE = 8;
-  private static final int TYPE_JAVA_LANG_BOOLEAN = 9;
-  private static final int TYPE_JS_NATIVE = 10;
-  private static final int TYPE_JS_UNKNOWN_NATIVE = 11;
-  private static final int TYPE_JS_FUNCTION = 12;
-  private static final int TYPE_PRIMITIVE_LONG = 13;
-  private static final int TYPE_PRIMITIVE_NUMBER = 14;
-  private static final int TYPE_PRIMITIVE_BOOLEAN = 15;
+  private static final int TYPE_ARRAY = 3;
+  private static final int TYPE_JSO_ARRAY = 4;
+  private static final int TYPE_JAVA_LANG_OBJECT = 5;
+  private static final int TYPE_JAVA_LANG_STRING = 6;
+  private static final int TYPE_JAVA_LANG_DOUBLE = 7;
+  private static final int TYPE_JAVA_LANG_BOOLEAN = 8;
+  private static final int TYPE_JS_NATIVE = 9;
+  private static final int TYPE_JS_UNKNOWN_NATIVE = 10;
+  private static final int TYPE_JS_FUNCTION = 11;
+  private static final int TYPE_JS_OBJECT = 12;
+  private static final int TYPE_JS_ARRAY = 13;
+  // Primitive types must be consecutive.
+  private static final int TYPE_PRIMITIVE_LONG = 14;
+  private static final int TYPE_PRIMITIVE_NUMBER = 15;
+  private static final int TYPE_PRIMITIVE_BOOLEAN = 16;
 
   public static <T> T[] stampJavaTypeInfo(Object array, T[] referenceType) {
     if (Array.getElementTypeCategory(referenceType) != TYPE_JS_UNKNOWN_NATIVE) {
@@ -187,6 +189,8 @@
         return Cast.instanceOfArray(value);
       case TYPE_JS_FUNCTION:
         return Cast.instanceOfFunction(value);
+      case TYPE_JS_OBJECT:
+        return Cast.instanceOfJsObject(value);
       case TYPE_JAVA_OBJECT:
         return Cast.canCast(value, Array.getElementTypeId(array));
       case TYPE_JSO:
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 10468f9..840ad28 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
@@ -111,8 +111,8 @@
   /**
    * Allow a cast to (untyped) array. This case covers single and multidimensional JsType arrays.
    */
-  static Object castToNativeArray(Object src) {
-    checkType(src == null || instanceOfNativeArray(src));
+  static Object castToJsArray(Object src) {
+    checkType(src == null || instanceOfJsArray(src));
     return src;
   }
 
@@ -141,6 +141,14 @@
   }
 
   /**
+   * Allow a dynamic cast to a native GLOBAL.Object if it is JavaScript object.
+   */
+  static Object castToJsObject(Object src) {
+    checkType(src == null || isJsObject(src));
+    return src;
+  }
+
+  /**
    * A dynamic cast that optionally checks for JsType prototypes.
    */
   static Object castToNative(Object src, JavaScriptObject jsType) {
@@ -188,7 +196,7 @@
   /**
    * Returns true if {@code src} is an array (native or not).
    */
-  static boolean instanceOfNativeArray(Object src) {
+  static boolean instanceOfJsArray(Object src) {
     return isArray(src);
   }
 
@@ -220,6 +228,13 @@
   }
 
   /**
+   * Returns true if the object is a JS object.
+   */
+  static boolean instanceOfJsObject(Object src) {
+    return (src != null) && isJsObject(src);
+  }
+
+  /**
    * Returns whether the Object is a function.
    */
   @HasNoSideEffects
@@ -228,6 +243,11 @@
   }-*/;
 
   @HasNoSideEffects
+  private static native boolean isJsObject(Object src)/*-{
+    return typeof(src) === "object";
+  }-*/;
+
+  @HasNoSideEffects
   static boolean isJavaScriptObject(Object src) {
     return isJsObjectOrFunction(src) && !Util.hasTypeMarker(src);
   }
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 5bb4166..3bf511a 100644
--- a/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
+++ b/dev/core/test/com/google/gwt/dev/jjs/JavaAstConstructor.java
@@ -94,7 +94,7 @@
           "  public static Object castToAllowJso(Object src, int dstId) { return src;}",
           "  public static Object castToArray(Object src) { return src;}",
           "  public static Object castToJsoArray(Object src) { return src;}",
-          "  public static Object castToNativeArray(Object src) { return src;}",
+          "  public static Object castToJsArray(Object src) { return src;}",
           "  public static Object castToJso(Object src) { return src;}",
           "  public static Object castToString(Object src) { return src;}",
           "  public static Object castToDouble(Object src) { return src;}",
@@ -102,6 +102,7 @@
           "  public static Object castToNative(Object src, JavaScriptObject type) { return src;}",
           "  public static Object castToUnknownNative(Object src) { return src;}",
           "  public static Object castToFunction(Object src) { return src; }",
+          "  public static Object castToJsObject(Object src) { return src; }",
           "  public static Class<?> getClass(Object src) { return null; }",
           "  public static boolean hasJavaObjectVirtualDispatch(Object o) { return true; }",
           "  public static boolean instanceOf(Object src, int dstId) { return false;}",
@@ -110,7 +111,7 @@
           "  public static boolean instanceOfBoolean(Object o) { return true; }",
           "  public static boolean instanceOfArray(Object src) { return false;}",
           "  public static boolean instanceOfJsoArray(Object src) { return false;}",
-          "  public static boolean instanceOfNativeArray(Object src) { return false;}",
+          "  public static boolean instanceOfJsArray(Object src) { return false;}",
           "  public static boolean instanceOfAllowJso(Object src, int dst) { return false;}",
           "  public static boolean instanceOfJso(Object src) { return false;}",
           "  public static boolean instanceOfUnknownNative(Object src)  { return false;}",
@@ -118,6 +119,7 @@
           "    return false;",
           "  }",
           "  public static boolean instanceOfFunction(Object src) { return false; }",
+          "  public static boolean instanceOfJsObject(Object src) { return false; }",
           "  public static boolean isArray(Object o) { return false; }",
           "  public static boolean isJavaScriptObject(Object o) { return true; }",
           "  public static native boolean isNull(Object a) /*-{ }-*/;",
diff --git a/user/test/com/google/gwt/core/interop/NativeJsTypeTest.java b/user/test/com/google/gwt/core/interop/NativeJsTypeTest.java
index 2ccdcec..262ffaa 100644
--- a/user/test/com/google/gwt/core/interop/NativeJsTypeTest.java
+++ b/user/test/com/google/gwt/core/interop/NativeJsTypeTest.java
@@ -18,9 +18,11 @@
 import static jsinterop.annotations.JsPackage.GLOBAL;
 
 import com.google.gwt.core.client.JavaScriptObject;
+import com.google.gwt.core.interop.JsTypeSpecialTypesTest.SomeFunctionalInterface;
 import com.google.gwt.junit.client.GWTTestCase;
 
 import javaemul.internal.annotations.DoNotInline;
+import jsinterop.annotations.JsFunction;
 import jsinterop.annotations.JsOverlay;
 import jsinterop.annotations.JsPackage;
 import jsinterop.annotations.JsType;
@@ -206,4 +208,132 @@
      assertEquals(new Integer(5),
          new NativeJsTypeWithStaticInitializationAndInstanceOverlayMethod().getObject());
   }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Function")
+  static class NativeFunction {
+  }
+
+  private static native Object createFunction() /*-{
+    return function() {};
+   }-*/;
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Array")
+  static class NativeArray {
+  }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Number")
+  static class NativeNumber {
+  }
+
+  private static native Object createNumber() /*-{
+    return 1;
+  }-*/;
+
+  private static native Object createBoxedNumber() /*-{
+    return new Number(1);
+  }-*/;
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "String")
+  static class NativeString {
+  }
+
+  private static native Object createBoxedString() /*-{
+    return new String("hello");
+  }-*/;
+
+  @JsFunction
+  interface SomeFunctionInterface {
+    void m();
+  }
+
+  static class SomeFunction implements SomeFunctionInterface {
+    public void m() {
+    }
+  }
+
+  public void testSpecialNativeInstanceOf() {
+    Object aJsFunction = new SomeFunction();
+    // True cases.
+    assertTrue(aJsFunction instanceof NativeFunction);
+    assertTrue(aJsFunction instanceof SomeFunctionalInterface);
+    // False cases.
+    assertFalse(aJsFunction instanceof NativeObject);
+    assertFalse(aJsFunction instanceof NativeArray);
+    assertFalse(aJsFunction instanceof NativeNumber);
+    assertFalse(aJsFunction instanceof NativeString);
+
+    Object anotherFunction = createFunction();
+    // True cases.
+    assertTrue(anotherFunction instanceof NativeFunction);
+    assertTrue(anotherFunction instanceof SomeFunctionalInterface);
+    // False cases.
+    assertFalse(anotherFunction instanceof NativeObject);
+    assertFalse(anotherFunction instanceof NativeArray);
+    assertFalse(anotherFunction instanceof NativeNumber);
+    assertFalse(anotherFunction instanceof NativeString);
+
+    Object aString = "Hello";
+    // True cases.
+    assertTrue(aString instanceof NativeString);
+    // False cases.
+    assertFalse(aString instanceof NativeFunction);
+    assertFalse(aString instanceof NativeObject);
+    assertFalse(aString instanceof NativeArray);
+    assertFalse(aString instanceof NativeNumber);
+
+    Object aBoxedString = createBoxedString();
+    // True cases.
+    // Note that boxed strings are (surprisingly) not strings but objects.
+    assertTrue(aBoxedString instanceof NativeObject);
+    // False cases.
+    assertFalse(aBoxedString instanceof NativeFunction);
+    assertFalse(aBoxedString instanceof NativeArray);
+    assertFalse(aBoxedString instanceof NativeNumber);
+    assertFalse(aBoxedString instanceof NativeString);
+
+    Object anArray = new String[0];
+    // True cases.
+    assertTrue(anArray instanceof NativeArray);
+    assertTrue(anArray instanceof NativeObject);
+    // False cases.
+    assertFalse(anArray instanceof NativeFunction);
+    assertFalse(anArray instanceof NativeNumber);
+    assertFalse(anArray instanceof NativeString);
+
+    Object aNativeArray = JavaScriptObject.createArray();
+    // True cases.
+    assertTrue(aNativeArray instanceof NativeArray);
+    assertTrue(anArray instanceof NativeObject);
+    // False cases.
+    assertFalse(aNativeArray instanceof NativeFunction);
+    assertFalse(aNativeArray instanceof NativeNumber);
+    assertFalse(aNativeArray instanceof NativeString);
+
+    Object aNumber = new Double(3);
+    // True cases.
+    assertTrue(aNumber instanceof NativeNumber);
+    // False cases.
+    assertFalse(aNumber instanceof NativeArray);
+    assertFalse(aNumber instanceof NativeObject);
+    assertFalse(aNumber instanceof NativeFunction);
+    assertFalse(aNumber instanceof NativeString);
+
+    Object anotherNumber = createNumber();
+    // True cases.
+    assertTrue(anotherNumber instanceof NativeNumber);
+    // False cases.
+    assertFalse(anotherNumber instanceof NativeArray);
+    assertFalse(anotherNumber instanceof NativeObject);
+    assertFalse(anotherNumber instanceof NativeFunction);
+    assertFalse(anotherNumber instanceof NativeString);
+
+    Object aBoxedNumber = createBoxedNumber();
+    // True cases.
+    assertTrue(aBoxedNumber instanceof NativeObject);
+    // False cases.
+    assertFalse(aBoxedNumber instanceof NativeNumber);
+    assertFalse(aBoxedNumber instanceof NativeArray);
+    assertFalse(aBoxedNumber instanceof NativeFunction);
+    assertFalse(aBoxedNumber instanceof NativeString);
+  }
 }
diff --git a/user/test/com/google/gwt/dev/jjs/optimized/CastOptimizationTest.java b/user/test/com/google/gwt/dev/jjs/optimized/CastOptimizationTest.java
index 500b198..c4d14cc 100644
--- a/user/test/com/google/gwt/dev/jjs/optimized/CastOptimizationTest.java
+++ b/user/test/com/google/gwt/dev/jjs/optimized/CastOptimizationTest.java
@@ -21,6 +21,7 @@
 
 import java.util.Random;
 
+import jsinterop.annotations.JsPackage;
 import jsinterop.annotations.JsType;
 
 /**
@@ -40,6 +41,26 @@
     protected JsoTestObject() { }
   }
 
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
+  static class NativeObject {
+  }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Function")
+  static class NativeFunction {
+  }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Array")
+  static class NativeArray {
+  }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Number")
+  static class NativeNumber {
+  }
+
+  @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "String")
+  static class NativeString {
+  }
+
   private static Object field;
 
   private static int randomNumber = new Random().nextInt(42);
@@ -86,6 +107,22 @@
     return ((String) field);
   }
 
+  public static NativeFunction castNativeFunction() {
+    return ((NativeFunction) field);
+  }
+
+  public static NativeObject castNativeObject() {
+    return ((NativeObject) field);
+  }
+
+  public static NativeArray castNativeArray() {
+    return ((NativeArray) field);
+  }
+
+  public static NativeNumber castNativeNumber() {
+    return ((NativeNumber) field);
+  }
+
   private static native String getGeneratedCastFunctionDefinition() /*-{
     return function() {
       @CastOptimizationTest::castOp()();
@@ -93,6 +130,10 @@
       @CastOptimizationTest::castOpDualJso()();
       @CastOptimizationTest::castOpJsType()();
       @CastOptimizationTest::castOpString()();
+      @CastOptimizationTest::castNativeFunction()();
+      @CastOptimizationTest::castNativeNumber()();
+      @CastOptimizationTest::castNativeArray()();
+      @CastOptimizationTest::castNativeObject()();
     }.toString();
   }-*/;