Lazy call to custom stack trace formatting using Error.prepareStackTrace.
authoryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 26 Jul 2013 11:28:08 +0000 (11:28 +0000)
committeryangguo@chromium.org <yangguo@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 26 Jul 2013 11:28:08 +0000 (11:28 +0000)
This enables custom stack trace formatting for stack overflow.
A consequence is that stack trace formatting is now easily observable,
but we already established that the default stack trace formatting can
be observed anyways. It is only triggered by the .stack getter, and
it has to be explicitly called, (e.g. not implicitly after GC).

R=mstarzinger@chromium.org
BUG=v8:2559

Review URL: https://codereview.chromium.org/20692002

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@15902 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

src/messages.js
test/mjsunit/stack-traces-custom-lazy.js [new file with mode: 0644]
test/mjsunit/stack-traces.js

index 8b647dd..92ac1bc 100644 (file)
@@ -1078,7 +1078,26 @@ function GetStackFrames(raw_stack) {
 }
 
 
-function FormatStackTrace(error_string, frames) {
+// Flag to prevent recursive call of Error.prepareStackTrace.
+var formatting_custom_stack_trace = false;
+
+
+function FormatStackTrace(obj, error_string, frames) {
+  if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
+    var array = [];
+    %MoveArrayContents(frames, array);
+    formatting_custom_stack_trace = true;
+    var stack_trace = void 0;
+    try {
+      stack_trace = $Error.prepareStackTrace(obj, array);
+    } catch (e) {
+      throw e;  // The custom formatting function threw.  Rethrow.
+    } finally {
+      formatting_custom_stack_trace = false;
+    }
+    return stack_trace;
+  }
+
   var lines = new InternalArray();
   lines.push(error_string);
   for (var i = 0; i < frames.length; i++) {
@@ -1115,10 +1134,6 @@ function GetTypeName(receiver, requireConstructor) {
 }
 
 
-// Flag to prevent recursive call of Error.prepareStackTrace.
-var formatting_custom_stack_trace = false;
-
-
 function captureStackTrace(obj, cons_opt) {
   var stackTraceLimit = $Error.stackTraceLimit;
   if (!stackTraceLimit || !IS_NUMBER(stackTraceLimit)) return;
@@ -1129,21 +1144,6 @@ function captureStackTrace(obj, cons_opt) {
                                  cons_opt ? cons_opt : captureStackTrace,
                                  stackTraceLimit);
 
-  // Don't be lazy if the error stack formatting is custom (observable).
-  if (IS_FUNCTION($Error.prepareStackTrace) && !formatting_custom_stack_trace) {
-    var array = [];
-    %MoveArrayContents(GetStackFrames(stack), array);
-    formatting_custom_stack_trace = true;
-    try {
-      obj.stack = $Error.prepareStackTrace(obj, array);
-    } catch (e) {
-      throw e;  // The custom formatting function threw.  Rethrow.
-    } finally {
-      formatting_custom_stack_trace = false;
-    }
-    return;
-  }
-
   var error_string = FormatErrorString(obj);
   // The holder of this getter ('obj') may not be the receiver ('this').
   // When this getter is called the first time, we use the context values to
@@ -1151,7 +1151,7 @@ function captureStackTrace(obj, cons_opt) {
   // property (on the holder).
   var getter = function() {
     // Stack is still a raw array awaiting to be formatted.
-    var result = FormatStackTrace(error_string, GetStackFrames(stack));
+    var result = FormatStackTrace(obj, error_string, GetStackFrames(stack));
     // Turn this accessor into a data property.
     %DefineOrRedefineDataProperty(obj, 'stack', result, NONE);
     // Release context values.
@@ -1321,7 +1321,7 @@ function SetUpStackOverflowBoilerplate() {
     // We may not have captured any stack trace.
     if (IS_UNDEFINED(stack)) return stack;
 
-    var result = FormatStackTrace(error_string, GetStackFrames(stack));
+    var result = FormatStackTrace(holder, error_string, GetStackFrames(stack));
     // Replace this accessor with a data property.
     %DefineOrRedefineDataProperty(holder, 'stack', result, NONE);
     return result;
diff --git a/test/mjsunit/stack-traces-custom-lazy.js b/test/mjsunit/stack-traces-custom-lazy.js
new file mode 100644 (file)
index 0000000..91d97f3
--- /dev/null
@@ -0,0 +1,49 @@
+// Copyright 2013 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+//     * Redistributions of source code must retain the above copyright
+//       notice, this list of conditions and the following disclaimer.
+//     * Redistributions in binary form must reproduce the above
+//       copyright notice, this list of conditions and the following
+//       disclaimer in the documentation and/or other materials provided
+//       with the distribution.
+//     * Neither the name of Google Inc. nor the names of its
+//       contributors may be used to endorse or promote products derived
+//       from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+function testPrepareStackTrace(closure) {
+  var error = undefined;
+  try {
+    closure();
+    assertUnreachable();
+  } catch (e) {
+    error = e;
+  }
+
+  // We expect custom formatting to be lazy. Setting the custom
+  // function right before calling error.stack should be fine.
+  Error.prepareStackTrace = function(e, frames) {
+    return "bar";
+  }
+
+  assertEquals("bar", error.stack);
+  Error.prepareStackTrace = undefined;
+}
+
+testPrepareStackTrace(function() { throw new Error("foo"); });
+testPrepareStackTrace(function f() { f(); });
+
index 4a37ee6..46a16eb 100644 (file)
@@ -315,7 +315,11 @@ assertTrue(fired);
 Error.prepareStackTrace = function() { throw new Error("abc"); };
 var message;
 try {
-  throw new Error();
+  try {
+    throw new Error();
+  } catch (e) {
+    e.stack;
+  }
 } catch (e) {
   message = e.message;
 }
@@ -324,6 +328,6 @@ assertEquals("abc", message);
 
 // Test that modifying Error.prepareStackTrace by itself works.
 Error.prepareStackTrace = function() { Error.prepareStackTrace = "custom"; };
-new Error();
+new Error().stack;
 
 assertEquals("custom", Error.prepareStackTrace);