Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
Use strings instead of stack frames for long stack traces.
Browse files Browse the repository at this point in the history
This fixes #117 and #111.
  • Loading branch information
domenic committed Nov 22, 2012
1 parent 46a0366 commit 61567b7
Showing 1 changed file with 35 additions and 159 deletions.
194 changes: 35 additions & 159 deletions q.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,33 +24,6 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*
* With formatStackTrace and formatSourcePosition functions
* Copyright 2006-2008 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 (definition) {
Expand Down Expand Up @@ -260,133 +233,39 @@ if (typeof ReturnValue !== "undefined") {

// long stack traces

function formatStackTrace(error, frames) {
var lines = [];
try {
lines.push(error.toString());
} catch (e) {
try {
lines.push("<error: " + e + ">");
} catch (ee) {
lines.push("<error>");
}
}
for (var i = 0; i < frames.length; i++) {
var frame = frames[i];
var line;

// <Inserted by @domenic>
if (typeof frame === "string") {
lines.push(frame);
// </Inserted by @domenic>
} else {
try {
line = formatSourcePosition(frame);
} catch (e) {
try {
line = "<error: " + e + ">";
} catch (ee) {
// Any code that reaches this point is seriously nasty!
line = "<error>";
}
}
lines.push(" at " + line);
}
}
return lines.join("\n");
}
var STACK_JUMP_SEPARATOR = "From previous event:";

function formatSourcePosition(frame) {
var fileLocation = "";
if (frame.isNative()) {
fileLocation = "native";
} else if (frame.isEval()) {
fileLocation = "eval at " + frame.getEvalOrigin();
} else {
var fileName = frame.getFileName();
if (fileName) {
fileLocation += fileName;
var lineNumber = frame.getLineNumber();
if (lineNumber !== null) {
fileLocation += ":" + lineNumber;
var columnNumber = frame.getColumnNumber();
if (columnNumber) {
fileLocation += ":" + columnNumber;
}
}
}
}
if (!fileLocation) {
fileLocation = "unknown source";
}
var line = "";
var functionName = frame.getFunction().name;
var addPrefix = true;
var isConstructor = frame.isConstructor();
var isMethodCall = !(frame.isToplevel() || isConstructor);
if (isMethodCall) {
var methodName = frame.getMethodName();
line += frame.getTypeName() + ".";
if (functionName) {
line += functionName;
if (methodName && (methodName !== functionName)) {
line += " [as " + methodName + "]";
}
} else {
line += methodName || "<anonymous>";
function filterStackString(stackString) {
var lines = stackString.split("\n");
var desiredLines = [];
for (var i = 0; i < lines.length; ++i) {
var line = lines[i];

if (!isInternalFrame(line) && !isNodeFrame(line)) {
desiredLines.push(line);
}
} else if (isConstructor) {
line += "new " + (functionName || "<anonymous>");
} else if (functionName) {
line += functionName;
} else {
line += fileLocation;
addPrefix = false;
}
if (addPrefix) {
line += " (" + fileLocation + ")";
}
return line;
return desiredLines.join("\n");
}

function isInternalFrame(fileName, frame) {
if (fileName !== qFileName) {
return false;
}
var line = frame.getLineNumber();
return line >= qStartingLine && line <= qEndingLine;
function isNodeFrame(stackLine) {
return stackLine.indexOf("(module.js:") !== -1 ||
stackLine.indexOf("(node.js:") !== -1;
}

/*
* Retrieves an array of structured stack frames parsed from the ``stack``
* property of a given object.
*
* @param objectWithStack {Object} an object with a ``stack`` property: usually
* an error or promise.
*
* @returns an array of stack frame objects. For more information, see
* [V8's JavaScript stack trace API documentation](http://code.google.com/p/v8/wiki/JavaScriptStackTraceApi).
*/
function getStackFrames(objectWithStack) {
var oldPrepareStackTrace = Error.prepareStackTrace;

Error.prepareStackTrace = function (error, frames) {
// Filter out frames from the innards of Node and Q.
return frames.filter(function (frame) {
var fileName = frame.getFileName();
return (
fileName !== "module.js" &&
fileName !== "node.js" &&
!isInternalFrame(fileName, frame)
);
});
};
function isInternalFrame(stackLine) {
var pieces = /at [^ ]+ \((.*):(\d+):\d+\)/.exec(stackLine);

var stack = objectWithStack.stack;
if (!pieces) {
return false;
}

Error.prepareStackTrace = oldPrepareStackTrace;
var fileName = pieces[1];
var lineNumber = pieces[2];

return stack;
return fileName === qFileName &&
lineNumber >= qStartingLine &&
lineNumber <= qEndingLine;
}

// discover own file name and line number range for filtering stack
Expand Down Expand Up @@ -476,6 +355,11 @@ function defer() {

if (Error.captureStackTrace) {
Error.captureStackTrace(promise, defer);

// Reify the stack into a string by using the accessor; this prevents
// memory leaks as per GH-111. At the same time, cut off the first line;
// it's always just "[object Promise]\n", as per the `toString`.
promise.stack = promise.stack.substring(promise.stack.indexOf("\n") + 1);
}

function become(resolvedValue) {
Expand Down Expand Up @@ -1409,23 +1293,15 @@ function done(promise, fulfilled, rejected, progress) {
// If possible (that is, if in V8), transform the error stack
// trace by removing Node and Q cruft, then concatenating with
// the stack trace of the promise we are ``done``ing. See #57.
var errorStackFrames;
if (
Error.captureStackTrace &&
if (Error.captureStackTrace &&
typeof error === "object" &&
(errorStackFrames = getStackFrames(error))
error !== null &&
error.stack &&
error.stack.indexOf(STACK_JUMP_SEPARATOR) === -1
) {
var promiseStackFrames = getStackFrames(promise);

// Check to make sure the stack trace hasn't already been
// rendered (possibly by us).
if (typeof errorStackFrames !== "string") {
var combinedStackFrames = errorStackFrames.concat(
"From previous event:",
promiseStackFrames
);
error.stack = formatStackTrace(error, combinedStackFrames);
}
error.stack = filterStackString(error.stack) +
"\n" + STACK_JUMP_SEPARATOR + "\n" +
filterStackString(promise.stack);
}

if (exports.onerror) {
Expand Down

0 comments on commit 61567b7

Please sign in to comment.