Skip to content

Commit

Permalink
repl: fix prepareStackTrace frames array order
Browse files Browse the repository at this point in the history
The second parameter of `Error.prepareStackTrace` is an array of
reversed call site frames.

PR-URL: nodejs#50827
Fixes: nodejs#50733
Reviewed-By: Ethan Arrowood <ethan@arrowood.dev>
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com>
Reviewed-By: Geoffrey Booth <webadmin@geoffreybooth.com>
  • Loading branch information
legendecas authored and pull[bot] committed May 23, 2024
1 parent 97f7592 commit 37518ac
Show file tree
Hide file tree
Showing 2 changed files with 22 additions and 16 deletions.
26 changes: 12 additions & 14 deletions lib/repl.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,21 @@
const {
ArrayPrototypeAt,
ArrayPrototypeFilter,
ArrayPrototypeFindIndex,
ArrayPrototypeFindLastIndex,
ArrayPrototypeForEach,
ArrayPrototypeIncludes,
ArrayPrototypeJoin,
ArrayPrototypeMap,
ArrayPrototypePop,
ArrayPrototypePush,
ArrayPrototypePushApply,
ArrayPrototypeReverse,
ArrayPrototypeShift,
ArrayPrototypeSlice,
ArrayPrototypeSome,
ArrayPrototypeSort,
ArrayPrototypeSplice,
ArrayPrototypeUnshift,
Boolean,
Error,
Error: MainContextError,
FunctionPrototypeBind,
JSONStringify,
MathMaxApply,
Expand Down Expand Up @@ -630,10 +628,10 @@ function REPLServer(prompt,
if (self.breakEvalOnSigint) {
const interrupt = new Promise((resolve, reject) => {
sigintListener = () => {
const tmp = Error.stackTraceLimit;
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = 0;
const tmp = MainContextError.stackTraceLimit;
if (isErrorStackTraceLimitWritable()) MainContextError.stackTraceLimit = 0;
const err = new ERR_SCRIPT_EXECUTION_INTERRUPTED();
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = tmp;
if (isErrorStackTraceLimitWritable()) MainContextError.stackTraceLimit = tmp;
reject(err);
};
prioritizedSigintQueue.add(sigintListener);
Expand Down Expand Up @@ -679,23 +677,23 @@ function REPLServer(prompt,
if (typeof stackFrames === 'object') {
// Search from the bottom of the call stack to
// find the first frame with a null function name
const idx = ArrayPrototypeFindIndex(
ArrayPrototypeReverse(stackFrames),
const idx = ArrayPrototypeFindLastIndex(
stackFrames,
(frame) => frame.getFunctionName() === null,
);
// If found, get rid of it and everything below it
frames = ArrayPrototypeSplice(stackFrames, idx + 1);
frames = ArrayPrototypeSlice(stackFrames, 0, idx);
} else {
frames = stackFrames;
}
// FIXME(devsnek): this is inconsistent with the checks
// that the real prepareStackTrace dispatch uses in
// lib/internal/errors.js.
if (typeof Error.prepareStackTrace === 'function') {
return Error.prepareStackTrace(error, frames);
if (typeof MainContextError.prepareStackTrace === 'function') {
return MainContextError.prepareStackTrace(error, frames);
}
ArrayPrototypePush(frames, error);
return ArrayPrototypeJoin(ArrayPrototypeReverse(frames), '\n at ');
ArrayPrototypeUnshift(frames, error);
return ArrayPrototypeJoin(frames, '\n at ');
});
decorateErrorStack(e);

Expand Down
12 changes: 10 additions & 2 deletions test/parallel/test-repl-pretty-custom-stack.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,9 @@ const origPrepareStackTrace = Error.prepareStackTrace;
Error.prepareStackTrace = (err, stack) => {
if (err instanceof SyntaxError)
return err.toString();
stack.push(err);
return stack.reverse().join('--->\n');
// Insert the error at the beginning of the stack
stack.unshift(err);
return stack.join('--->\n');
};

process.on('uncaughtException', (e) => {
Expand Down Expand Up @@ -78,3 +79,10 @@ const tests = [
];

tests.forEach(run);

// Verify that the stack can be generated when Error.prepareStackTrace is deleted.
delete Error.prepareStackTrace;
run({
command: 'throw new TypeError(\'Whoops!\')',
expected: 'Uncaught TypeError: Whoops!\n'
});

0 comments on commit 37518ac

Please sign in to comment.