Implement Error.stack
authoroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Feb 2012 22:38:39 +0000 (22:38 +0000)
committeroliver@apple.com <oliver@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 16 Feb 2012 22:38:39 +0000 (22:38 +0000)
https://bugs.webkit.org/show_bug.cgi?id=66994

Reviewed by Gavin Barraclough.

Source/JavaScriptCore:

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):

LayoutTests:

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.

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@107980 268f45cc-cd09-0410-ab3c-d52691b4dbfc

16 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/js/exception-properties-expected.txt
LayoutTests/fast/js/script-tests/exception-properties.js
LayoutTests/fast/js/script-tests/stack-trace.js [new file with mode: 0644]
LayoutTests/fast/js/stack-trace-expected.txt [new file with mode: 0644]
LayoutTests/fast/js/stack-trace.html [new file with mode: 0644]
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def
Source/JavaScriptCore/interpreter/Interpreter.cpp
Source/JavaScriptCore/interpreter/Interpreter.h
Source/JavaScriptCore/jsc.cpp
Source/JavaScriptCore/parser/Nodes.h
Source/JavaScriptCore/parser/Parser.h
Source/JavaScriptCore/runtime/CommonIdentifiers.h
Source/JavaScriptCore/runtime/Error.cpp
Source/JavaScriptCore/runtime/Error.h

index 3f49e79..2f6647b 100644 (file)
@@ -1,3 +1,48 @@
+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.
index 0803670..5363e04 100644 (file)
@@ -4,7 +4,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 
 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
index c2f60da..19eb345 100644 (file)
@@ -16,7 +16,7 @@ try {
     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', '""');
diff --git a/LayoutTests/fast/js/script-tests/stack-trace.js b/LayoutTests/fast/js/script-tests/stack-trace.js
new file mode 100644 (file)
index 0000000..4322174
--- /dev/null
@@ -0,0 +1,214 @@
+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;
diff --git a/LayoutTests/fast/js/stack-trace-expected.txt b/LayoutTests/fast/js/stack-trace-expected.txt
new file mode 100644 (file)
index 0000000..9588de3
--- /dev/null
@@ -0,0 +1,316 @@
+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
+
+
diff --git a/LayoutTests/fast/js/stack-trace.html b/LayoutTests/fast/js/stack-trace.html
new file mode 100644 (file)
index 0000000..e08c910
--- /dev/null
@@ -0,0 +1,14 @@
+<!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>
index 35cb9a8..a189cf5 100644 (file)
@@ -1,3 +1,41 @@
+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
index f3bd8b0..3549d53 100644 (file)
@@ -208,6 +208,7 @@ EXPORTS
     ?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
index f6b3be2..019c80b 100644 (file)
@@ -45,7 +45,6 @@
 #include "JSActivation.h"
 #include "JSArray.h"
 #include "JSByteArray.h"
-#include "JSFunction.h"
 #include "JSNotAnObject.h"
 #include "JSPropertyNameIterator.h"
 #include "LiteralParser.h"
@@ -60,6 +59,7 @@
 #include "Register.h"
 #include "SamplingTool.h"
 #include "StrictEvalActivation.h"
+#include "StrongInlines.h"
 #include "UStringConcatenate.h"
 #include <limits.h>
 #include <stdio.h>
@@ -790,6 +790,132 @@ static void appendSourceToError(CallFrame* callFrame, ErrorInstance* exception,
     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();
@@ -808,7 +934,9 @@ NEVER_INLINE HandlerInfo* Interpreter::throwException(CallFrame*& callFrame, JSV
 
             // 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);
@@ -1743,20 +1871,31 @@ JSValue Interpreter::privateExecute(ExecutionFlag flag, RegisterFile* registerFi
     #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:;
index 2e6a3ca..3ea0a58 100644 (file)
@@ -31,6 +31,7 @@
 
 #include "ArgList.h"
 #include "JSCell.h"
+#include "JSFunction.h"
 #include "JSValue.h"
 #include "JSObject.h"
 #include "Opcode.h"
@@ -42,8 +43,8 @@ namespace JSC {
 
     class CodeBlock;
     class EvalExecutable;
+    class ExecutableBase;
     class FunctionExecutable;
-    class JSFunction;
     class JSGlobalObject;
     class ProgramExecutable;
     class Register;
@@ -62,6 +63,63 @@ namespace JSC {
         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)
@@ -151,6 +209,8 @@ namespace JSC {
 
         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();
index 47ec8c6..25245e6 100644 (file)
@@ -27,6 +27,7 @@
 #include "CurrentTime.h"
 #include "ExceptionHelpers.h"
 #include "InitializeThreading.h"
+#include "Interpreter.h"
 #include "JSArray.h"
 #include "JSFunction.h"
 #include "JSLock.h"
@@ -78,6 +79,7 @@ static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>&
 
 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*);
@@ -184,6 +186,7 @@ protected:
         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)
@@ -252,6 +255,22 @@ EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
     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);
index 278d17e..1c009ee 100644 (file)
@@ -1493,7 +1493,7 @@ namespace JSC {
         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;
index e3e6e1e..9b76242 100644 (file)
@@ -1025,7 +1025,7 @@ PassRefPtr<ParsedNode> Parser<LexerType>::parse(JSGlobalObject* lexicalGlobalObj
         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)
index 08d8644..d79e5c7 100644 (file)
@@ -62,6 +62,7 @@
     macro(prototype) \
     macro(set) \
     macro(source) \
+    macro(stack) \
     macro(test) \
     macro(toExponential) \
     macro(toFixed) \
index 0947e3c..243dc88 100644 (file)
@@ -26,7 +26,9 @@
 
 #include "ConstructData.h"
 #include "ErrorConstructor.h"
+#include "ExceptionHelpers.h"
 #include "FunctionPrototype.h"
+#include "JSArray.h"
 #include "JSFunction.h"
 #include "JSGlobalObject.h"
 #include "JSObject.h"
@@ -116,7 +118,7 @@ JSObject* createURIError(ExecState* exec, const UString& message)
     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();
 
@@ -124,13 +126,34 @@ JSObject* addErrorInfo(JSGlobalData* globalData, JSObject* error, int line, cons
         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)
index 88b540a..59b3949 100644 (file)
@@ -24,6 +24,7 @@
 #define Error_h
 
 #include "InternalFunction.h"
+#include "Interpreter.h"
 #include "JSObject.h"
 #include <stdint.h>
 
@@ -56,9 +57,9 @@ namespace JSC {
 
     // 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);