Makes some change to Collector for further refactoring

- Finally gets rid of explicit inferFrom API in Collectors
- Introduces CollectorModern as a base for CollectorMoz and CollectorChrome
- CollectorChrome no longer extends CollectorMoz

Change-Id: I496c2358180662e54ff50cd2a5c08b29f505e314
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 9384bbd..757413b 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -57,22 +57,7 @@
 
     public abstract JavaScriptObject collect();
 
-    protected StackTraceElement[] getStackTrace(JsArrayString stack) {
-      if (stack.length() == 0) {
-        return null;
-      }
-      int length = stack.length();
-      StackTraceElement[] stackTrace = new StackTraceElement[length];
-      for (int i = 0; i < length; i++) {
-        stackTrace[i] = new StackTraceElement(UNKNOWN, stack.get(i), null, LINE_NUMBER_UNKNOWN);
-      }
-      return stackTrace;
-    }
-
-    /**
-     * Attempt to infer the stack from an unknown JavaScriptObject that had been thrown.
-     */
-    public abstract JsArrayString inferFrom(JavaScriptObject e);
+    public abstract StackTraceElement[] getStackTrace(JavaScriptObject stackInfo);
   }
 
   /**
@@ -113,8 +98,9 @@
     }-*/;
 
     @Override
-    public JsArrayString inferFrom(JavaScriptObject e) {
-      return getFnStack(e);
+    public StackTraceElement[] getStackTrace(JavaScriptObject stackInfo) {
+      JsArrayString stack = getFnStack(stackInfo);
+      return fnStacktoSte(stack);
     }
   }
 
@@ -138,14 +124,8 @@
     }-*/;
 
     @Override
-    public JsArrayString inferFrom(JavaScriptObject e) {
-      return getFnStack(e);
-    }
-
-    @Override
-    protected StackTraceElement[] getStackTrace(JsArrayString st) {
-      JsArray<JsArrayString> stack = st.cast();
-
+    public StackTraceElement[] getStackTrace(JavaScriptObject stackInfo) {
+      JsArray<JsArrayString> stack = getFnStack(stackInfo).cast();
       if (stack.length() == 0) {
         return null;
       }
@@ -173,13 +153,10 @@
   }
 
   /**
-   * Mozilla provides a <code>stack</code> property in thrown objects.
+   * Modern browsers provide a <code>stack</code> property in thrown objects.
    */
-  static class CollectorMoz extends Collector {
-    /**
-     * This implementation doesn't suffer from the limitations of crawling
-     * <code>caller</code> since Mozilla provides proper activation records.
-     */
+  abstract static class CollectorModern extends Collector {
+
     @Override
     public JavaScriptObject collect() {
       return makeException();
@@ -195,24 +172,28 @@
         return e;
       }
     }-*/;
+  }
+
+  static class CollectorMoz extends CollectorModern {
 
     @Override
-    public JsArrayString inferFrom(JavaScriptObject e) {
-      JsArrayString stack = getStack(e);
+    public StackTraceElement[] getStackTrace(JavaScriptObject stackInfo) {
+      JsArrayString stack = inferFrom(stackInfo);
+      return fnStacktoSte(stack);
+    }
+
+    private JsArrayString inferFrom(JavaScriptObject e) {
+      JsArrayString stack = split(e);
       for (int i = 0, j = stack.length(); i < j; i++) {
         stack.set(i, extractName(stack.get(i)));
       }
       return stack;
     }
 
-    private native JsArrayString getStack(JavaScriptObject e) /*-{
-      return (e && e.stack) ? e.stack.split('\n') : [];
-    }-*/;
-
     /**
      * Extract the name of a function from it's toString() representation.
      */
-    protected String extractName(String fnToString) {
+    private String extractName(String fnToString) {
       String toReturn = "";
       fnToString = fnToString.trim();
       int index = fnToString.indexOf("(");
@@ -250,7 +231,7 @@
    * at Type.functionName [as methodName] (file.js:1:2)
    * </pre>
    */
-  static class CollectorChrome extends CollectorMoz {
+  static class CollectorChrome extends CollectorModern {
 
     static {
       increaseChromeStackTraceLimit();
@@ -262,9 +243,12 @@
       Error.stackTraceLimit = 128;
     }-*/;
 
-    @Override
-    public JsArrayString inferFrom(JavaScriptObject e) {
-      JsArrayString stack = super.inferFrom(e);
+    private JsArrayString inferFrom(JavaScriptObject stackInfo) {
+      JsArrayString stack = split(stackInfo);
+      for (int i = 0, j = stack.length(); i < j; i++) {
+        stack.set(i, extractName(stack.get(i)));
+      }
+
       if (stack.length() > 0 && stack.get(0).startsWith(ANONYMOUS + "@@")) {
         // Chrome contains the error itself as the first line of the stack (iOS doesn't).
         stack = splice(stack, 1);
@@ -272,8 +256,7 @@
       return stack;
     }
 
-    @Override
-    protected String extractName(String fnToString) {
+    private String extractName(String fnToString) {
       String extractedName = ANONYMOUS;
       String location = "";
 
@@ -326,7 +309,9 @@
     }
 
     @Override
-    protected StackTraceElement[] getStackTrace(JsArrayString stack) {
+    public StackTraceElement[] getStackTrace(JavaScriptObject stackInfo) {
+      JsArrayString stack = inferFrom(stackInfo);
+
       int length = stack.length();
       if (length == 0) {
         return null;
@@ -383,13 +368,8 @@
     }
 
     @Override
-    protected StackTraceElement[] getStackTrace(JsArrayString stack) {
-      return null;
-    }
-
-    @Override
-    public JsArrayString inferFrom(JavaScriptObject e) {
-      return null;
+    public StackTraceElement[] getStackTrace(JavaScriptObject stackInfo) {
+      return new StackTraceElement[0];
     }
   }
 
@@ -411,8 +391,7 @@
 
   private static void constructStackTrace(Throwable t, Object thrown, boolean strip) {
     JavaScriptObject e = (thrown instanceof JavaScriptObject) ? (JavaScriptObject) thrown : null;
-    JsArrayString stack = collector.inferFrom(e);
-    StackTraceElement[] stackTrace = collector.getStackTrace(stack);
+    StackTraceElement[] stackTrace = collector.getStackTrace(e);
     if (stackTrace != null) {
       if (strip) {
         stackTrace = dropInternalFrames(stackTrace);
@@ -447,6 +426,18 @@
     return "stack" in new Error; // Checked via 'in' to avoid execution of stack getter in Chrome
   }-*/;
 
+  private static StackTraceElement[] fnStacktoSte(JsArrayString stack) {
+    int length = stack.length();
+    if (length == 0) {
+      return null;
+    }
+    StackTraceElement[] stackTrace = new StackTraceElement[length];
+    for (int i = 0; i < length; i++) {
+      stackTrace[i] = new StackTraceElement(UNKNOWN, stack.get(i), null, LINE_NUMBER_UNKNOWN);
+    }
+    return stackTrace;
+  }
+
   private static native JsArrayString getFnStack(JavaScriptObject e) /*-{
     return (e && e.fnStack && e.fnStack instanceof Array) ? e.fnStack : [];
   }-*/;
@@ -455,12 +446,17 @@
     return fn.name || (fn.name = @StackTraceCreator::extractFunctionName(*)(fn.toString()));
   }-*/;
 
+  // Visible for testing
   static native String extractFunctionName(String fnName) /*-{
     var fnRE = /function(?:\s+([\w$]+))?\s*\(/;
     var match = fnRE.exec(fnName);
     return (match && match[1]) || @StackTraceCreator::ANONYMOUS;
   }-*/;
 
+  private static native JsArrayString split(JavaScriptObject e) /*-{
+    return (e && e.stack) ? e.stack.split('\n') : [];
+  }-*/;
+
   private static native <T> T splice(T arr, int length) /*-{
     (arr.length >= length) && arr.splice(0, length);
     return arr;
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
index 6123e16..9f9b04a 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
@@ -18,7 +18,7 @@
 import static com.google.gwt.core.client.impl.StackTraceCreator.extractFunctionName;
 
 import com.google.gwt.core.client.JavaScriptObject;
-import com.google.gwt.core.client.JsArrayString;
+import com.google.gwt.core.client.impl.StackTraceCreator.Collector;
 import com.google.gwt.core.client.impl.StackTraceCreator.CollectorChrome;
 import com.google.gwt.core.client.impl.StackTraceCreator.CollectorChromeNoSourceMap;
 import com.google.gwt.core.client.impl.StackTraceCreator.CollectorMoz;
@@ -208,10 +208,9 @@
     return new StackTraceElement("Unknown", methodName, fileName, lineNumber);
   }
 
-  private static void assertStackTrace(JavaScriptObject exception, CollectorMoz collector,
+  private static void assertStackTrace(JavaScriptObject exception, Collector collector,
       StackTraceElement[] expected) {
-    JsArrayString stack = collector.inferFrom(exception);
-    assertEquals(expected, collector.getStackTrace(stack));
+    assertEquals(expected, collector.getStackTrace(exception));
   }
 
   private static void assertEquals(StackTraceElement[] expecteds, StackTraceElement[] actuals) {