/* * 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 junit.framework.Assert.fail; import com.google.gwt.core.client.JavaScriptObject; /** * Example stack traces from various browsers. * <p> * Some samples are adapted from * https://github.com/stacktracejs/stacktrace.js/blob/master/test/CapturedExceptions.js and also * used {@link StackTraceGenerator} to add some GWT specific scenarios not covered before. */ 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) { if (e.getStackTrace().length == 0) { e.fillInStackTrace(); } 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 [ @StackTraceExamples::getFunctionName(*)(arguments.callee), @StackTraceExamples::getFunctionName(*)(arguments.callee.caller), ]; } return native1(); }-*/; private static native String getFunctionName(JavaScriptObject fn) /*-{ return fn.name || (fn.name = @StackTraceExamples::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]) || "anonymous"; }-*/; // 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 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)" }; }-*/; public static native JavaScriptObject chrome_31_multiline() /*-{ return { message: "Object function () {\n" + " return {\n" + " name: \"provide multi-line source in exception\"\n" + " };\n" + " } has no method 'nonExistentMethod'", name: "TypeError", stack: "TypeError: Object function () {\n" + " return {\n" + " name: \"provide multi-line source in exception\"\n" + " };\n" + " } has no method 'nonExistentMethod'\n" + " at dumpException6 (http://www.example.com/test/ABCD.cache.js:82:20)\n" + " at HTMLButtonElement.onclick (http://www.example.com/test/ABCD.cache.js:101:122)" }; }-*/; public static native JavaScriptObject chrome_31_file() /*-{ return { message: "N/A", name: "TypeError", stack: "TypeError: N/A\n" + " at dumpException6 (file:///E:/test/ExceptionLab.html:82:20)\n" + " at HTMLButtonElement.onclick (file:///E:/test/ExceptionLab.html:101:122)" }; }-*/; public static native JavaScriptObject android_gingerbread() /*-{ return { message: "Cannot read property 'length' of undefined", stack: "TypeError: Cannot read property 'length' of undefined\n" + " at [object Object].Kj [as Ac] (http://www.example.com/test/ABCD.cache.js:300:9)\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 $entry0 (http://www.example.com/test/ABCD.cache.js:50:10)\n" + " at http://www.example.com/test/ABCD.cache.js:40:10" }; }-*/; public static native JavaScriptObject safari_6() /*-{ return { message: "'null' is not an object (evaluating 'x.undef')", stack: "@http://www.example.com/test/ABCD.cache.js:48\n" + "dumpException3@http://www.example.com/test/ABCD.cache.js:52\n" + "onclick@http://www.example.com/test/ABCD.cache.js:82\n" + "[native code]", line: 48, sourceURL: "file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html" }; }-*/; // Mozilla/5.0 (iPad; CPU OS 6_1 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) // Version/6.0 Mobile/10B141 Safari/8536.25 public static native JavaScriptObject safari_6_ios() /*-{ return { message: "Cannot read property 'length' of undefined", name: "TypeError", stack: "$third@http://www.example.com/test/ABCD.cache.js:300\n" + "$second@http://www.example.com/test/ABCD.cache.js:200\n" + "$first@http://www.example.com/test/ABCD.cache.js:100\n" + "$entry0@http://www.example.com/test/ABCD.cache.js:50\n" + "@http://www.example.com/test/ABCD.cache.js:10\n" + "@http://www.example.com/test/ABCD.cache.js:5\n" + "[native code]" }; }-*/; public static native JavaScriptObject firefox_3_6() /*-{ return { fileName: "http://www.example.com/test/ABCD.cache.js", lineNumber: 44, message: "this.undef is not a function", name: "TypeError", stack: "()@http://www.example.com/test/ABCD.cache.js:44\n" + "(null)@http://www.example.com/test/ABCD.cache.js:31\n" + "printStackTrace()@http://www.example.com/test/ABCD.cache.js:18\n" + "bar(1)@http://www.example.com/test/ABCD.cache.js:13\n" + "bar([object Object])@http://www.example.com/test/ABCD.cache.js:16\n" + "foo()@http://www.example.com/test/ABCD.cache.js:20\n" + "@http://www.example.com/test/ABCD.cache.js:24\n" + "([object Object])@http://www.example.com/test/ABCD.cache.js:26\n" + "" }; }-*/; public static native JavaScriptObject firefox_22() /*-{ return { message: "x is null", name: "TypeError", stack: "@http://www.example.com/test/ABCD.cache.js:4\n" + "createException@http://www.example.com/test/ABCD.cache.js:8\n" + "createException4@http://www.example.com/test/ABCD.cache.js:56\n" + "dumpException4@http://www.example.com/test/ABCD.cache.js:60\n" + "Ul/<[\"a.b\"].xyz@http://www.example.com/test/ABCD.cache.js:7\n" + "onclick@http://www.example.com/test/ABCD.cache.js:1\n" + "", fileName: "http://www.example.com/test/ABCD.cache.js", lineNumber: 4, columnNumber: 6 }; }-*/; public static native JavaScriptObject ie_10() /*-{ return { message: "Unable to get property 'undef' of undefined or null reference", name: "TypeError", stack: "TypeError: Unable to get property 'undef' of undefined or null reference\n" + " at Anonymous function (http://www.example.com/test/ABCD.cache.js:48:13)\n" + " at dumpException3 (http://www.example.com/test/ABCD.cache.js:46:9)\n" + " at onclick (http://www.example.com/test/ABCD.cache.js:82:1)", description: "Unable to get property 'undef' of undefined or null reference", number: -2146823281 }; }-*/; }