Revision: 2541
Author: [email protected]
Date: Mon Jul 27 05:01:32 2009
Log: Added Error.captureStackTrace function.
Added utility function for capturing stack traces so that efficient
stack trace collection works for custom errors too, not just built-in
ones.

Review URL: http://codereview.chromium.org/159403

http://code.google.com/p/v8/source/detail?r=2541

Modified:
  /branches/bleeding_edge/src/messages.js
  /branches/bleeding_edge/src/runtime.cc
  /branches/bleeding_edge/test/mjsunit/stack-traces.js

=======================================
--- /branches/bleeding_edge/src/messages.js     Mon Jul  6 03:56:52 2009
+++ /branches/bleeding_edge/src/messages.js     Mon Jul 27 05:01:32 2009
@@ -561,20 +561,24 @@
  var kAddMessageAccessorsMarker = { };

  // Defines accessors for a property that is calculated the first time
-// the property is read and then replaces the accessor with the value.
-// Also, setting the property causes the accessors to be deleted.
+// the property is read.
  function DefineOneShotAccessor(obj, name, fun) {
    // Note that the accessors consistently operate on 'obj', not 'this'.
    // Since the object may occur in someone else's prototype chain we
    // can't rely on 'this' being the same as 'obj'.
+  var hasBeenSet = false;
+  var value;
    obj.__defineGetter__(name, function () {
-    var value = fun(obj);
-    obj[name] = value;
+    if (hasBeenSet) {
+      return value;
+    }
+    hasBeenSet = true;
+    value = fun(obj);
      return value;
    });
    obj.__defineSetter__(name, function (v) {
-    delete obj[name];
-    obj[name] = v;
+    hasBeenSet = true;
+    value = v;
    });
  }

@@ -833,22 +837,25 @@
        } else if (!IS_UNDEFINED(m)) {
          this.message = ToString(m);
        }
-      var stackTraceLimit = $Error.stackTraceLimit;
-      if (stackTraceLimit) {
-        // Cap the limit to avoid extremely big traces
-        if (stackTraceLimit < 0 || stackTraceLimit > 10000)
-          stackTraceLimit = 10000;
-        var raw_stack = %CollectStackTrace(f, stackTraceLimit);
-        DefineOneShotAccessor(this, 'stack', function (obj) {
-          return FormatRawStackTrace(obj, raw_stack);
-        });
-      }
+      captureStackTrace(this, f);
      } else {
        return new f(m);
      }
    });
  }

+function captureStackTrace(obj, cons_opt) {
+  var stackTraceLimit = $Error.stackTraceLimit;
+  if (!stackTraceLimit) return;
+  if (stackTraceLimit < 0 || stackTraceLimit > 10000)
+    stackTraceLimit = 10000;
+  var raw_stack = %CollectStackTrace(cons_opt ? cons_opt :  
captureStackTrace,
+      stackTraceLimit);
+  DefineOneShotAccessor(obj, 'stack', function (obj) {
+    return FormatRawStackTrace(obj, raw_stack);
+  });
+};
+
  $Math.__proto__ = global.Object.prototype;

  DefineError(function Error() { });
@@ -859,6 +866,8 @@
  DefineError(function EvalError() { });
  DefineError(function URIError() { });

+$Error.captureStackTrace = captureStackTrace;
+
  // Setup extra properties of the Error.prototype object.
  $Error.prototype.message = '';

=======================================
--- /branches/bleeding_edge/src/runtime.cc      Fri Jul 10 02:57:53 2009
+++ /branches/bleeding_edge/src/runtime.cc      Mon Jul 27 05:01:32 2009
@@ -7408,14 +7408,15 @@
    // Not sure when this can happen but skip it just in case.
    if (!raw_fun->IsJSFunction())
      return false;
-  if ((raw_fun == caller) && !(*seen_caller) && frame->IsConstructor()) {
+  if ((raw_fun == caller) && !(*seen_caller)) {
      *seen_caller = true;
      return false;
    }
-  // Skip the most obvious builtin calls.  Some builtin calls (such as
-  // Number.ADD which is invoked using 'call') are very difficult to
-  // recognize so we're leaving them in for now.
-  return !frame->receiver()->IsJSBuiltinsObject();
+  // Skip all frames until we've seen the caller.  Also, skip the most
+  // obvious builtin calls.  Some builtin calls (such as Number.ADD
+  // which is invoked using 'call') are very difficult to recognize
+  // so we're leaving them in for now.
+  return *seen_caller && !frame->receiver()->IsJSBuiltinsObject();
  }


@@ -7433,7 +7434,9 @@
    Handle<JSArray> result = Factory::NewJSArray(initial_size * 3);

    StackFrameIterator iter;
-  bool seen_caller = false;
+  // If the caller parameter is a function we skip frames until we're
+  // under it before starting to collect.
+  bool seen_caller = !caller->IsJSFunction();
    int cursor = 0;
    int frames_seen = 0;
    while (!iter.done() && frames_seen < limit) {
=======================================
--- /branches/bleeding_edge/test/mjsunit/stack-traces.js        Thu Jul  2  
08:30:03 2009
+++ /branches/bleeding_edge/test/mjsunit/stack-traces.js        Mon Jul 27  
05:01:32 2009
@@ -83,10 +83,27 @@
  function testAnonymousMethod() {
    (function () { FAIL }).call([1, 2, 3]);
  }
+
+function CustomError(message, stripPoint) {
+  this.message = message;
+  Error.captureStackTrace(this, stripPoint);
+}
+
+CustomError.prototype.toString = function () {
+  return "CustomError: " + this.message;
+};
+
+function testDefaultCustomError() {
+  throw new CustomError("hep-hey", undefined);
+}
+
+function testStrippedCustomError() {
+  throw new CustomError("hep-hey", CustomError);
+}

  // Utility function for testing that the expected strings occur
  // in the stack trace produced when running the given function.
-function testTrace(fun, expected) {
+function testTrace(fun, expected, unexpected) {
    var threw = false;
    try {
      fun();
@@ -94,6 +111,11 @@
      for (var i = 0; i < expected.length; i++) {
        assertTrue(e.stack.indexOf(expected[i]) != -1);
      }
+    if (unexpected) {
+      for (var i = 0; i < unexpected.length; i++) {
+        assertEquals(e.stack.indexOf(unexpected[i]), -1);
+      }
+    }
      threw = true;
    }
    assertTrue(threw);
@@ -165,6 +187,10 @@
  testTrace(testConstructor, ["new Plonk"]);
  testTrace(testRenamedMethod, ["Wookie.a$b$c$d [as d]"]);
  testTrace(testAnonymousMethod, ["Array.<anonymous>"]);
+testTrace(testDefaultCustomError, ["hep-hey", "new CustomError"],
+    ["collectStackTrace"]);
+testTrace(testStrippedCustomError, ["hep-hey"], ["new CustomError",
+    "collectStackTrace"]);

  testCallerCensorship();
  testUnintendedCallerCensorship();

--~--~---------~--~----~------------~-------~--~----~
v8-dev mailing list
[email protected]
http://groups.google.com/group/v8-dev
-~----------~----~----~----~------~----~------~--~---

Reply via email to