Fix erroneous boxing of primitive return values by $entry()

Bug: issue 8548
Change-Id: I99938731ad8db81737a45a1d641bf3bd12ea4e68
(cherry picked from commit 2b54a63ccc1d7f68229fb04ae47abc21bfa205d1)
diff --git a/user/src/com/google/gwt/core/client/impl/Impl.java b/user/src/com/google/gwt/core/client/impl/Impl.java
index f138450..8f605ca 100644
--- a/user/src/com/google/gwt/core/client/impl/Impl.java
+++ b/user/src/com/google/gwt/core/client/impl/Impl.java
@@ -90,7 +90,16 @@
   public static native JavaScriptObject entry(JavaScriptObject jsFunction) /*-{
     return function() {
       try {
-        return @com.google.gwt.core.client.impl.Impl::entry0(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)(jsFunction, this, arguments);
+        if (@com.google.gwt.core.client.GWT::isScript()()) {
+          return @com.google.gwt.core.client.impl.Impl::entry0(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)(jsFunction, this, arguments);
+        } else {
+          var _ = @com.google.gwt.core.client.impl.Impl::entry0(Ljava/lang/Object;Ljava/lang/Object;Ljava/lang/Object;)(jsFunction, this, arguments);
+          if (_ != null) {
+            // Unwraps for Development Mode (see #apply())
+            _ = _.val;
+          }
+          return _;
+        }
       } catch (e) {
         // This catch block is here to ensure that the finally block in entry0
         // will be executed correctly on IE6/7.  We can't put a catch Throwable
@@ -291,8 +300,8 @@
     } else {
       var _ = jsFunction.apply(thisObj, args);
       if (_ != null) {
-        // Wrap for Development Mode
-        _ = Object(_);
+        // Wrap for Development Mode (unwrapped in #entry())
+        _ = { val: _ };
       }
       return _;
     }
diff --git a/user/test/com/google/gwt/core/client/EntryTest.java b/user/test/com/google/gwt/core/client/EntryTest.java
new file mode 100644
index 0000000..6827682
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/EntryTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014 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;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+/**
+ * Tests for the $entry() method in JSNI.
+ */
+public class EntryTest extends GWTTestCase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.core.Core";
+  }
+
+  /**
+   * Tests that methods with primitive return types correctly return JS values
+   * when wrapped with {@code $entry} (rather than JS objects).
+   * <p>
+   * We test this with a boolean {@code false} that we coerce to a boolean. If the
+   * $entry-wrapped function returns it as a JS Boolean object, it'll coerce to
+   * {@code true} because it's non-null.
+   *
+   * @see <a href="https://code.google.com/p/google-web-toolkit/issues/detail?id=8548">issue 8548</a>
+   */
+  public native void testPrimitiveReturnType() /*-{
+    var assertStringEquals = @junit.framework.Assert::assertEquals(Ljava/lang/String;Ljava/lang/String;);
+    var assertFalse = @junit.framework.Assert::assertFalse(Z);
+    var assertTrue = @junit.framework.Assert::assertTrue(Z);
+
+    var assertIsBooleanValueFalse = function(shouldBeBooleanValueFalse) {
+      assertStringEquals("boolean", typeof shouldBeBooleanValueFalse);
+      assertFalse(!!shouldBeBooleanValueFalse);
+    };
+    var assertIsBooleanObjectFalse = function(shouldBeBooleanObjectFalse) {
+      assertStringEquals("object", typeof shouldBeBooleanObjectFalse);
+      assertTrue(shouldBeBooleanObjectFalse instanceof Boolean);
+      assertFalse(shouldBeBooleanObjectFalse.valueOf());
+      // that was the failing code in issue 8548, so test it explicitly:
+      assertTrue(!!shouldBeBooleanObjectFalse);
+    }
+
+    // Make sure we don't erroneously wrap values
+    var returnsBooleanValueFalse = $entry(function() { return false; });
+    assertIsBooleanValueFalse(returnsBooleanValueFalse());
+    // try if with a Java method returning a Java primitive boolean (issue 8548)
+    var returnsJavaPrimitiveBooleanFalse = $entry(@com.google.gwt.core.client.EntryTest::returnsFalse());
+    assertIsBooleanValueFalse(returnsJavaPrimitiveBooleanFalse());
+
+    // Make sure we don't erroneously unwrap objects
+    var returnsBooleanObjectFalse = $entry(function() { return new Boolean(false); });
+    assertIsBooleanObjectFalse(returnsBooleanObjectFalse());
+
+    // Just to be sure, make sure we round-trip values correctly:
+    var returnsFirstArgument = $entry(function(a) { return a; });
+    assertIsBooleanValueFalse(returnsFirstArgument(false));
+    assertIsBooleanObjectFalse(returnsFirstArgument(new Boolean(false)));
+  }-*/;
+
+  private static boolean returnsFalse() {
+    return false;
+  }
+}