Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add new StackStyle.MOZILLA_LF #888

Merged
merged 3 commits into from
May 11, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
215 changes: 86 additions & 129 deletions src/org/mozilla/javascript/RhinoException.java

Large diffs are not rendered by default.

24 changes: 13 additions & 11 deletions src/org/mozilla/javascript/StackStyle.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,29 @@

package org.mozilla.javascript;

/**
* This class distinguishes between the three different supported stack trace formats.
*/

/** This class distinguishes between the three different supported stack trace formats. */
public enum StackStyle {
/**
* This is the default stack trace style in Rhino, which is like Java:
* <code> at fileName:lineNumber (functionName)</code>
* This is the default stack trace style in Rhino, which is like Java: <code>
* at fileName:lineNumber (functionName)</code>
*/
RHINO,

/**
* This stack trace style comes from the old Mozilla code:
* <code>functionName()@fileName:lineNumber</code>
* This stack trace style comes from the old Mozilla code: <code>
* functionName()@fileName:lineNumber</code>
*/
MOZILLA,

/**
* This stack trace style matches that output from V8, either:
* <code> at functionName (fileName:lineNumber:columnNumber)</code>
* or, for anonymous functions:
* This is the same as MOZILLA but uses LF as speparator instead of the system dependent line
* separator.
*/
MOZILLA_LF,

/**
* This stack trace style matches that output from V8, either: <code>
* at functionName (fileName:lineNumber:columnNumber)</code> or, for anonymous functions:
* <code> at fileName:lineNumber:columnNumber</code>
*/
V8
Expand Down
230 changes: 230 additions & 0 deletions testsrc/jstests/extensions/stack-traces-mozilla-lf.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,230 @@
load("testsrc/assert.js");

function nestedThrower(msg) {
throw new Error(msg);
}
function parentThrower(msg) {
nestedThrower(msg);
}
function grandparentThrower(msg) {
parentThrower(msg);
}
function ObjectThrower(msg) {
nestedThrower(msg);
}
function nestedCapture(o, f) {
Error.captureStackTrace(o, f);
}
function parentCapture(o, f) {
nestedCapture(o, f);
}
function grandParentCapture(o, f) {
parentCapture(o, f);
}
function countLines(msg) {
if (!msg) {
return 0;
}
// Subtract one for the newline at the end
return msg.split('\n').length - 1;
}

// Test that toString contains the error but not the stack
// and test that the stack contains the file name
try {
throw new Error('Test 1');
} catch (e) {
assertFalse(e.stack == undefined);
assertTrue(/Test 1/.test(e.toString()));
assertFalse(/stack-traces-mozilla-lf.js/.test(e.toString()));
assertFalse(/Test 1/.test(e.stack));
assertTrue(/stack-traces-mozilla-lf.js/.test(e.stack));
}

// Assert that the function name is nested inside a nested stack trace
try {
nestedThrower('Nested 1');
} catch (e) {
assertFalse(/Nested 1/.test(e.stack));
assertTrue(/nestedThrower/.test(e.stack));
}

// Do the same for a second level of nesting
try {
parentThrower('Nested 2');
} catch (e) {
assertFalse(/Nested 2/.test(e.stack));
assertTrue(/nestedThrower/.test(e.stack));
assertTrue(/parentThrower/.test(e.stack));
}

// Do the same for a constructor
try {
new ObjectThrower('Nested 3');
} catch (e) {
assertFalse(/Nested 3/.test(e.stack));
assertTrue(/nestedThrower/.test(e.stack));
assertTrue(/ObjectThrower/.test(e.stack));
}

// Count stack lines before and after changing limit
try {
grandparentThrower('Count 1');
} catch (e) {
assertTrue(countLines(e.stack) >= 3);
}

assertTrue(Error.stackTraceLimit != undefined);
Error.stackTraceLimit = 2;
assertEquals(2, Error.stackTraceLimit);

try {
grandparentThrower('Count 2');
} catch (e) {
assertEquals(2, countLines(e.stack));
}

Error.stackTraceLimit = 0;
assertEquals(0, Error.stackTraceLimit);

try {
grandparentThrower('Count 3');
} catch (e) {
assertEquals(0, countLines(e.stack));
}

Error.stackTraceLimit = Infinity;
assertEquals(Infinity, Error.stackTraceLimit);

try {
grandparentThrower('Count 1');
} catch (e) {
assertTrue(countLines(e.stack) >= 3);
}

// Test captureStackTrace

var o = {};
grandParentCapture(o);
assertTrue(/nestedCapture/.test(o.stack));
assertTrue(/parentCapture/.test(o.stack));
assertTrue(/grandParentCapture/.test(o.stack));

// Put in a function to be hidden from the stack

var m = {};
grandParentCapture(m, parentCapture);
assertTrue(/grandParentCapture/.test(m.stack));
assertFalse(/parentCapture/.test(m.stack));
assertFalse(/nestedCapture/.test(m.stack));

// Put in a function not in the stack

var n = {};
grandParentCapture(n, print);
assertFalse(/nestedCapture/.test(n.stack));
assertFalse(/parentCapture/.test(n.stack));
assertFalse(/grandParentCapture/.test(n.stack));

// Test prepareStackTrace

assertEquals(undefined, Error.prepareStackTrace);

var prepareCalled = false;

function diagnoseStack(err, stack) {
var s = '';
prepareCalled = true;
stack.forEach(function (e) {
assertEquals('object', typeof e);
s += e.getFunctionName() + ' ';
s += e.getMethodName() + ' ';
s += e.getFileName() + ' ';
s += e.getLineNumber() + ' ';
s += '\n';
});
return s;
}
Error.prepareStackTrace = diagnoseStack;

var s1 = {};
grandParentCapture(s1);
assertTrue(/nestedCapture/.test(s1.stack));
assertTrue(/parentCapture/.test(s1.stack));
assertTrue(/grandParentCapture/.test(s1.stack));
assertTrue(prepareCalled);

// Test that it is limited by the function flag as above

var s2 = {};
grandParentCapture(s2, parentCapture);
assertFalse(/nestedCapture/.test(s2.stack));
assertFalse(/parentCapture/.test(s2.stack));
assertTrue(/grandParentCapture/.test(s2.stack));

// Test that it all works on a throw. Note that the error text isn't included.

try {
grandparentThrower('Custom 1');
} catch (e) {
assertFalse(/Custom 1/.test(e.stack));
assertTrue(/nestedThrower/.test(e.stack));
assertTrue(/parentThrower/.test(e.stack));
assertTrue(/grandparentThrower/.test(e.stack));
}

// And test that it works the old way when we format it back
Error.prepareStackTrace = undefined;

try {
grandparentThrower('Custom 2');
} catch (e) {
assertFalse(/Custom 2/.test(e.stack));
assertTrue(/nestedThrower/.test(e.stack));
assertTrue(/parentThrower/.test(e.stack));
assertTrue(/grandparentThrower/.test(e.stack));
}

// test that all the functions on a stack frame work

function printFrame(l, f) {
var o =
{typeofThis: typeof f.getThis(),
typeName: f.getTypeName(),
function: f.getFunction(),
functionName: f.getFunctionName(),
methodName: f.getMethodName(),
fileName: f.getFileName(),
lineNumber: f.getLineNumber(),
columnNumber: f.getColumnNumber(),
evalOrigin: f.getEvalOrigin(),
topLevel: f.isToplevel(),
eval: f.isEval(),
native: f.isNative(),
constructor: f.isConstructor()
};

l.push(o);
return l;
}

Error.prepareStackTrace = function (e, frames) {
return frames.reduce(printFrame, []);
};

try {
grandparentThrower('testing stack');
} catch (e) {
e.stack.forEach(function (f) {
verifyFrame(f);
});
}

function verifyFrame(f) {
assertEquals(typeof f.fileName, 'string');
assertEquals(typeof f.lineNumber, 'number');
assertEquals(typeof f.topLevel, 'boolean');
assertEquals(typeof f.eval, 'boolean');
assertEquals(typeof f.native, 'boolean');
assertEquals(typeof f.constructor, 'boolean');
}
Loading