Major revamp of stack trace tests

Added more tests and more coverage for different modes.

Includes a small patch to Chrome collector as tests revealed
a small bug with it.

Change-Id: Ie98fe6b569b3c47e475ae0fd847cacd90a6a823a
Review-Link: https://gwt-review.googlesource.com/#/c/7771/
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 2087943..9384bbd 100644
--- a/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
+++ b/user/src/com/google/gwt/core/client/impl/StackTraceCreator.java
@@ -328,6 +328,9 @@
     @Override
     protected StackTraceElement[] getStackTrace(JsArrayString stack) {
       int length = stack.length();
+      if (length == 0) {
+        return null;
+      }
       StackTraceElement[] stackTrace = new StackTraceElement[length];
       for (int i = 0; i < length; i++) {
         String stackElements[] = stack.get(i).split("@@");
diff --git a/user/test/com/google/gwt/core/CoreSuite.java b/user/test/com/google/gwt/core/CoreSuite.java
index 95caf03..60a4ebb 100644
--- a/user/test/com/google/gwt/core/CoreSuite.java
+++ b/user/test/com/google/gwt/core/CoreSuite.java
@@ -29,8 +29,10 @@
 import com.google.gwt.core.client.impl.ImplTest;
 import com.google.gwt.core.client.impl.SchedulerImplTest;
 import com.google.gwt.core.client.impl.StackTraceCreatorCollectorTest;
-import com.google.gwt.core.client.impl.StackTraceCreatorEmulTest;
-import com.google.gwt.core.client.impl.StackTraceCreatorTest;
+import com.google.gwt.core.client.impl.StackTraceDevTest;
+import com.google.gwt.core.client.impl.StackTraceEmulTest;
+import com.google.gwt.core.client.impl.StackTraceNativeTest;
+import com.google.gwt.core.client.impl.StackTraceStripTest;
 import com.google.gwt.core.client.prefetch.RunAsyncCodeTest;
 import com.google.gwt.core.shared.SerializableThrowableTest;
 import com.google.gwt.junit.tools.GWTTestSuite;
@@ -60,8 +62,10 @@
     suite.addTestSuite(ScriptInjectorTest.class);
     suite.addTestSuite(SerializableThrowableTest.class);
     suite.addTestSuite(StackTraceCreatorCollectorTest.class);
-    suite.addTestSuite(StackTraceCreatorEmulTest.class);
-    suite.addTestSuite(StackTraceCreatorTest.class);
+    suite.addTestSuite(StackTraceDevTest.class);
+    suite.addTestSuite(StackTraceEmulTest.class);
+    suite.addTestSuite(StackTraceNativeTest.class);
+    suite.addTestSuite(StackTraceStripTest.class);
 
     // Uncomment to print native stack traces for different platforms
     // suite.addTestSuite(com.google.gwt.core.client.impl.StackTraceGenerator.class);
diff --git a/user/test/com/google/gwt/core/StackTraceNoEmul.gwt.xml b/user/test/com/google/gwt/core/StackTraceNative.gwt.xml
similarity index 100%
rename from user/test/com/google/gwt/core/StackTraceNoEmul.gwt.xml
rename to user/test/com/google/gwt/core/StackTraceNative.gwt.xml
diff --git a/user/test/com/google/gwt/core/StackTraceStrip.gwt.xml b/user/test/com/google/gwt/core/StackTraceStrip.gwt.xml
new file mode 100644
index 0000000..cf3dd8a
--- /dev/null
+++ b/user/test/com/google/gwt/core/StackTraceStrip.gwt.xml
@@ -0,0 +1,20 @@
+<!--                                                                        -->
+<!-- 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   -->
+<!-- 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. License for the specific language governing permissions and   -->
+<!-- limitations under the License.                                         -->
+
+<module>
+  <inherits name="com.google.gwt.core.Core" />
+  <!-- Make sure junit xml processed earlier so stack mode will not be overwritten -->
+  <inherits name="com.google.gwt.junit.JUnit" />
+  <set-property name="compiler.stackMode" value="strip" />
+</module>
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 c912264..6123e16 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorCollectorTest.java
@@ -46,26 +46,6 @@
     assertEquals("anonymous", extractFunctionName("abc"));
   }
 
-  public void testChromeExtractName() {
-    CollectorChrome c = new CollectorChrome();
-
-    assertEquals("anonymous@@file.js:1:2", c.extractName(" at file.js:1:2"));
-    assertEquals("functionName@@file.js:1:2", c.extractName(" at functionName (file.js:1:2)"));
-    assertEquals("functionName@@file.js:1:2", c.extractName(" at Type.functionName (file.js:1:2)"));
-    assertEquals("functionName@@file.js:1:2",
-        c.extractName(" at Type.functionName [as methodName] (file.js:1:2)"));
-
-    // iOS style
-    assertEquals("functionName@@file.js:1", c.extractName("functionName@file.js:1"));
-  }
-
-  public void testFirefox14ExtractName() {
-    CollectorMoz c = new CollectorMoz();
-
-    assertEquals("anonymous", c.extractName("@file.js:1"));
-    assertEquals("functionName", c.extractName("functionName@file.js:1"));
-  }
-
   public void testChrome_25() {
     StackTraceElement[] expected = new StackTraceElement[] {
         createSTE("$third", "http://www.example.com/test/ABCD.cache.js@10", 300),
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorEmulTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorEmulTest.java
deleted file mode 100644
index 8c9782d..0000000
--- a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorEmulTest.java
+++ /dev/null
@@ -1,158 +0,0 @@
-/*
- * Copyright 2009 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.impl;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.core.client.JavaScriptException;
-
-/**
- * Tests {@link StackTraceCreator} in the emulated mode.
- */
-public class StackTraceCreatorEmulTest extends StackTraceCreatorTest {
-
-  public static void testJavaScriptException() {
-    StackTraceElement[] start = sample();
-    Throwable t = null;
-    try {
-      throwNative();
-      fail("No exception thrown");
-    } catch (JavaScriptException e) {
-      /*
-       * Some browsers may or may not be able to implement this at all, so we'll
-       * at least make sure that an array is returned;
-       */
-      assertNotNull(e.getStackTrace());
-      if (e.getStackTrace().length == 0) {
-        assertTrue("Development Mode", GWT.isScript());
-        return;
-      } else {
-        t = e;
-      }
-    }
-
-    String myName = null;
-    if (!GWT.isScript()) {
-      myName = "testJavaScriptException";
-    } else {
-      myName = Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorEmulTest::testJavaScriptException()");
-    }
-
-    checkStack(myName, t);
-
-    StackTraceElement[] end = sample();
-    assertEquals(start, end);
-  }
-
-  /**
-   * Just make sure that reentrant behavior doesn't fail.
-   */
-  public static void testReentrantCalls() {
-    if (!GWT.isScript()) {
-      // sample is useless in Development Mode
-      return;
-    }
-
-    StackTraceElement[] start = sample();
-
-    StackTraceElement[] stack = countDown(5);
-    assertNotNull(stack);
-    assertTrue(stack.length > 0);
-
-    StackTraceElement[] end = sample();
-    assertEquals(start, end);
-  }
-
-  public static void testStackTraces() {
-    StackTraceElement[] start = sample();
-
-    Throwable t;
-    try {
-      throw new RuntimeException();
-    } catch (Throwable t2) {
-      t = t2;
-    }
-
-    String myName = null;
-    if (!GWT.isScript()) {
-      myName = "testStackTraces";
-    } else {
-      myName = Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorEmulTest::testStackTraces()");
-    }
-
-    checkStack(myName, t);
-
-    StackTraceElement[] end = sample();
-    assertEquals(start, end);
-  }
-
-  private static void assertEquals(StackTraceElement[] start, StackTraceElement[] end) {
-    assertEquals("length", start.length, end.length);
-    for (int i = 0, j = start.length; i < j; i++) {
-      assertEquals("frame " + i, start[i].getMethodName(), end[i].getMethodName());
-    }
-  }
-
-  private static void checkStack(String myName, Throwable t) {
-    assertNotNull("myName", myName);
-    assertNotNull("t", t);
-
-    assertEquals("Needs a trim()", myName.trim(), myName);
-    assertFalse("function", myName.startsWith("function"));
-    assertFalse("(", myName.contains("("));
-
-    StackTraceElement[] stack = t.getStackTrace();
-    assertNotNull("stack", stack);
-    assertTrue("stack.length", stack.length > 0);
-
-    boolean found = false;
-    StringBuilder observedStack = new StringBuilder();
-    for (int i = 0, j = stack.length; i < j; i++) {
-      StackTraceElement elt = stack[i];
-      String value = elt.getMethodName();
-
-      assertNotNull("value", value);
-      assertTrue("value.length", value.length() > 0);
-      assertEquals("value.trim()", value.length(), value.trim().length());
-
-      observedStack.append("\n").append(value);
-      found |= myName.equals(value);
-    }
-
-    assertTrue("Did not find " + myName + " in the stack " + observedStack,
-        found);
-  }
-
-  private static StackTraceElement[] countDown(int count) {
-    if (count > 0) {
-      return countDown(count - 1);
-    } else {
-      return sample();
-    }
-  }
-
-  private static StackTraceElement[] sample() {
-    return new Throwable().getStackTrace();
-  }
-
-  private static native void throwNative() /*-{
-    null.a();
-  }-*/;
-
-  @Override
-  public String getModuleName() {
-    return "com.google.gwt.core.Core";
-  }
-}
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceCreatorTest.java
deleted file mode 100644
index 1c423b4..0000000
--- a/user/test/com/google/gwt/core/client/impl/StackTraceCreatorTest.java
+++ /dev/null
@@ -1,190 +0,0 @@
-/*
- * Copyright 2009 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.impl;
-
-import com.google.gwt.core.client.impl.StackTraceCreator.CollectorLegacy;
-import com.google.gwt.junit.DoNotRunWith;
-import com.google.gwt.junit.Platform;
-import com.google.gwt.junit.client.GWTTestCase;
-
-import junit.framework.AssertionFailedError;
-
-/**
- * Tests {@link StackTraceCreator}.
- */
-public class StackTraceCreatorTest extends GWTTestCase {
-
-  @Override
-  public String getModuleName() {
-    return "com.google.gwt.core.StackTraceNoEmul";
-  }
-
-  @DoNotRunWith(Platform.Devel)
-  public void testTrace() {
-    Throwable t = null;
-    try {
-      throwException1(false /* throw java exception */);
-      fail("No exception thrown");
-    } catch (Throwable e) {
-      t = e;
-    }
-
-    final String[] expected = {
-        Impl.getNameOf("@java.lang.Throwable::new(Ljava/lang/String;)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException3(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException2(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException1(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::testTrace()"),
-    };
-
-    assertTrace(expected, t, 0);
-  }
-
-  @DoNotRunWith(Platform.Devel)
-  public void testTraceRecursion() {
-    Throwable t = null;
-    try {
-      throwExceptionRecursive(3);
-      fail("No exception thrown");
-    } catch (Throwable e) {
-      t = e;
-    }
-
-    final String[] expectedModern = {
-        Impl.getNameOf("@java.lang.Throwable::new(Ljava/lang/String;)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException3(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException2(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException1(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwExceptionRecursive(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwExceptionRecursive(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwExceptionRecursive(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::testTraceRecursion()"),
-    };
-
-    final String[] expectedLegacy = {
-        Impl.getNameOf("@java.lang.Throwable::new(Ljava/lang/String;)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException3(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException2(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException1(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwExceptionRecursive(*)"),
-    };
-
-    final String[] expected = isLegacyCollector() ? expectedLegacy : expectedModern;
-    assertTrace(expected, t, 0);
-  }
-
-  @DoNotRunWith(Platform.Devel)
-  public void testTraceNative() {
-    Throwable t = null;
-    try {
-      throwException1(true /* throw js exception */);
-      fail("No exception thrown");
-    } catch (Throwable e) {
-      t = e;
-    }
-
-    String[] nativeMethodNames = throwNative(false /* don't throw - collect mode */);
-    final String[] expectedModern = {
-        nativeMethodNames[0],
-        nativeMethodNames[1],
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwNative(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException3(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException2(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::throwException1(*)"),
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::testTraceNative()"),
-    };
-
-    final String[] expectedLegacy = {
-        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceCreatorTest::testTraceNative()"),
-    };
-
-    final String[] expected = isLegacyCollector() ? expectedLegacy : expectedModern;
-
-    int offset = getTraceOffset(t.getStackTrace(), expected[0]);
-    assertTrace(expected, t, offset);
-  }
-
-  private void assertTrace(String[] expected, Throwable t, int offset) {
-    StackTraceElement[] trace = t.getStackTrace();
-    for (int i = 0; i < expected.length; i++) {
-      StackTraceElement actualElement = trace[i + offset];
-      String methodName = actualElement == null ? "!MISSING!" : actualElement.getMethodName();
-      if (expected[i].equals(methodName)) {
-        continue;
-      }
-      AssertionFailedError e = new AssertionFailedError("Incorrect frame at " + i + " - "
-          + " Expected: " + expected[i] + " Actual: " + methodName);
-      e.initCause(t);
-      throw e;
-    }
-  }
-
-  protected int getTraceOffset(StackTraceElement[] trace, String firstFrame) {
-    // TODO(goktug): Native exception traces are not properly stripped,
-    // Working around that for now by calculating an offset.
-    int offset = 0;
-    for (StackTraceElement ste : trace) {
-      if (ste.getMethodName().equals(firstFrame)) {
-        break;
-      }
-      offset++;
-    }
-    return offset;
-  }
-
-  private static boolean isLegacyCollector() {
-    return StackTraceCreator.collector instanceof CollectorLegacy;
-  }
-
-  private static void throwExceptionRecursive(int count) throws Throwable {
-    if (count > 1) {
-      throwExceptionRecursive(count - 1);
-    } else {
-      throwException1(false);
-    }
-  }
-
-  private static void throwException1(boolean throwNative) throws Throwable {
-    throwException2(throwNative);
-  }
-
-  private static void throwException2(boolean throwNative) throws Throwable {
-    throwException3(throwNative);
-  }
-
-  private static void throwException3(boolean throwNative) throws Throwable {
-    if (throwNative) {
-      throwNative(true /* really throw exception */);
-    } else {
-      throw new Throwable("broken");
-    }
-  }
-
-  private static native String[] throwNative(boolean reallyThrow) /*-{
-    function native1() {
-      return native2();
-    }
-    function native2() {
-      if (reallyThrow) null.a();
-
-      return [
-        @StackTraceCreator::getFunctionName(*)(arguments.callee),
-        @StackTraceCreator::getFunctionName(*)(arguments.callee.caller),
-      ];
-    }
-    return native1();
-  }-*/;
-}
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceDevTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceDevTest.java
new file mode 100644
index 0000000..afe494b
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceDevTest.java
@@ -0,0 +1,67 @@
+/*
+ * 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.impl;
+
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests {@link StackTraceCreator} in the native mode.
+ */
+@DoNotRunWith(Platform.Prod)
+public class StackTraceDevTest extends StackTraceTestBase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.core.StackTraceNative";
+  }
+
+  @Override
+  protected String[] getTraceJava() {
+    return new String[] {
+        "throwException2",
+        "throwException1",
+        "getLiveException",
+        "testTraceJava",
+    };
+  }
+
+  @Override
+  protected String[] getTraceRecursion() {
+    return new String[] {
+        "throwException2",
+        "throwException1",
+        "throwRecursive",
+        "throwRecursive",
+        "throwRecursive",
+        "throwException2",
+        "throwException1",
+        "getLiveException",
+        "testTraceRecursion",
+    };
+  }
+
+  @Override
+  protected String[] getTraceJse(Object whatToThrow) {
+    return new String[] {
+        "throwJse",
+        "throwException2",
+        "throwException1",
+        "getLiveException",
+        "assertJse",
+    };
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java
new file mode 100644
index 0000000..400597b
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceEmulTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2009 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.impl;
+
+import static com.google.gwt.core.client.impl.StackTraceExamples.JAVA;
+import static com.google.gwt.core.client.impl.StackTraceExamples.TYPE_ERROR;
+
+/**
+ * Tests {@link StackTraceCreator} in the emulated mode.
+ */
+public class StackTraceEmulTest extends StackTraceNativeTest {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.core.Core";
+  }
+
+  /**
+   * Verifies throw/try/catch doesn't poison the emulated stack frames.
+   */
+  public static void testViaSample() {
+    StackTraceElement[] start = sample();
+
+    Exception e = StackTraceExamples.getLiveException(JAVA);
+    assertTrue(e.getStackTrace().length > 0);
+
+    StackTraceElement[] end = sample();
+    assertEquals(start, end);
+  }
+
+  /**
+   * Verifies throw/try/catch with JSE doesn't poison the emulated stack frames.
+   */
+  public void testJseViaSample() {
+    StackTraceElement[] start = sample();
+
+    Exception e = StackTraceExamples.getLiveException(TYPE_ERROR);
+    assertTrue(e.getStackTrace().length > 0);
+
+    StackTraceElement[] end = sample();
+    assertEquals(start, end);
+  }
+
+  private static void assertEquals(StackTraceElement[] start, StackTraceElement[] end) {
+    assertEquals("length", start.length, end.length);
+    for (int i = 0, j = start.length; i < j; i++) {
+      assertEquals("frame " + i, start[i].getMethodName(), end[i].getMethodName());
+    }
+  }
+
+  private static StackTraceElement[] sample() {
+    return new Throwable().getStackTrace();
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java b/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
index 2d0def3..7d6750f 100644
--- a/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceExamples.java
@@ -15,6 +15,8 @@
  */
 package com.google.gwt.core.client.impl;
 
+import static junit.framework.Assert.fail;
+
 import com.google.gwt.core.client.JavaScriptObject;
 
 /**
@@ -26,16 +28,81 @@
  */
 public class StackTraceExamples {
 
+  public static final Object JAVA = new Object();
+  public static final Object RECURSION = new Object();
+  public static final Object TYPE_ERROR = new Object();
+
+  private static final Object NO_THROW = new Object();
+  private static final int NO_OF_RECURSION = 3;
+
+  public static Exception getLiveException(Object whatToThrow) {
+    try {
+      throwException1(whatToThrow);
+      fail("No exception thrown");
+      return null; // shouldn't happen
+    } catch (Exception e) {
+      return e;
+    }
+  }
+
+  private static void throwException1(Object whatToThrow) throws Exception {
+    throwException2(whatToThrow);
+  }
+
+  private static void throwException2(Object whatToThrow) throws Exception {
+    if (whatToThrow == JAVA) {
+      throw new Exception("broken");
+    } else if (whatToThrow == RECURSION) {
+      throwRecursive(NO_OF_RECURSION);
+    } else {
+      throwJse(whatToThrow);
+    }
+  }
+
+  private static void throwRecursive(int count) throws Exception {
+    if (count > 1) {
+      throwRecursive(count - 1);
+    } else {
+      throwException1(JAVA);
+    }
+  }
+
+  public static String[] getNativeMethodNames() {
+    return throwJse(NO_THROW);
+  }
+
+  private static native String[] throwJse(Object whatToThrow) /*-{
+    function native1() {
+      return native2();
+    }
+    function native2() {
+      if (whatToThrow == @StackTraceExamples::TYPE_ERROR) {
+        null.a();
+      }
+
+      if (whatToThrow != @StackTraceExamples::NO_THROW) {
+        throw whatToThrow;
+      }
+
+      return [
+        @StackTraceCreator::getFunctionName(*)(arguments.callee),
+        @StackTraceCreator::getFunctionName(*)(arguments.callee.caller),
+      ];
+    }
+    return native1();
+  }-*/;
+
   // Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.22 (KHTML, like Gecko)
   // Chrome/25.0.1364.97 Safari/537.22
+  // Modified to test 'xxx [as yyy]' and 'xxx.yyy' scenarios
   public static native JavaScriptObject chrome_25() /*-{
     return {
         message: "Cannot read property 'length' of undefined",
         name: "TypeError",
         stack: "TypeError: Cannot read property 'length' of undefined\n" +
-            "    at $third (http://www.example.com/test/ABCD.cache.js:300:10)\n" +
-            "    at $second (http://www.example.com/test/ABCD.cache.js:200:10)\n" +
-            "    at $first (http://www.example.com/test/ABCD.cache.js:100:10)\n" +
+            "    at C.$third [as alt2](http://www.example.com/test/ABCD.cache.js:300:10)\n" +
+            "    at B.$second (http://www.example.com/test/ABCD.cache.js:200:10)\n" +
+            "    at $first [as alt1] (http://www.example.com/test/ABCD.cache.js:100:10)\n" +
             "    at $entry0 (http://www.example.com/test/ABCD.cache.js:50:10)"
     };
   }-*/;
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceNativeTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceNativeTest.java
new file mode 100644
index 0000000..d00ce84
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceNativeTest.java
@@ -0,0 +1,100 @@
+/*
+ * 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.impl;
+
+import static com.google.gwt.core.client.impl.StackTraceExamples.TYPE_ERROR;
+
+import com.google.gwt.core.client.impl.StackTraceCreator.CollectorLegacy;
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests {@link StackTraceCreator} in the native mode.
+ */
+@DoNotRunWith(Platform.Devel)
+public class StackTraceNativeTest extends StackTraceTestBase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.core.StackTraceNative";
+  }
+
+  @Override
+  protected String[] getTraceJava() {
+    return new String[] {
+        Impl.getNameOf("@java.lang.Throwable::new(Ljava/lang/String;)"),
+        Impl.getNameOf("@java.lang.Exception::new(Ljava/lang/String;)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException2(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException1(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::getLiveException(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceTestBase::testTraceJava()"),
+    };
+  }
+
+  @Override
+  protected String[] getTraceRecursion() {
+    final String[] expectedModern = {
+        Impl.getNameOf("@java.lang.Throwable::new(Ljava/lang/String;)"),
+        Impl.getNameOf("@java.lang.Exception::new(Ljava/lang/String;)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException2(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException1(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwRecursive(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwRecursive(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwRecursive(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException2(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException1(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::getLiveException(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceTestBase::testTraceRecursion()"),
+    };
+
+    final String[] expectedLegacy = {
+        Impl.getNameOf("@java.lang.Throwable::new(Ljava/lang/String;)"),
+        Impl.getNameOf("@java.lang.Exception::new(Ljava/lang/String;)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException2(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException1(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwRecursive(*)"),
+    };
+
+    return isLegacyCollector() ? expectedLegacy : expectedModern;
+  }
+
+  @Override
+  protected String[] getTraceJse(Object thrown) {
+    String[] nativeMethodNames = StackTraceExamples.getNativeMethodNames();
+    final String[] full = {
+        nativeMethodNames[0],
+        nativeMethodNames[1],
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwJse(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException2(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::throwException1(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceExamples::getLiveException(*)"),
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceTestBase::assertJse(*)"),
+    };
+
+    final String[] limited = {
+        Impl.getNameOf("@com.google.gwt.core.client.impl.StackTraceTestBase::assertJse(*)"),
+    };
+
+    // For legacy browsers and non-error javascript exceptions (e.g. throw "string"), we can only
+    // construct stack trace from the catch block and below.
+
+    return (isLegacyCollector() || thrown != TYPE_ERROR) ? limited : full;
+  }
+
+  private static boolean isLegacyCollector() {
+    return StackTraceCreator.collector instanceof CollectorLegacy;
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceStripTest.java b/user/test/com/google/gwt/core/client/impl/StackTraceStripTest.java
new file mode 100644
index 0000000..1c955ae
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceStripTest.java
@@ -0,0 +1,46 @@
+/*
+ * 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.impl;
+
+import com.google.gwt.junit.DoNotRunWith;
+import com.google.gwt.junit.Platform;
+
+/**
+ * Tests {@link StackTraceCreator} in the strip mode.
+ */
+@DoNotRunWith(Platform.Devel)
+public class StackTraceStripTest extends StackTraceTestBase {
+
+  @Override
+  public String getModuleName() {
+    return "com.google.gwt.core.StackTraceStrip";
+  }
+
+  @Override
+  protected String[] getTraceJava() {
+    return new String[0];
+  }
+
+  @Override
+  protected String[] getTraceRecursion() {
+    return new String[0];
+  }
+
+  @Override
+  protected String[] getTraceJse(Object whatToThrow) {
+    return new String[0];
+  }
+}
diff --git a/user/test/com/google/gwt/core/client/impl/StackTraceTestBase.java b/user/test/com/google/gwt/core/client/impl/StackTraceTestBase.java
new file mode 100644
index 0000000..67fc362
--- /dev/null
+++ b/user/test/com/google/gwt/core/client/impl/StackTraceTestBase.java
@@ -0,0 +1,94 @@
+/*
+ * 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.impl;
+
+import static com.google.gwt.core.client.impl.StackTraceExamples.JAVA;
+import static com.google.gwt.core.client.impl.StackTraceExamples.RECURSION;
+import static com.google.gwt.core.client.impl.StackTraceExamples.TYPE_ERROR;
+
+import com.google.gwt.junit.client.GWTTestCase;
+
+import junit.framework.AssertionFailedError;
+
+/**
+ * Tests {@link StackTraceCreator}.
+ */
+public abstract class StackTraceTestBase extends GWTTestCase {
+
+  public void testTraceJava() {
+    Exception t = StackTraceExamples.getLiveException(JAVA);
+    assertTrace(getTraceJava(), t, 0);
+  }
+
+  protected abstract String[] getTraceJava();
+
+  public void testTraceRecursion() {
+    Exception t = StackTraceExamples.getLiveException(RECURSION);
+    assertTrace(getTraceRecursion(), t, 0);
+  }
+
+  protected abstract String[] getTraceRecursion();
+
+  public void testTraceTypeError() {
+    assertJse(TYPE_ERROR);
+  }
+
+  public void testTraceString() {
+    assertJse("testing");
+  }
+
+  public void testTraceNull() {
+    assertJse(null);
+  }
+
+  private void assertJse(Object whatToThrow) {
+    Exception t = StackTraceExamples.getLiveException(whatToThrow);
+
+    String[] expected = getTraceJse(whatToThrow);
+    int offset = getTraceOffset(t.getStackTrace(), expected[0]);
+    assertTrace(expected, t, offset);
+  }
+
+  protected abstract String[] getTraceJse(Object whatToThrow);
+
+  private void assertTrace(String[] expected, Exception t, int offset) {
+    StackTraceElement[] trace = t.getStackTrace();
+    for (int i = 0; i < expected.length; i++) {
+      StackTraceElement actualElement = trace[i + offset];
+      String methodName = actualElement == null ? "!MISSING!" : actualElement.getMethodName();
+      if (expected[i].equals(methodName)) {
+        continue;
+      }
+      AssertionFailedError e = new AssertionFailedError("Incorrect frame at " + i + " - "
+          + " Expected: " + expected[i] + " Actual: " + methodName);
+      e.initCause(t);
+      throw e;
+    }
+  }
+
+  protected int getTraceOffset(StackTraceElement[] trace, String firstFrame) {
+    // TODO(goktug): Native exception traces are not properly stripped,
+    // Working around that for now by calculating an offset.
+    int offset = 0;
+    for (StackTraceElement ste : trace) {
+      if (ste.getMethodName().equals(firstFrame)) {
+        break;
+      }
+      offset++;
+    }
+    return offset;
+  }
+}