blob: d959e161ab5fd54400862fbfbbb64043441b8c8f [file] [log] [blame]
/*
* 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)",
toString: function() { return this.name + ": " + this.message; },
};
}-*/;
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
};
}-*/;
}