Fix isJavaScriptObject to not treat boolean/number as JSO

JsJsonBoolean/JsJsonNumber updated to avoid issue (since those are
JSOs that relied on unboxing behavior)

Change-Id: I53b5150fdc6060133af4b9c95876e8c16492e1b5
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 122fa90..4805bdd 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
@@ -161,7 +161,7 @@
 
   @HasNoSideEffects
   static boolean isJavaScriptObject(Object src) {
-    return !isJavaString(src) && !Util.hasTypeMarker(src);
+    return isJsObjectOrFunction(src) && !Util.hasTypeMarker(src);
   }
 
   /**
@@ -278,6 +278,11 @@
     return o;
   }
 
+  @HasNoSideEffects
+  static native boolean isJsObjectOrFunction(Object src) /*-{
+    return typeof(src) === "object" || typeof(src) === "function";
+  }-*/;
+
   // NOTE: if any of these three are edited, update JProgram.DispatchType's constructor
   /**
    * Returns whether the Object is a Java String.
diff --git a/elemental/src/elemental/js/json/JsJsonBoolean.java b/elemental/src/elemental/js/json/JsJsonBoolean.java
index c3d0c1b..66192d1 100644
--- a/elemental/src/elemental/js/json/JsJsonBoolean.java
+++ b/elemental/src/elemental/js/json/JsJsonBoolean.java
@@ -18,7 +18,7 @@
 import elemental.json.JsonBoolean;
 
 /**
- * Client-side 'zero overhead' JSO implementation using extension method
+ * Client-side 'low overhead' JSO implementation using extension method
  * technique.
  */
 final public class JsJsonBoolean extends JsJsonValue
@@ -32,8 +32,7 @@
    * MAGIC: primitive boolean cast to object interface.
    */
   private static native JsJsonBoolean createProd(boolean bool) /*-{
-    // box for DevMode, not ProdMode
-    return @com.google.gwt.core.client.GWT::isScript()() ? bool : Object(bool);
+    return Object(bool);
   }-*/;
 
   protected JsJsonBoolean() {
@@ -44,6 +43,6 @@
   }
 
   private native boolean valueProd() /*-{
-    return @elemental.js.json.JsJsonValue::debox(Lelemental/json/JsonValue;)(this);
+    return this && this.valueOf();
   }-*/;
 }
diff --git a/elemental/src/elemental/js/json/JsJsonNumber.java b/elemental/src/elemental/js/json/JsJsonNumber.java
index 1d0e513..b9a444c 100644
--- a/elemental/src/elemental/js/json/JsJsonNumber.java
+++ b/elemental/src/elemental/js/json/JsJsonNumber.java
@@ -18,7 +18,7 @@
 import elemental.json.JsonNumber;
 
 /**
- * Client-side 'zero overhead' JSO implementation using extension method
+ * Client-side 'low overhead' JSO implementation using extension method
  * technique.
  */
 final public class JsJsonNumber extends JsJsonValue
@@ -32,8 +32,7 @@
    * MAGIC: primitive number cast to object interface.
    */
   private static native JsJsonNumber createProd(double number) /*-{
-    // box for DevMode, not ProdMode
-    return @com.google.gwt.core.client.GWT::isScript()() ? number : Object(number);
+    return Object(number);
   }-*/;
 
   protected JsJsonNumber() {
@@ -44,6 +43,6 @@
   }
 
   private native double valueProd() /*-{
-    return @elemental.js.json.JsJsonValue::debox(Lelemental/json/JsonValue;)(this);
+    return this && this.valueOf();
   }-*/;
 }
diff --git a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
index 9f9d6a0..b669889 100644
--- a/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
+++ b/user/test/com/google/gwt/dev/jjs/test/SingleJsoImplTest.java
@@ -585,6 +585,23 @@
     return {};
   }-*/;
 
+  interface JsFunction {
+    int call();
+  }
+
+  final static class JsFunctionImpl extends JavaScriptObject implements JsFunction {
+    protected JsFunctionImpl() {
+    }
+
+    static native JsFunction makeFunc() /*-{
+      return function() { return 42; };
+    }-*/;
+
+    public native int call() /*-{
+      return this();
+    }-*/;
+  }
+
   @Override
   public String getModuleName() {
     return "com.google.gwt.dev.jjs.CompilerSuite";
@@ -983,4 +1000,16 @@
     assertNotNull(o);
     assertTrue(o.isOk());
   }
+
+  public void testJsFunctionCastsAsJso() {
+    JsFunction func = JsFunctionImpl.makeFunc();
+    assertTrue(func instanceof JavaScriptObject);
+    JavaScriptObject jso = (JavaScriptObject) func;
+    JsFunction func2 = (JsFunction) jso;
+    if (func2 instanceof JavaScriptObject) {
+      assertEquals(42, func2.call());
+    } else {
+      fail("Function should return true for instanceof JSO");
+    }
+  }
 }
diff --git a/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java b/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java
index 7975d66..b2c264c 100644
--- a/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/BooleanTest.java
@@ -15,6 +15,7 @@
  */
 package com.google.gwt.emultest.java.lang;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
@@ -93,6 +94,16 @@
     }
   }
 
+  public void testDoesNotCastToJso() {
+    try {
+      Boolean b = true;
+      Object o = b;
+      JavaScriptObject jso = (JavaScriptObject) o;
+      fail("Boolean should fail to cast to a JSO");
+    } catch (ClassCastException e) {
+    }
+  }
+
   public void testEqualityNormalizer() {
     Boolean b = false;
     if (b != null) {
diff --git a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
index e18693d..7fb15e5 100644
--- a/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
+++ b/user/test/com/google/gwt/emultest/java/lang/DoubleTest.java
@@ -16,6 +16,7 @@
 
 package com.google.gwt.emultest.java.lang;
 
+import com.google.gwt.core.client.JavaScriptObject;
 import com.google.gwt.junit.client.GWTTestCase;
 
 /**
@@ -105,6 +106,16 @@
     }
   }
 
+  public void testDoesNotCastToJso() {
+    try {
+      Double d = 1.2;
+      Object o = d;
+      JavaScriptObject jso = (JavaScriptObject) o;
+      fail("Double should fail to cast to a JSO");
+    } catch (ClassCastException e) {
+    }
+  }
+
   public void testEqualityNormalizer() {
     Double d = 0.0;
     if (d != null) {