diff --git a/lib/internal/errors.js b/lib/internal/errors.js index 8b0a7a0d7aab66..a0f3f26d5d34be 100644 --- a/lib/internal/errors.js +++ b/lib/internal/errors.js @@ -43,6 +43,7 @@ const { kMaxLength } = internalBinding('buffer'); const MainContextError = Error; const ErrorToString = Error.prototype.toString; const overrideStackTrace = new WeakMap(); +const kNoOverride = Symbol('kNoOverride'); const prepareStackTrace = (globalThis, error, trace) => { // API for node internals to override error stack formatting // without interfering with userland code. @@ -52,6 +53,23 @@ const prepareStackTrace = (globalThis, error, trace) => { return f(error, trace); } + const globalOverride = + maybeOverridePrepareStackTrace(globalThis, error, trace); + if (globalOverride !== kNoOverride) return globalOverride; + + // Normal error formatting: + // + // Error: Message + // at function (file) + // at file + const errorString = ErrorToString.call(error); + if (trace.length === 0) { + return errorString; + } + return `${errorString}\n at ${trace.join('\n at ')}`; +}; + +const maybeOverridePrepareStackTrace = (globalThis, error, trace) => { // Polyfill of V8's Error.prepareStackTrace API. // https://crbug.com/v8/7848 // `globalThis` is the global that contains the constructor which @@ -66,19 +84,9 @@ const prepareStackTrace = (globalThis, error, trace) => { return MainContextError.prepareStackTrace(error, trace); } - // Normal error formatting: - // - // Error: Message - // at function (file) - // at file - const errorString = ErrorToString.call(error); - if (trace.length === 0) { - return errorString; - } - return `${errorString}\n at ${trace.join('\n at ')}`; + return kNoOverride; }; - let excludedStackFn; // Lazily loaded @@ -691,7 +699,9 @@ module.exports = { SystemError, // This is exported only to facilitate testing. E, + kNoOverride, prepareStackTrace, + maybeOverridePrepareStackTrace, overrideStackTrace, kEnhanceStackBeforeInspector, fatalExceptionStackEnhancers diff --git a/lib/internal/source_map/prepare_stack_trace.js b/lib/internal/source_map/prepare_stack_trace.js index d6c5fce60a29e3..91bedcb266be70 100644 --- a/lib/internal/source_map/prepare_stack_trace.js +++ b/lib/internal/source_map/prepare_stack_trace.js @@ -2,7 +2,11 @@ const debug = require('internal/util/debuglog').debuglog('source_map'); const { findSourceMap } = require('internal/source_map/source_map_cache'); -const { overrideStackTrace } = require('internal/errors'); +const { + kNoOverride, + overrideStackTrace, + maybeOverridePrepareStackTrace +} = require('internal/errors'); // Create a prettified stacktrace, inserting context from source maps // if possible. @@ -17,6 +21,10 @@ const prepareStackTrace = (globalThis, error, trace) => { return f(error, trace); } + const globalOverride = + maybeOverridePrepareStackTrace(globalThis, error, trace); + if (globalOverride !== kNoOverride) return globalOverride; + const { SourceMap } = require('internal/source_map/source_map'); const errorString = ErrorToString.call(error); diff --git a/test/parallel/test-error-prepare-stack-trace.js b/test/parallel/test-error-prepare-stack-trace.js new file mode 100644 index 00000000000000..2ace9c8d71491d --- /dev/null +++ b/test/parallel/test-error-prepare-stack-trace.js @@ -0,0 +1,19 @@ +// Flags: --enable-source-maps +'use strict'; + +require('../common'); +const assert = require('assert'); + +// Error.prepareStackTrace() can be overridden with source maps enabled. +{ + let prepareCalled = false; + Error.prepareStackTrace = (_error, trace) => { + prepareCalled = true; + }; + try { + throw new Error('foo'); + } catch (err) { + err.stack; + } + assert(prepareCalled); +}