From: christian.plesner.hansen@gmail.com Date: Mon, 27 Jul 2009 12:01:32 +0000 (+0000) Subject: Added Error.captureStackTrace function. X-Git-Tag: upstream/4.7.83~23582 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=c1581cd5bde8cb0eb2dafc998b3dcc15d51420b0;p=platform%2Fupstream%2Fv8.git 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 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2541 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- diff --git a/src/messages.js b/src/messages.js index 870c969..fd505ff 100644 --- a/src/messages.js +++ b/src/messages.js @@ -561,20 +561,24 @@ function GetStackTraceLine(recv, fun, pos, isGlobal) { 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 @@ function DefineError(f) { } 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 ReferenceError() { }); DefineError(function EvalError() { }); DefineError(function URIError() { }); +$Error.captureStackTrace = captureStackTrace; + // Setup extra properties of the Error.prototype object. $Error.prototype.message = ''; diff --git a/src/runtime.cc b/src/runtime.cc index 350d391..7f55389 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -7408,14 +7408,15 @@ static bool ShowFrameInStackTrace(StackFrame* raw_frame, Object* caller, // 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 @@ static Object* Runtime_CollectStackTrace(Arguments args) { Handle 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) { diff --git a/test/mjsunit/stack-traces.js b/test/mjsunit/stack-traces.js index e457ece..3bb5755 100644 --- a/test/mjsunit/stack-traces.js +++ b/test/mjsunit/stack-traces.js @@ -84,9 +84,26 @@ 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 @@ function testTrace(fun, expected) { 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(testValue, ["at Number.causeError"]); testTrace(testConstructor, ["new Plonk"]); testTrace(testRenamedMethod, ["Wookie.a$b$c$d [as d]"]); testTrace(testAnonymousMethod, ["Array."]); +testTrace(testDefaultCustomError, ["hep-hey", "new CustomError"], + ["collectStackTrace"]); +testTrace(testStrippedCustomError, ["hep-hey"], ["new CustomError", + "collectStackTrace"]); testCallerCensorship(); testUnintendedCallerCensorship();