From 2cc6a0c5453d9c2e3348f7ef4fb535a433728c21 Mon Sep 17 00:00:00 2001 From: legendecas Date: Wed, 22 Nov 2023 01:47:32 +0800 Subject: [PATCH] repl: fix prepareStackTrace frames array order The second parameter of `Error.prepareStackTrace` is an array of reversed call site frames. PR-URL: https://github.com/nodejs/node/pull/50827 Fixes: https://github.com/nodejs/node/issues/50733 Reviewed-By: Ethan Arrowood Reviewed-By: James M Snell Reviewed-By: Joyee Cheung Reviewed-By: Geoffrey Booth --- lib/repl.js | 26 +++++++++---------- .../parallel/test-repl-pretty-custom-stack.js | 12 +++++++-- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/lib/repl.js b/lib/repl.js index 3029c94b1e1ac0..8b96a1f55652af 100644 --- a/lib/repl.js +++ b/lib/repl.js @@ -45,7 +45,7 @@ const { ArrayPrototypeAt, ArrayPrototypeFilter, - ArrayPrototypeFindIndex, + ArrayPrototypeFindLastIndex, ArrayPrototypeForEach, ArrayPrototypeIncludes, ArrayPrototypeJoin, @@ -53,15 +53,13 @@ const { ArrayPrototypePop, ArrayPrototypePush, ArrayPrototypePushApply, - ArrayPrototypeReverse, ArrayPrototypeShift, ArrayPrototypeSlice, ArrayPrototypeSome, ArrayPrototypeSort, - ArrayPrototypeSplice, ArrayPrototypeUnshift, Boolean, - Error, + Error: MainContextError, FunctionPrototypeBind, JSONStringify, MathMaxApply, @@ -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); @@ -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); diff --git a/test/parallel/test-repl-pretty-custom-stack.js b/test/parallel/test-repl-pretty-custom-stack.js index a10cd032a688c4..f5697c2362e261 100644 --- a/test/parallel/test-repl-pretty-custom-stack.js +++ b/test/parallel/test-repl-pretty-custom-stack.js @@ -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) => { @@ -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' +});