Make accesses to Error.stack property lazy

This significantly improves the performance on browsers
that does lazy stack evaluation (e.g. Chrome).

Change-Id: I7cf41abf3078e71f549198ebcc9e3fe1a66e533d
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 02a0a2c..f755e9a 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -170,9 +170,17 @@
 
     @Override
     public native void collect(Object t, Object jsThrown) /*-{
-      // TODO(goktug): optimize for Chrome by not evaluating stack (use Error.captureStackTrace)
-      t.stack = (jsThrown && jsThrown.stack) || @StackTraceCreator::makeException()().stack;
-     }-*/;
+      // Carefully crafted to delay the 'stack' property until stack trace construction as it is
+      // very costly in some browsers (e.g. Chrome).
+
+      var referenceThrown = jsThrown instanceof Object && "stack" in jsThrown
+          ? jsThrown
+          : @StackTraceCreator::makeException()();
+
+      @StackTraceCreator::addLazyProperty(*)(t, "stack", function() {
+        return referenceThrown.stack;
+      });
+    }-*/;
 
     @Override
     public StackTraceElement[] getStackTrace(Object t) {
@@ -378,6 +386,36 @@
     return e.stack ? e.stack.split('\n') : [];
   }-*/;
 
+  private static native void addLazyProperty(JavaScriptObject obj, String name,
+      JavaScriptObject getterFn) /*-{
+    if (Object.defineProperty) {
+      try {
+        Object.defineProperty(obj, name, {
+          configurable: true,
+          get: getterFn,
+          set: function(value) {
+            Object.defineProperty(this, name, {
+              value: value,
+              configurable: true,
+              writable: true,
+            });
+          },
+        });
+      } catch(defPropException) {
+        // This might happen because some old browsers like Safari5, IE8, have Object.defineProperty
+        // method which doesn't work on all objects.
+      };
+
+      // At this point we need to be sure that the property added, if not should fallback to eager.
+      if (name in obj) {
+        return;
+      }
+    }
+
+    // Fallback to eager property
+    obj[name] = getterFn();
+  }-*/;
+
   private static native <T> T splice(T arr, int length) /*-{
     (arr.length >= length) && arr.splice(0, length);
     return arr;