lib: stop using prepareStackTrace

PR-URL: https://github.com/nodejs/node/pull/29777
Reviewed-By: Ben Coe <bencoe@gmail.com>
Reviewed-By: Colin Ihrig <cjihrig@gmail.com>
This commit is contained in:
Gus Caplan 2019-09-16 15:32:15 -05:00 committed by Rich Trott
parent 48a1f75a90
commit 70c2444155
4 changed files with 65 additions and 52 deletions

View File

@ -23,13 +23,16 @@
const { Object, ObjectPrototype } = primordials; const { Object, ObjectPrototype } = primordials;
const { Buffer } = require('buffer'); const { Buffer } = require('buffer');
const { codes: { const {
ERR_AMBIGUOUS_ARGUMENT, codes: {
ERR_INVALID_ARG_TYPE, ERR_AMBIGUOUS_ARGUMENT,
ERR_INVALID_ARG_VALUE, ERR_INVALID_ARG_TYPE,
ERR_INVALID_RETURN_VALUE, ERR_INVALID_ARG_VALUE,
ERR_MISSING_ARGS ERR_INVALID_RETURN_VALUE,
} } = require('internal/errors'); ERR_MISSING_ARGS,
},
overrideStackTrace,
} = require('internal/errors');
const AssertionError = require('internal/assert/assertion_error'); const AssertionError = require('internal/assert/assertion_error');
const { openSync, closeSync, readSync } = require('fs'); const { openSync, closeSync, readSync } = require('fs');
const { inspect } = require('internal/util/inspect'); const { inspect } = require('internal/util/inspect');
@ -262,10 +265,8 @@ function getErrMessage(message, fn) {
Error.captureStackTrace(err, fn); Error.captureStackTrace(err, fn);
Error.stackTraceLimit = tmpLimit; Error.stackTraceLimit = tmpLimit;
const tmpPrepare = Error.prepareStackTrace; overrideStackTrace.set(err, (_, stack) => stack);
Error.prepareStackTrace = (_, stack) => stack;
const call = err.stack[0]; const call = err.stack[0];
Error.prepareStackTrace = tmpPrepare;
const filename = call.getFileName(); const filename = call.getFileName();
const line = call.getLineNumber() - 1; const line = call.getLineNumber() - 1;

View File

@ -21,9 +21,18 @@ const { kMaxLength } = internalBinding('buffer');
const MainContextError = Error; const MainContextError = Error;
const ErrorToString = Error.prototype.toString; const ErrorToString = Error.prototype.toString;
// Polyfill of V8's Error.prepareStackTrace API. const overrideStackTrace = new WeakMap();
// https://crbug.com/v8/7848
const prepareStackTrace = (globalThis, error, trace) => { const prepareStackTrace = (globalThis, error, trace) => {
// API for node internals to override error stack formatting
// without interfering with userland code.
if (overrideStackTrace.has(error)) {
const f = overrideStackTrace.get(error);
overrideStackTrace.delete(error);
return f(error, trace);
}
// Polyfill of V8's Error.prepareStackTrace API.
// https://crbug.com/v8/7848
// `globalThis` is the global that contains the constructor which // `globalThis` is the global that contains the constructor which
// created `error`. // created `error`.
if (typeof globalThis.Error.prepareStackTrace === 'function') { if (typeof globalThis.Error.prepareStackTrace === 'function') {
@ -36,6 +45,11 @@ const prepareStackTrace = (globalThis, error, trace) => {
return MainContextError.prepareStackTrace(error, trace); return MainContextError.prepareStackTrace(error, trace);
} }
// Normal error formatting:
//
// Error: Message
// at function (file)
// at file
const errorString = ErrorToString.call(error); const errorString = ErrorToString.call(error);
if (trace.length === 0) { if (trace.length === 0) {
return errorString; return errorString;
@ -680,6 +694,7 @@ module.exports = {
// This is exported only to facilitate testing. // This is exported only to facilitate testing.
E, E,
prepareStackTrace, prepareStackTrace,
overrideStackTrace,
kEnhanceStackBeforeInspector, kEnhanceStackBeforeInspector,
fatalExceptionStackEnhancers fatalExceptionStackEnhancers
}; };

View File

@ -7,7 +7,8 @@ const {
ERR_NO_CRYPTO, ERR_NO_CRYPTO,
ERR_UNKNOWN_SIGNAL ERR_UNKNOWN_SIGNAL
}, },
uvErrmapGet uvErrmapGet,
overrideStackTrace,
} = require('internal/errors'); } = require('internal/errors');
const { signals } = internalBinding('constants').os; const { signals } = internalBinding('constants').os;
const { const {
@ -338,15 +339,13 @@ function isInsideNodeModules() {
// side-effect-free. Since this is currently only used for a deprecated API, // side-effect-free. Since this is currently only used for a deprecated API,
// the perf implications should be okay. // the perf implications should be okay.
getStructuredStack = runInNewContext(`(function() { getStructuredStack = runInNewContext(`(function() {
Error.prepareStackTrace = function(err, trace) {
return trace;
};
Error.stackTraceLimit = Infinity; Error.stackTraceLimit = Infinity;
return function structuredStack() { return function structuredStack() {
return new Error().stack; const e = new Error();
overrideStackTrace.set(e, (err, trace) => trace);
return e.stack;
}; };
})()`, {}, { filename: 'structured-stack' }); })()`, { overrideStackTrace }, { filename: 'structured-stack' });
} }
const stack = getStructuredStack(); const stack = getStructuredStack();

View File

@ -69,12 +69,15 @@ const CJSModule = require('internal/modules/cjs/loader').Module;
const domain = require('domain'); const domain = require('domain');
const debug = require('internal/util/debuglog').debuglog('repl'); const debug = require('internal/util/debuglog').debuglog('repl');
const { const {
ERR_CANNOT_WATCH_SIGINT, codes: {
ERR_INVALID_ARG_TYPE, ERR_CANNOT_WATCH_SIGINT,
ERR_INVALID_REPL_EVAL_CONFIG, ERR_INVALID_ARG_TYPE,
ERR_INVALID_REPL_INPUT, ERR_INVALID_REPL_EVAL_CONFIG,
ERR_SCRIPT_EXECUTION_INTERRUPTED ERR_INVALID_REPL_INPUT,
} = require('internal/errors').codes; ERR_SCRIPT_EXECUTION_INTERRUPTED,
},
overrideStackTrace,
} = require('internal/errors');
const { sendInspectorCommand } = require('internal/util/inspector'); const { sendInspectorCommand } = require('internal/util/inspector');
const experimentalREPLAwait = require('internal/options').getOptionValue( const experimentalREPLAwait = require('internal/options').getOptionValue(
'--experimental-repl-await' '--experimental-repl-await'
@ -473,10 +476,29 @@ function REPLServer(prompt,
let errStack = ''; let errStack = '';
if (typeof e === 'object' && e !== null) { if (typeof e === 'object' && e !== null) {
const pstrace = Error.prepareStackTrace; overrideStackTrace.set(e, (error, stackFrames) => {
Error.prepareStackTrace = prepareStackTrace(pstrace); let frames;
if (typeof stackFrames === 'object') {
// Search from the bottom of the call stack to
// find the first frame with a null function name
const idx = stackFrames
.reverse()
.findIndex((frame) => frame.getFunctionName() === null);
// If found, get rid of it and everything below it
frames = stackFrames.splice(idx + 1);
} 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);
}
frames.push(error);
return frames.reverse().join('\n at ');
});
decorateErrorStack(e); decorateErrorStack(e);
Error.prepareStackTrace = pstrace;
if (e.domainThrown) { if (e.domainThrown) {
delete e.domain; delete e.domain;
@ -590,30 +612,6 @@ function REPLServer(prompt,
} }
} }
function filterInternalStackFrames(structuredStack) {
// Search from the bottom of the call stack to
// find the first frame with a null function name
if (typeof structuredStack !== 'object')
return structuredStack;
const idx = structuredStack.reverse().findIndex(
(frame) => frame.getFunctionName() === null);
// If found, get rid of it and everything below it
structuredStack = structuredStack.splice(idx + 1);
return structuredStack;
}
function prepareStackTrace(fn) {
return (error, stackFrames) => {
const frames = filterInternalStackFrames(stackFrames);
if (fn) {
return fn(error, frames);
}
frames.push(error);
return frames.reverse().join('\n at ');
};
}
function _parseREPLKeyword(keyword, rest) { function _parseREPLKeyword(keyword, rest) {
const cmd = this.commands[keyword]; const cmd = this.commands[keyword];
if (cmd) { if (cmd) {