+2012-02-16 Oliver Hunt <oliver@apple.com>
+
+ Implement Error.stack
+ https://bugs.webkit.org/show_bug.cgi?id=66994
+
+ Reviewed by Gavin Barraclough.
+
+ Add testcases for producing a stack trace on exception objects.
+
+ * fast/js/exception-properties-expected.txt:
+ * fast/js/script-tests/exception-properties.js:
+ * fast/js/script-tests/stack-trace.js: Added.
+ (printStack):
+ (hostThrower):
+ (callbacker):
+ (outer):
+ (inner):
+ (evaler):
+ (normalOuter):
+ (normalInner):
+ (scripterInner):
+ (scripterOuter):
+ (selfRecursive1):
+ (selfRecursive2):
+ (selfRecursive3):
+ (throwError):
+ (object.get getter1.o.valueOf):
+ (object.get getter1):
+ (object.get getter2):
+ (object.get getter3.o2.valueOf):
+ (object.get getter3):
+ (object.nonInlineable.callCount):
+ (object.nonInlineable):
+ (object.inlineable):
+ (yetAnotherInlinedCall):
+ (makeInlinableCall):
+ (.try.g):
+ (h):
+ (mapTest):
+ (mapTestDriver):
+ (dfgFunction):
+ (try.f):
+ * fast/js/stack-trace-expected.txt: Added.
+ * fast/js/stack-trace.html: Added.
+
2012-02-16 James Robinson <jamesr@chromium.org>
[chromium] Unreviewed, update baselines for region repaint tests due to skia off-by-one.
PASS enumerableProperties(error) is []
-PASS enumerableProperties(nativeError) is ["line", "sourceURL"]
+PASS enumerableProperties(nativeError) is ["line", "sourceURL", "stack"]
PASS Object.getPrototypeOf(nativeError).name is "RangeError"
PASS Object.getPrototypeOf(nativeError).message is ""
PASS successfullyParsed is true
var error = new Error("message");
shouldBe('enumerableProperties(error)', '[]');
- shouldBe('enumerableProperties(nativeError)', '["line", "sourceURL"]');
+ shouldBe('enumerableProperties(nativeError)', '["line", "sourceURL", "stack"]');
shouldBe('Object.getPrototypeOf(nativeError).name', '"RangeError"');
shouldBe('Object.getPrototypeOf(nativeError).message', '""');
--- /dev/null
+if (!this.alert) {
+ debug = print;
+ description = print;
+}
+
+description(
+'This test checks stack trace corectness in special cases.'
+);
+
+function printStack(stackTrace) {
+ debug("--> Stack Trace:")
+ var length = Math.min(stackTrace.length, 100);
+ for (var i = 0; i < length; i++) {
+ var indexOfAt = stackTrace[i].indexOf('@')
+ var indexOfLastSlash = stackTrace[i].lastIndexOf('/');
+ if (indexOfLastSlash == -1)
+ indexOfLastSlash = indexOfAt
+ var functionName = stackTrace[i].substring(0, indexOfAt);
+ var fileName = stackTrace[i].substring(indexOfLastSlash + 1);
+ debug(" " + i + " " + functionName + " at " + fileName);
+ }
+ debug('');
+}
+
+function hostThrower() { Element.prototype.appendChild.call({ }, [{ }]); }
+function callbacker(f) { [0].map(f); }
+function outer(errorName) { inner(errorName); }
+function inner(errorName) { throw new Error("Error in " + errorName); }
+function evaler(code) { eval(code); }
+function normalOuter() { normalInner(); }
+function normalInner() { if(thisVarDoesntExist) failIfTrue("shouldFailBeforeThis") };
+function scripterInner() { htmlInner(); }
+function scripterOuter() { htmlOuter(); }
+ // Expected functions in stack trace
+// Normal Case
+try { normalOuter() } catch (e) { printStack(e.stack) } // normalOuter -> normalInner
+
+// Eval Case
+try { evaler("inner('inner eval');"); } catch (e) { printStack(e.stack) } // evaler -> eval -> inner
+try { evaler("outer('outer eval');"); } catch (e) { printStack(e.stack) } // evaler -> eval -> outer -> inner
+
+// Function Callback Case
+try { callbacker(inner('inner map')); } catch (e) { printStack(e.stack); } // callbacker -> map -> inner
+try { callbacker(outer('outer map')); } catch (e) { printStack(e.stack); } // callbacker -> map -> outer -> inner
+
+// Host Code Case
+try { hostThrower(); } catch (e) { printStack(e.stack); } // hostThrower
+
+try { scripterInner(); } catch (e) { printStack(e.stack) } // program -> scripter -> inner
+try { scripterOuter(); } catch (e) { printStack(e.stack) } // program -> scripter -> outer -> inner
+
+function selfRecursive1() {
+ selfRecursive1();
+}
+
+try { selfRecursive1(); } catch (e) { printStack(e.stack) } // selfRecursive1 -> selfRecursive1 -> selfRecursive1 -> selfRecursive1 ...
+
+function selfRecursive2() {
+ // A little work to make the DFG kick in
+ for (var i = 0; i < 10; i++) {
+ if (i == 9)
+ selfRecursive2();
+ }
+}
+
+try { selfRecursive2(); } catch (e) { printStack(e.stack) } // selfRecursive2 -> selfRecursive2 -> selfRecursive2 -> selfRecursive2 ...
+
+function selfRecursive3() {
+ eval("selfRecursive3()");
+}
+
+try { selfRecursive3(); } catch (e) { printStack(e.stack) } // selfRecursive3 -> eval -> selfRecursive3 -> eval ...
+
+var callCount = 0;
+
+function throwError() {
+ throw {};
+}
+
+var object = {
+ get getter1() {
+ var o = {
+ valueOf: function() {
+ throwError()
+ }
+ };
+ +o;
+ },
+ get getter2() {
+ var o = {
+ valueOf: throwError
+ };
+ +o;
+ },
+ get getter3() {
+ var o1 = {
+ valueOf: throwError
+ };
+ var o2 = {
+ valueOf: function () {
+ throwError();
+ }
+ };
+ if (callCount == 9998)
+ +o1;
+ if (callCount == 9999)
+ +o2;
+ },
+ nonInlineable : function () {
+ if (0) return [arguments, function(){}];
+ ++callCount;
+ if (callCount == 1) {
+ this.getter1;
+ } else if (callCount == 2) {
+ this.getter2;
+ } else {
+ this.getter3;
+ }
+ },
+ inlineable : function () {
+ this.nonInlineable();
+ }
+}
+
+function yetAnotherInlinedCall(o) {
+ o.inlineable();
+}
+
+function makeInlinableCall(o) {
+ for (var i = 0; i < 10000; i++) {
+ new yetAnotherInlinedCall(o);
+ }
+}
+for (var k = 0; k < 4; k++) {
+ try {
+ function g() {
+ var j = 0;
+ for (var i = 0; i < 1000; i++) {
+ j++;
+ makeInlinableCall(object);
+ }
+ }
+ [1].map(g);
+ } catch (e) {
+ printStack(e.stack);
+ }
+}
+
+function h() {
+ if (callCount++ == 1000)
+ throw {};
+ if (callCount > 1000) {
+ [].map.apply(undefined, throwError);
+ }
+}
+
+function mapTest(a) {
+ a.map(h);
+}
+
+function mapTestDriver() {
+ var a = [1,2,3];
+ for (var i = 0; i < 2000; i++)
+ mapTest(a);
+}
+
+try {
+ callCount = 0;
+ mapTestDriver()
+} catch(e) {
+ printStack(e.stack);
+}
+
+try {
+ mapTestDriver()
+} catch(e) {
+ printStack(e.stack);
+}
+
+var dfgFunctionShouldThrow = false;
+function dfgFunction() {
+ if (dfgFunctionShouldThrow) {
+ dfgFunctionShouldThrow = false;
+ throwError();
+ }
+}
+
+for (var k = 0; k < 1000; k++)
+ dfgFunction();
+
+try {
+ dfgFunctionShouldThrow = true;
+ [1,2,3,4].map(dfgFunction);
+} catch (e) {
+ printStack(e.stack);
+}
+
+try {
+ var o = { };
+ o.__defineGetter__("g", dfgFunction);
+ function f(o) {
+ o.g;
+ }
+ for (var k = 0; k < 1000; k++)
+ f(o);
+
+ dfgFunctionShouldThrow = true;
+ f(o);
+
+} catch (e) {
+ printStack(e.stack);
+}
+
+successfullyParsed = true;
--- /dev/null
+This test checks stack trace corectness in special cases.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+--> Stack Trace:
+ 0 normalInner at stack-trace.js:31
+ 1 normalOuter at stack-trace.js:30
+ 2 global code at stack-trace.js:36
+
+--> Stack Trace:
+ 0 inner at stack-trace.js:28
+ 1 at eval code
+ 2 eval at [native code]
+
+--> Stack Trace:
+ 0 inner at stack-trace.js:28
+ 1 outer at stack-trace.js:27
+ 2 at eval code
+ 3 eval at [native code]
+
+--> Stack Trace:
+ 0 inner at stack-trace.js:28
+ 1 global code at stack-trace.js:43
+
+--> Stack Trace:
+ 0 inner at stack-trace.js:28
+ 1 outer at stack-trace.js:27
+ 2 global code at stack-trace.js:44
+
+--> Stack Trace:
+ 0 hostThrower at stack-trace.js:25
+ 1 global code at stack-trace.js:47
+
+--> Stack Trace:
+ 0 htmlInner at stack-trace.html:10
+ 1 scripterInner at stack-trace.js:32
+ 2 global code at stack-trace.js:49
+
+--> Stack Trace:
+ 0 htmlInner at stack-trace.html:10
+ 1 htmlOuter at stack-trace.html:11
+ 2 scripterOuter at stack-trace.js:33
+ 3 global code at stack-trace.js:50
+
+--> Stack Trace:
+ 0 selfRecursive1 at stack-trace.js:53
+ 1 selfRecursive1 at stack-trace.js:53
+ 2 selfRecursive1 at stack-trace.js:53
+ 3 selfRecursive1 at stack-trace.js:53
+ 4 selfRecursive1 at stack-trace.js:53
+ 5 selfRecursive1 at stack-trace.js:53
+ 6 selfRecursive1 at stack-trace.js:53
+ 7 selfRecursive1 at stack-trace.js:53
+ 8 selfRecursive1 at stack-trace.js:53
+ 9 selfRecursive1 at stack-trace.js:53
+ 10 selfRecursive1 at stack-trace.js:53
+ 11 selfRecursive1 at stack-trace.js:53
+ 12 selfRecursive1 at stack-trace.js:53
+ 13 selfRecursive1 at stack-trace.js:53
+ 14 selfRecursive1 at stack-trace.js:53
+ 15 selfRecursive1 at stack-trace.js:53
+ 16 selfRecursive1 at stack-trace.js:53
+ 17 selfRecursive1 at stack-trace.js:53
+ 18 selfRecursive1 at stack-trace.js:53
+ 19 selfRecursive1 at stack-trace.js:53
+ 20 selfRecursive1 at stack-trace.js:53
+ 21 selfRecursive1 at stack-trace.js:53
+ 22 selfRecursive1 at stack-trace.js:53
+ 23 selfRecursive1 at stack-trace.js:53
+ 24 selfRecursive1 at stack-trace.js:53
+ 25 selfRecursive1 at stack-trace.js:53
+ 26 selfRecursive1 at stack-trace.js:53
+ 27 selfRecursive1 at stack-trace.js:53
+ 28 selfRecursive1 at stack-trace.js:53
+ 29 selfRecursive1 at stack-trace.js:53
+ 30 selfRecursive1 at stack-trace.js:53
+ 31 selfRecursive1 at stack-trace.js:53
+ 32 selfRecursive1 at stack-trace.js:53
+ 33 selfRecursive1 at stack-trace.js:53
+ 34 selfRecursive1 at stack-trace.js:53
+ 35 selfRecursive1 at stack-trace.js:53
+ 36 selfRecursive1 at stack-trace.js:53
+ 37 selfRecursive1 at stack-trace.js:53
+ 38 selfRecursive1 at stack-trace.js:53
+ 39 selfRecursive1 at stack-trace.js:53
+ 40 selfRecursive1 at stack-trace.js:53
+ 41 selfRecursive1 at stack-trace.js:53
+ 42 selfRecursive1 at stack-trace.js:53
+ 43 selfRecursive1 at stack-trace.js:53
+ 44 selfRecursive1 at stack-trace.js:53
+ 45 selfRecursive1 at stack-trace.js:53
+ 46 selfRecursive1 at stack-trace.js:53
+ 47 selfRecursive1 at stack-trace.js:53
+ 48 selfRecursive1 at stack-trace.js:53
+ 49 selfRecursive1 at stack-trace.js:53
+ 50 selfRecursive1 at stack-trace.js:53
+ 51 selfRecursive1 at stack-trace.js:53
+ 52 selfRecursive1 at stack-trace.js:53
+ 53 selfRecursive1 at stack-trace.js:53
+ 54 selfRecursive1 at stack-trace.js:53
+ 55 selfRecursive1 at stack-trace.js:53
+ 56 selfRecursive1 at stack-trace.js:53
+ 57 selfRecursive1 at stack-trace.js:53
+ 58 selfRecursive1 at stack-trace.js:53
+ 59 selfRecursive1 at stack-trace.js:53
+ 60 selfRecursive1 at stack-trace.js:53
+ 61 selfRecursive1 at stack-trace.js:53
+ 62 selfRecursive1 at stack-trace.js:53
+ 63 selfRecursive1 at stack-trace.js:53
+ 64 selfRecursive1 at stack-trace.js:53
+ 65 selfRecursive1 at stack-trace.js:53
+ 66 selfRecursive1 at stack-trace.js:53
+ 67 selfRecursive1 at stack-trace.js:53
+ 68 selfRecursive1 at stack-trace.js:53
+ 69 selfRecursive1 at stack-trace.js:53
+ 70 selfRecursive1 at stack-trace.js:53
+ 71 selfRecursive1 at stack-trace.js:53
+ 72 selfRecursive1 at stack-trace.js:53
+ 73 selfRecursive1 at stack-trace.js:53
+ 74 selfRecursive1 at stack-trace.js:53
+ 75 selfRecursive1 at stack-trace.js:53
+ 76 selfRecursive1 at stack-trace.js:53
+ 77 selfRecursive1 at stack-trace.js:53
+ 78 selfRecursive1 at stack-trace.js:53
+ 79 selfRecursive1 at stack-trace.js:53
+ 80 selfRecursive1 at stack-trace.js:53
+ 81 selfRecursive1 at stack-trace.js:53
+ 82 selfRecursive1 at stack-trace.js:53
+ 83 selfRecursive1 at stack-trace.js:53
+ 84 selfRecursive1 at stack-trace.js:53
+ 85 selfRecursive1 at stack-trace.js:53
+ 86 selfRecursive1 at stack-trace.js:53
+ 87 selfRecursive1 at stack-trace.js:53
+ 88 selfRecursive1 at stack-trace.js:53
+ 89 selfRecursive1 at stack-trace.js:53
+ 90 selfRecursive1 at stack-trace.js:53
+ 91 selfRecursive1 at stack-trace.js:53
+ 92 selfRecursive1 at stack-trace.js:53
+ 93 selfRecursive1 at stack-trace.js:53
+ 94 selfRecursive1 at stack-trace.js:53
+ 95 selfRecursive1 at stack-trace.js:53
+ 96 selfRecursive1 at stack-trace.js:53
+ 97 selfRecursive1 at stack-trace.js:53
+ 98 selfRecursive1 at stack-trace.js:53
+ 99 selfRecursive1 at stack-trace.js:53
+
+--> Stack Trace:
+ 0 selfRecursive2 at stack-trace.js:62
+ 1 selfRecursive2 at stack-trace.js:62
+ 2 selfRecursive2 at stack-trace.js:62
+ 3 selfRecursive2 at stack-trace.js:62
+ 4 selfRecursive2 at stack-trace.js:62
+ 5 selfRecursive2 at stack-trace.js:62
+ 6 selfRecursive2 at stack-trace.js:62
+ 7 selfRecursive2 at stack-trace.js:62
+ 8 selfRecursive2 at stack-trace.js:62
+ 9 selfRecursive2 at stack-trace.js:62
+ 10 selfRecursive2 at stack-trace.js:62
+ 11 selfRecursive2 at stack-trace.js:62
+ 12 selfRecursive2 at stack-trace.js:62
+ 13 selfRecursive2 at stack-trace.js:62
+ 14 selfRecursive2 at stack-trace.js:62
+ 15 selfRecursive2 at stack-trace.js:62
+ 16 selfRecursive2 at stack-trace.js:62
+ 17 selfRecursive2 at stack-trace.js:62
+ 18 selfRecursive2 at stack-trace.js:62
+ 19 selfRecursive2 at stack-trace.js:62
+ 20 selfRecursive2 at stack-trace.js:62
+ 21 selfRecursive2 at stack-trace.js:62
+ 22 selfRecursive2 at stack-trace.js:62
+ 23 selfRecursive2 at stack-trace.js:62
+ 24 selfRecursive2 at stack-trace.js:62
+ 25 selfRecursive2 at stack-trace.js:62
+ 26 selfRecursive2 at stack-trace.js:62
+ 27 selfRecursive2 at stack-trace.js:62
+ 28 selfRecursive2 at stack-trace.js:62
+ 29 selfRecursive2 at stack-trace.js:62
+ 30 selfRecursive2 at stack-trace.js:62
+ 31 selfRecursive2 at stack-trace.js:62
+ 32 selfRecursive2 at stack-trace.js:62
+ 33 selfRecursive2 at stack-trace.js:62
+ 34 selfRecursive2 at stack-trace.js:62
+ 35 selfRecursive2 at stack-trace.js:62
+ 36 selfRecursive2 at stack-trace.js:62
+ 37 selfRecursive2 at stack-trace.js:62
+ 38 selfRecursive2 at stack-trace.js:62
+ 39 selfRecursive2 at stack-trace.js:62
+ 40 selfRecursive2 at stack-trace.js:62
+ 41 selfRecursive2 at stack-trace.js:62
+ 42 selfRecursive2 at stack-trace.js:62
+ 43 selfRecursive2 at stack-trace.js:62
+ 44 selfRecursive2 at stack-trace.js:62
+ 45 selfRecursive2 at stack-trace.js:62
+ 46 selfRecursive2 at stack-trace.js:62
+ 47 selfRecursive2 at stack-trace.js:62
+ 48 selfRecursive2 at stack-trace.js:62
+ 49 selfRecursive2 at stack-trace.js:62
+ 50 selfRecursive2 at stack-trace.js:62
+ 51 selfRecursive2 at stack-trace.js:62
+ 52 selfRecursive2 at stack-trace.js:62
+ 53 selfRecursive2 at stack-trace.js:62
+ 54 selfRecursive2 at stack-trace.js:62
+ 55 selfRecursive2 at stack-trace.js:62
+ 56 selfRecursive2 at stack-trace.js:62
+ 57 selfRecursive2 at stack-trace.js:62
+ 58 selfRecursive2 at stack-trace.js:62
+ 59 selfRecursive2 at stack-trace.js:62
+ 60 selfRecursive2 at stack-trace.js:62
+ 61 selfRecursive2 at stack-trace.js:62
+ 62 selfRecursive2 at stack-trace.js:62
+ 63 selfRecursive2 at stack-trace.js:62
+ 64 selfRecursive2 at stack-trace.js:62
+ 65 selfRecursive2 at stack-trace.js:62
+ 66 selfRecursive2 at stack-trace.js:62
+ 67 selfRecursive2 at stack-trace.js:62
+ 68 selfRecursive2 at stack-trace.js:62
+ 69 selfRecursive2 at stack-trace.js:62
+ 70 selfRecursive2 at stack-trace.js:62
+ 71 selfRecursive2 at stack-trace.js:62
+ 72 selfRecursive2 at stack-trace.js:62
+ 73 selfRecursive2 at stack-trace.js:62
+ 74 selfRecursive2 at stack-trace.js:62
+ 75 selfRecursive2 at stack-trace.js:62
+ 76 selfRecursive2 at stack-trace.js:62
+ 77 selfRecursive2 at stack-trace.js:62
+ 78 selfRecursive2 at stack-trace.js:62
+ 79 selfRecursive2 at stack-trace.js:62
+ 80 selfRecursive2 at stack-trace.js:62
+ 81 selfRecursive2 at stack-trace.js:62
+ 82 selfRecursive2 at stack-trace.js:62
+ 83 selfRecursive2 at stack-trace.js:62
+ 84 selfRecursive2 at stack-trace.js:62
+ 85 selfRecursive2 at stack-trace.js:62
+ 86 selfRecursive2 at stack-trace.js:62
+ 87 selfRecursive2 at stack-trace.js:62
+ 88 selfRecursive2 at stack-trace.js:62
+ 89 selfRecursive2 at stack-trace.js:62
+ 90 selfRecursive2 at stack-trace.js:62
+ 91 selfRecursive2 at stack-trace.js:62
+ 92 selfRecursive2 at stack-trace.js:62
+ 93 selfRecursive2 at stack-trace.js:62
+ 94 selfRecursive2 at stack-trace.js:62
+ 95 selfRecursive2 at stack-trace.js:62
+ 96 selfRecursive2 at stack-trace.js:62
+ 97 selfRecursive2 at stack-trace.js:62
+ 98 selfRecursive2 at stack-trace.js:62
+ 99 selfRecursive2 at stack-trace.js:62
+
+--> Stack Trace:
+ 0 selfRecursive3 at stack-trace.js:69
+ 1 at eval code
+ 2 eval at [native code]
+
+--> Stack Trace:
+ 0 throwError at stack-trace.js:77
+ 1 valueOf at stack-trace.js:84
+ 2 getter1 at stack-trace.js:87
+ 3 nonInlineable at stack-trace.js:113
+ 4 inlineable at stack-trace.js:121
+ 5 yetAnotherInlinedCall at stack-trace.js:126
+ 6 makeInlinableCall at stack-trace.js:131
+ 7 g at stack-trace.js:140
+ 8 map at [native code]
+
+--> Stack Trace:
+ 0 throwError at stack-trace.js:77
+ 1 getter2 at stack-trace.js:93
+ 2 nonInlineable at stack-trace.js:115
+ 3 inlineable at stack-trace.js:121
+ 4 yetAnotherInlinedCall at stack-trace.js:126
+ 5 makeInlinableCall at stack-trace.js:131
+ 6 g at stack-trace.js:140
+ 7 map at [native code]
+
+--> Stack Trace:
+ 0 throwError at stack-trace.js:77
+ 1 getter3 at stack-trace.js:105
+ 2 nonInlineable at stack-trace.js:117
+ 3 inlineable at stack-trace.js:121
+ 4 yetAnotherInlinedCall at stack-trace.js:126
+ 5 makeInlinableCall at stack-trace.js:131
+ 6 g at stack-trace.js:140
+ 7 map at [native code]
+
+--> Stack Trace:
+ 0 throwError at stack-trace.js:77
+ 1 valueOf at stack-trace.js:101
+ 2 getter3 at stack-trace.js:107
+ 3 nonInlineable at stack-trace.js:117
+ 4 inlineable at stack-trace.js:121
+ 5 yetAnotherInlinedCall at stack-trace.js:126
+ 6 makeInlinableCall at stack-trace.js:131
+ 7 g at stack-trace.js:140
+ 8 map at [native code]
+
+--> Stack Trace:
+ 0 h at stack-trace.js:151
+ 1 map at [native code]
+
+--> Stack Trace:
+ 0 h at stack-trace.js:153
+ 1 map at [native code]
+
+--> Stack Trace:
+ 0 throwError at stack-trace.js:77
+ 1 dfgFunction at stack-trace.js:184
+ 2 map at [native code]
+
+--> Stack Trace:
+ 0 throwError at stack-trace.js:77
+ 1 dfgFunction at stack-trace.js:184
+ 2 f at stack-trace.js:202
+ 3 global code at stack-trace.js:208
+
+
--- /dev/null
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+ <link rel="stylesheet" href="resources/js-test-style.css">
+ <script src="resources/js-test-pre.js"></script>
+</head>
+<body>
+ <p id="description"></p>
+ <p id="console"></p>
+ <script>function htmlInner() { throw new Error("Error in HTML"); }</script>
+ <script>function htmlOuter() { htmlInner(); }</script>
+ <script src="script-tests/stack-trace.js"></script>
+</body>
+</html>
+2012-02-16 Oliver Hunt <oliver@apple.com>
+
+ Implement Error.stack
+ https://bugs.webkit.org/show_bug.cgi?id=66994
+
+ Reviewed by Gavin Barraclough.
+
+ Implement support for stack traces on exception objects. This is a rewrite
+ of the core portion of the last stack walking logic, but the mechanical work
+ of adding the information to an exception comes from the original work by
+ Juan Carlos Montemayor Elosua.
+
+ * interpreter/Interpreter.cpp:
+ (JSC::getCallerInfo):
+ (JSC):
+ (JSC::getSourceURLFromCallFrame):
+ (JSC::getStackFrameCodeType):
+ (JSC::Interpreter::getStackTrace):
+ (JSC::Interpreter::throwException):
+ (JSC::Interpreter::privateExecute):
+ * interpreter/Interpreter.h:
+ (JSC):
+ (StackFrame):
+ (JSC::StackFrame::toString):
+ (Interpreter):
+ * jsc.cpp:
+ (GlobalObject::finishCreation):
+ (functionJSCStack):
+ * parser/Nodes.h:
+ (JSC::FunctionBodyNode::setInferredName):
+ * parser/Parser.h:
+ (JSC::::parse):
+ * runtime/CommonIdentifiers.h:
+ * runtime/Error.cpp:
+ (JSC::addErrorInfo):
+ * runtime/Error.h:
+ (JSC):
+
2012-02-15 Gavin Barraclough <barraclough@apple.com>
Numerous trivial bugs in Object.defineProperty
?getPropertyDescriptor@JSObject@JSC@@QAE_NPAVExecState@2@ABVIdentifier@2@AAVPropertyDescriptor@2@@Z
?getPropertyNames@JSObject@JSC@@SAXPAV12@PAVExecState@2@AAVPropertyNameArray@2@W4EnumerationMode@2@@Z
?getSlice@ArgList@JSC@@QBEXHAAV12@@Z
+ ?getStackTrace@Interpreter@JSC@@SAXPAVJSGlobalData@2@HAAV?$Vector@UStackFrame@JSC@@$0A@@WTF@@@Z
?getString@JSCell@JSC@@QBE?AVUString@2@PAVExecState@2@@Z
?getString@JSCell@JSC@@QBE_NPAVExecState@2@AAVUString@2@@Z
?getter@PropertyDescriptor@JSC@@QBE?AVJSValue@2@XZ
#include "JSActivation.h"
#include "JSArray.h"
#include "JSByteArray.h"
-#include "JSFunction.h"
#include "JSNotAnObject.h"
#include "JSPropertyNameIterator.h"
#include "LiteralParser.h"
#include "Register.h"
#include "SamplingTool.h"
#include "StrictEvalActivation.h"
+#include "StrongInlines.h"
#include "UStringConcatenate.h"
#include <limits.h>
#include <stdio.h>
exception->putDirect(*globalData, globalData->propertyNames->message, jsString(globalData, message));
}
+static CallFrame* getCallerInfo(JSGlobalData* globalData, CallFrame* callFrame, int& lineNumber)
+{
+ UNUSED_PARAM(globalData);
+ unsigned bytecodeOffset = 0;
+ lineNumber = -1;
+ ASSERT(!callFrame->hasHostCallFrameFlag());
+ CallFrame* callerFrame = callFrame->codeBlock() ? callFrame->trueCallerFrame() : 0;
+ bool callframeIsHost = callerFrame->addHostCallFrameFlag() == callFrame->callerFrame();
+ ASSERT(!callerFrame->hasHostCallFrameFlag());
+
+ if (callerFrame == CallFrame::noCaller() || !callerFrame || !callerFrame->codeBlock())
+ return callerFrame;
+
+ CodeBlock* callerCodeBlock = callerFrame->codeBlock();
+
+ if (callframeIsHost) {
+ // Don't need to deal with inline callframes here as by definition we haven't
+ // inlined a call with an intervening native call frame.
+#if ENABLE(INTERPRETER)
+ if (!globalData->canUseJIT()) {
+ bytecodeOffset = callerFrame->bytecodeOffsetForNonDFGCode();
+ lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1);
+ return callerFrame;
+ }
+#endif
+#if ENABLE(JIT)
+#if ENABLE(DFG_JIT)
+ if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT)
+ bytecodeOffset = callerCodeBlock->codeOrigin(callerFrame->codeOriginIndexForDFG()).bytecodeIndex;
+ else
+#endif
+ bytecodeOffset = callerFrame->bytecodeOffsetForNonDFGCode();
+#endif
+ } else {
+#if ENABLE(INTERPRETER)
+ if (!globalData->canUseJIT()) {
+ bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnVPC());
+ lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset - 1);
+ return callerFrame;
+ }
+#endif
+#if ENABLE(JIT)
+ #if ENABLE(DFG_JIT)
+ if (callFrame->isInlineCallFrame()) {
+ InlineCallFrame* icf = callFrame->inlineCallFrame();
+ bytecodeOffset = icf->caller.bytecodeIndex;
+ if (InlineCallFrame* parentCallFrame = icf->caller.inlineCallFrame) {
+ FunctionExecutable* executable = static_cast<FunctionExecutable*>(parentCallFrame->executable.get());
+ CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(parentCallFrame->isCall ? CodeForCall : CodeForConstruct);
+ ASSERT(newCodeBlock);
+ ASSERT(newCodeBlock->instructionCount() > bytecodeOffset);
+ callerCodeBlock = newCodeBlock;
+ }
+ } else if (callerCodeBlock && callerCodeBlock->getJITType() == JITCode::DFGJIT) {
+ CodeOrigin origin;
+ if (!callerCodeBlock->codeOriginForReturn(callFrame->returnPC(), origin))
+ ASSERT_NOT_REACHED();
+ bytecodeOffset = origin.bytecodeIndex;
+ if (InlineCallFrame* icf = origin.inlineCallFrame) {
+ FunctionExecutable* executable = static_cast<FunctionExecutable*>(icf->executable.get());
+ CodeBlock* newCodeBlock = executable->baselineCodeBlockFor(icf->isCall ? CodeForCall : CodeForConstruct);
+ ASSERT(newCodeBlock);
+ ASSERT(newCodeBlock->instructionCount() > bytecodeOffset);
+ callerCodeBlock = newCodeBlock;
+ }
+ } else
+ #endif
+ bytecodeOffset = callerCodeBlock->bytecodeOffset(callFrame->returnPC());
+#endif
+ }
+
+ lineNumber = callerCodeBlock->lineNumberForBytecodeOffset(bytecodeOffset);
+ return callerFrame;
+}
+
+static ALWAYS_INLINE const UString getSourceURLFromCallFrame(CallFrame* callFrame)
+{
+ ASSERT(!callFrame->hasHostCallFrameFlag());
+#if ENABLE(INTERPRETER)
+#if ENABLE(JIT)
+ if (callFrame->globalData().canUseJIT())
+ return callFrame->codeBlock()->ownerExecutable()->sourceURL();
+#endif
+ return callFrame->codeBlock()->source()->url();
+
+#else
+ return callFrame->codeBlock()->ownerExecutable()->sourceURL();
+#endif
+}
+
+static StackFrameCodeType getStackFrameCodeType(CallFrame* callFrame)
+{
+ ASSERT(!callFrame->hasHostCallFrameFlag());
+
+ switch (callFrame->codeBlock()->codeType()) {
+ case EvalCode:
+ return StackFrameEvalCode;
+ case FunctionCode:
+ return StackFrameFunctionCode;
+ case GlobalCode:
+ return StackFrameGlobalCode;
+ }
+ ASSERT_NOT_REACHED();
+ return StackFrameGlobalCode;
+}
+
+void Interpreter::getStackTrace(JSGlobalData* globalData, int line, Vector<StackFrame>& results)
+{
+ CallFrame* callFrame = globalData->topCallFrame->removeHostCallFrameFlag()->trueCallFrameFromVMCode();
+ if (!callFrame || callFrame == CallFrame::noCaller())
+ return;
+
+ while (callFrame && callFrame != CallFrame::noCaller()) {
+ UString sourceURL;
+ if (callFrame->codeBlock()) {
+ sourceURL = getSourceURLFromCallFrame(callFrame);
+ StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), getStackFrameCodeType(callFrame), Strong<ExecutableBase>(*globalData, callFrame->codeBlock()->ownerExecutable()), line, sourceURL};
+ results.append(s);
+ } else {
+ StackFrame s = { Strong<JSObject>(*globalData, callFrame->callee()), StackFrameNativeCode, Strong<ExecutableBase>(), -1, UString()};
+ results.append(s);
+ }
+ callFrame = getCallerInfo(globalData, callFrame, line);
+ }
+}
+
NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSValue& exceptionValue, unsigned bytecodeOffset)
{
CodeBlock* codeBlock = callFrame->codeBlock();
// FIXME: should only really be adding these properties to VM generated exceptions,
// but the inspector currently requires these for all thrown objects.
- addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source());
+ Vector<StackFrame> stackTrace;
+ getStackTrace(&callFrame->globalData(), codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), stackTrace);
+ addErrorInfo(callFrame, exception, codeBlock->lineNumberForBytecodeOffset(bytecodeOffset), codeBlock->ownerExecutable()->source(), stackTrace);
}
isInterrupt = isInterruptedExecutionException(exception) || isTerminatedExecutionException(exception);
#define SAMPLE(codeBlock, vPC)
#endif
+#define UPDATE_BYTECODE_OFFSET() \
+ do {\
+ callFrame->setBytecodeOffsetForNonDFGCode(vPC - codeBlock->instructions().data() + 1);\
+ } while (0)
+
#if ENABLE(COMPUTED_GOTO_INTERPRETER)
#define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto *vPC->u.opcode
#if ENABLE(OPCODE_STATS)
- #define DEFINE_OPCODE(opcode) opcode: OpcodeStats::recordInstruction(opcode);
+ #define DEFINE_OPCODE(opcode) \
+ opcode:\
+ OpcodeStats::recordInstruction(opcode);\
+ UPDATE_BYTECODE_OFFSET();
#else
- #define DEFINE_OPCODE(opcode) opcode:
+ #define DEFINE_OPCODE(opcode) opcode: UPDATE_BYTECODE_OFFSET();
#endif
NEXT_INSTRUCTION();
#else
#define NEXT_INSTRUCTION() SAMPLE(codeBlock, vPC); goto interpreterLoopStart
#if ENABLE(OPCODE_STATS)
- #define DEFINE_OPCODE(opcode) case opcode: OpcodeStats::recordInstruction(opcode);
+ #define DEFINE_OPCODE(opcode) \
+ case opcode:\
+ OpcodeStats::recordInstruction(opcode);\
+ UPDATE_BYTECODE_OFFSET();
#else
- #define DEFINE_OPCODE(opcode) case opcode:
+ #define DEFINE_OPCODE(opcode) case opcode: UPDATE_BYTECODE_OFFSET();
#endif
while (1) { // iterator loop begins
interpreterLoopStart:;
#include "ArgList.h"
#include "JSCell.h"
+#include "JSFunction.h"
#include "JSValue.h"
#include "JSObject.h"
#include "Opcode.h"
class CodeBlock;
class EvalExecutable;
+ class ExecutableBase;
class FunctionExecutable;
- class JSFunction;
class JSGlobalObject;
class ProgramExecutable;
class Register;
WillExecuteStatement
};
+ enum StackFrameCodeType {
+ StackFrameGlobalCode,
+ StackFrameEvalCode,
+ StackFrameFunctionCode,
+ StackFrameNativeCode
+ };
+
+ struct StackFrame {
+ Strong<JSObject> callee;
+ StackFrameCodeType codeType;
+ Strong<ExecutableBase> executable;
+ int line;
+ UString sourceURL;
+ UString toString(CallFrame* callFrame) const
+ {
+ bool hasSourceURLInfo = !sourceURL.isNull() && !sourceURL.isEmpty();
+ bool hasLineInfo = line > -1;
+ String traceLine;
+ JSObject* stackFrameCallee = callee.get();
+
+ switch (codeType) {
+ case StackFrameEvalCode:
+ if (hasSourceURLInfo) {
+ traceLine = hasLineInfo ? String::format("eval code@%s:%d", sourceURL.ascii().data(), line)
+ : String::format("eval code@%s", sourceURL.ascii().data());
+ } else
+ traceLine = String::format("eval code");
+ break;
+ case StackFrameNativeCode: {
+ if (callee) {
+ UString functionName = getCalculatedDisplayName(callFrame, stackFrameCallee);
+ traceLine = String::format("%s@[native code]", functionName.ascii().data());
+ } else
+ traceLine = "[native code]";
+ break;
+ }
+ case StackFrameFunctionCode: {
+ UString functionName = getCalculatedDisplayName(callFrame, stackFrameCallee);
+ if (hasSourceURLInfo) {
+ traceLine = hasLineInfo ? String::format("%s@%s:%d", functionName.ascii().data(), sourceURL.ascii().data(), line)
+ : String::format("%s@%s", functionName.ascii().data(), sourceURL.ascii().data());
+ } else
+ traceLine = String::format("%s\n", functionName.ascii().data());
+ break;
+ }
+ case StackFrameGlobalCode:
+ if (hasSourceURLInfo) {
+ traceLine = hasLineInfo ? String::format("global code@%s:%d", sourceURL.ascii().data(), line)
+ : String::format("global code@%s", sourceURL.ascii().data());
+ } else
+ traceLine = String::format("global code");
+
+ }
+ return traceLine.impl();
+ }
+ };
+
class TopCallFrameSetter {
public:
TopCallFrameSetter(JSGlobalData& global, CallFrame* callFrame)
NEVER_INLINE HandlerInfo* throwException(CallFrame*&, JSValue&, unsigned bytecodeOffset);
NEVER_INLINE void debug(CallFrame*, DebugHookID, int firstLine, int lastLine);
+ static const UString getTraceLine(CallFrame*, StackFrameCodeType, const UString&, int);
+ JS_EXPORT_PRIVATE static void getStackTrace(JSGlobalData*, int line, Vector<StackFrame>& results);
void dumpSampleData(ExecState* exec);
void startSampling();
#include "CurrentTime.h"
#include "ExceptionHelpers.h"
#include "InitializeThreading.h"
+#include "Interpreter.h"
#include "JSArray.h"
#include "JSFunction.h"
#include "JSLock.h"
static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
#ifndef NDEBUG
static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
addFunction(globalData, "run", functionRun, 1);
addFunction(globalData, "load", functionLoad, 1);
addFunction(globalData, "checkSyntax", functionCheckSyntax, 1);
+ addFunction(globalData, "jscStack", functionJSCStack, 1);
addFunction(globalData, "readline", functionReadline, 0);
addFunction(globalData, "preciseTime", functionPreciseTime, 0);
#if ENABLE(SAMPLING_FLAGS)
return JSValue::encode(jsUndefined());
}
+EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
+{
+ String trace = "--> Stack trace:\n";
+ Vector<StackFrame> stackTrace;
+ Interpreter::getStackTrace(&exec->globalData(), -1, stackTrace);
+ int i = 0;
+
+ for (Vector<StackFrame>::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) {
+ StackFrame level = *iter;
+ trace += String::format(" %i %s\n", i, level.toString(exec).utf8().data());
+ i++;
+ }
+ fprintf(stderr, "%s", trace.utf8().data());
+ return JSValue::encode(jsUndefined());
+}
+
EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
{
JSLock lock(SilenceAssertionsOnly);
void finishParsing(PassRefPtr<FunctionParameters>, const Identifier&);
const Identifier& ident() { return m_ident; }
- void setInferredName(const Identifier& inferredName) { m_inferredName = inferredName; }
+ void setInferredName(const Identifier& inferredName) { ASSERT(!inferredName.isNull()); m_inferredName = inferredName; }
const Identifier& inferredName() { return m_inferredName.isEmpty() ? m_ident : m_inferredName; }
static const bool scopeIsFunction = true;
else if (isEvalNode<ParsedNode>())
*exception = createSyntaxError(lexicalGlobalObject, errMsg);
else
- *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source);
+ *exception = addErrorInfo(&lexicalGlobalObject->globalData(), createSyntaxError(lexicalGlobalObject, errMsg), errLine, *m_source, Vector<StackFrame>());
}
if (debugger && !ParsedNode::scopeIsFunction)
macro(prototype) \
macro(set) \
macro(source) \
+ macro(stack) \
macro(test) \
macro(toExponential) \
macro(toFixed) \
#include "ConstructData.h"
#include "ErrorConstructor.h"
+#include "ExceptionHelpers.h"
#include "FunctionPrototype.h"
+#include "JSArray.h"
#include "JSFunction.h"
#include "JSGlobalObject.h"
#include "JSObject.h"
return createURIError(exec->lexicalGlobalObject(), message);
}
-JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source)
+JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace)
{
const UString& sourceURL = source.provider()->url();
error->putDirect(*globalData, Identifier(globalData, linePropertyName), jsNumber(line), ReadOnly | DontDelete);
if (!sourceURL.isNull())
error->putDirect(*globalData, Identifier(globalData, sourceURLPropertyName), jsString(globalData, sourceURL), ReadOnly | DontDelete);
+ if (!stackTrace.isEmpty()) {
+ JSGlobalObject* globalObject = 0;
+ if (isTerminatedExecutionException(error) || isInterruptedExecutionException(error))
+ globalObject = globalData->dynamicGlobalObject;
+ else
+ globalObject = error->globalObject();
+ // We use the tryCreateUninitialized creation mechanism and related initialization
+ // functions as they're the only mechanism we currently have that will guarantee we
+ // don't call setters on the prototype. Technically it's faster than the alternative,
+ // but the numerous allocations that take place in this loop makes that last bit
+ // somewhat moot.
+ JSArray* stackTraceArray = JSArray::tryCreateUninitialized(*globalData, globalObject->arrayStructure(), stackTrace.size());
+ if (!stackTraceArray)
+ return error;
+ for (unsigned i = 0; i < stackTrace.size(); i++) {
+ UString stackLevel = stackTrace[i].toString(globalObject->globalExec());
+ stackTraceArray->initializeIndex(*globalData, i, jsString(globalData, stackLevel));
+ }
+ stackTraceArray->completeInitialization(stackTrace.size());
+ error->putDirect(*globalData, globalData->propertyNames->stack, stackTraceArray, ReadOnly | DontDelete);
+ }
return error;
}
-JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source)
+JSObject* addErrorInfo(ExecState* exec, JSObject* error, int line, const SourceCode& source, const Vector<StackFrame>& stackTrace)
{
- return addErrorInfo(&exec->globalData(), error, line, source);
+ return addErrorInfo(&exec->globalData(), error, line, source, stackTrace);
}
bool hasErrorInfo(ExecState* exec, JSObject* error)
#define Error_h
#include "InternalFunction.h"
+#include "Interpreter.h"
#include "JSObject.h"
#include <stdint.h>
// Methods to add
bool hasErrorInfo(ExecState*, JSObject* error);
- JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&);
+ JSObject* addErrorInfo(JSGlobalData*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&);
// ExecState wrappers.
- JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&);
+ JSObject* addErrorInfo(ExecState*, JSObject* error, int line, const SourceCode&, const Vector<StackFrame>&);
// Methods to throw Errors.
JS_EXPORT_PRIVATE JSValue throwError(ExecState*, JSValue);