Remove list of JavaScriptException object properties:
- In Development Mode: always
- In Production Mode: when compiler.stackTrace = emulated

Fixes Issues: 3974

Review at http://gwt-code-reviews.appspot.com/1310802

Review by: scottb@google.com

git-svn-id: https://google-web-toolkit.googlecode.com/svn/trunk@9672 8db76d5a-ed1c-0410-87a9-c151d255dfc7
diff --git a/user/src/com/google/gwt/core/client/JavaScriptException.java b/user/src/com/google/gwt/core/client/JavaScriptException.java
index 495fce7..36efebc 100644
--- a/user/src/com/google/gwt/core/client/JavaScriptException.java
+++ b/user/src/com/google/gwt/core/client/JavaScriptException.java
@@ -74,32 +74,11 @@
   }-*/;
 
   private static String getProperties(Object e) {
-    return (e instanceof JavaScriptObject)
-        ? getProperties0((JavaScriptObject) e) : "";
+    return (GWT.isScript() && e instanceof JavaScriptObject)
+        ? StackTraceCreator.getProperties((JavaScriptObject) e) : "";
   }
 
   /**
-   * Returns the list of properties of an unexpected JavaScript exception.
-   */
-  private static native String getProperties0(JavaScriptObject e) /*-{
-    var result = "";
-    try {
-      for (var prop in e) {
-        if (prop != "name" && prop != "message" && prop != "toString") {
-          try {
-            result += "\n " + prop + ": " + e[prop];
-          } catch (ignored) {
-            // Skip the property if it threw an exception.
-          }
-        }
-      }
-    } catch (ignored) {
-      // If we can't do "in" on the exception, just return what we have.
-    }
-    return result;
-  }-*/;
-
-  /**
    * The original description of the JavaScript exception this class wraps,
    * initialized as <code>e.message</code>.
    */
diff --git a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
index cee201c..768ef90 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -84,6 +84,27 @@
     }
 
     /**
+     * Returns the list of properties of an unexpected JavaScript exception.
+     */
+    public native String getProperties(JavaScriptObject e) /*-{
+      var result = "";
+      try {
+        for (var prop in e) {
+          if (prop != "name" && prop != "message" && prop != "toString") {
+            try {
+              result += "\n " + prop + ": " + e[prop];
+            } catch (ignored) {
+              // Skip the property if it threw an exception.
+            }
+          }
+        }
+      } catch (ignored) {
+        // If we can't do "in" on the exception, just return what we have.
+      }
+      return result;
+    }-*/;
+
+    /**
      * Attempt to infer the stack from an unknown JavaScriptObject that had been
      * thrown. The default implementation just returns an empty array.
      * 
@@ -162,6 +183,17 @@
       t.setStackTrace(stackTrace);
     }
 
+    /**
+     * When compiler.stackMode = emulated, return an empty string, rather than a
+     * list of properties, since the additional information regarding the origin
+     * of the JavaScriptException, relative to compiled JavaScript source code,
+     * adds no real value, since we have fully emulated stack traces.
+     */
+    @Override
+    public String getProperties(JavaScriptObject e) {
+      return "";
+    }
+
     @Override
     public JsArrayString inferFrom(JavaScriptObject e) {
       throw new RuntimeException("Should not reach here");
@@ -390,6 +422,20 @@
   }
 
   /**
+   * Returns the list of properties of an unexpected JavaScript exception,
+   * unless compiler.stackMode = emulated, in which case the empty string is
+   * returned. This method should only be called in Production Mode.
+   */
+  public static String getProperties(JavaScriptObject e) {
+    if (!GWT.isScript()) {
+      throw new RuntimeException(
+          "StackTraceCreator should only be called in Production Mode");
+    }
+
+    return GWT.<Collector> create(Collector.class).getProperties(e);
+  }
+
+  /**
    * Create a stack trace based on the current execution stack. This method
    * should only be called in Production Mode.
    */
diff --git a/user/test/com/google/gwt/core/client/JavaScriptExceptionTest.java b/user/test/com/google/gwt/core/client/JavaScriptExceptionTest.java
index 2c5bd21..cbd8e17 100644
--- a/user/test/com/google/gwt/core/client/JavaScriptExceptionTest.java
+++ b/user/test/com/google/gwt/core/client/JavaScriptExceptionTest.java
@@ -16,6 +16,8 @@
 package com.google.gwt.core.client;
 
 import com.google.gwt.junit.client.GWTTestCase;
+import com.google.gwt.junit.client.WithProperties;
+import com.google.gwt.junit.client.WithProperties.Property;
 
 /**
  * Any JavaScript exceptions occurring within JSNI methods are wrapped as this
@@ -53,6 +55,35 @@
     @com.google.gwt.core.client.JavaScriptExceptionTest::throwJava(Ljava/lang/Throwable;)(t);
   }-*/;
 
+  public void assertJsoProperties(boolean extraPropertiesShouldBePresent) {
+    JavaScriptObject jso = makeJSO();
+    try {
+      throwNative(jso);
+      fail();
+    } catch (JavaScriptException e) {
+      assertEquals("myName", e.getName());
+      assertEquals("myDescription", e.getDescription());
+      assertSame(jso, e.getException());
+      assertTrue(e.getMessage().contains("myName"));
+      assertTrue(e.getMessage().contains("myDescription"));
+      if (extraPropertiesShouldBePresent) {
+        assertTrue(
+            "message does not contain 'extraField', but should: "
+                + e.getMessage(), e.getMessage().contains("extraField"));
+        assertTrue(
+            "message does not contain 'extraData', but should:"
+                + e.getMessage(), e.getMessage().contains("extraData"));
+      } else {
+        assertFalse(
+            "message contains 'extraField', but shouldn't: " + e.getMessage(),
+            e.getMessage().contains("extraField"));
+        assertFalse(
+            "message contains 'extraData', but shouldn't:" + e.getMessage(),
+            e.getMessage().contains("extraData"));
+      }
+    }
+  }
+
   /**
    * This test doesn't work in Development Mode yet; we'd need a way to throw
    * true native objects as exceptions. Windows/IE is the deal killer right now
@@ -82,21 +113,46 @@
       assertSame(e, t);
     }
   }
+  
+  @WithProperties({
+    @Property(name = "compiler.stackMode", value = "emulated")
+  })
+  public void testJsoStackModeEmulated() {
+    /**
+     * Whether we're in Development Mode, or in Production Mode with
+     * compiler.stackMode = emulated, extra properties should not be present.
+     * 
+     * @see StackTraceCreator#getProperties(JavaScriptObject)
+     */
+    assertJsoProperties(false);
+  }
+  
+  @WithProperties({
+    @Property(name = "compiler.stackMode", value = "native")
+  })
+  public void testJsoStackModeNative() {
+    /**
+     * In Production Mode with compiler.stackMode = native, extra properties
+     * should be present. In Development Mode, extra properties should not be
+     * present.
+     * 
+     * @see StackTraceCreator#getProperties(JavaScriptObject)
+     */
+    assertJsoProperties(GWT.isScript());
+  }
 
-  public void testJso() {
-    JavaScriptObject jso = makeJSO();
-    try {
-      throwNative(jso);
-      fail();
-    } catch (JavaScriptException e) {
-      assertEquals("myName", e.getName());
-      assertEquals("myDescription", e.getDescription());
-      assertSame(jso, e.getException());
-      assertTrue(e.getMessage().contains("myName"));
-      assertTrue(e.getMessage().contains("myDescription"));
-      assertTrue(e.getMessage().contains("extraField"));
-      assertTrue(e.getMessage().contains("extraData"));
-    }
+  @WithProperties({
+    @Property(name = "compiler.stackMode", value = "strip")
+  })
+  public void testJsoStackModeStrip() {
+    /**
+     * In Production Mode with compiler.stackMode = strip, extra properties
+     * should be present. In Development Mode, extra properties should not be
+     * present.
+     * 
+     * @see StackTraceCreator#getProperties(JavaScriptObject)
+     */
+    assertJsoProperties(GWT.isScript());
   }
 
   public void testNull() {