Skip to content

Commit 33cad27

Browse files
devsnektargos
authored andcommitted
errors: remove eager stack generation for node errors
PR-URL: #39182 Reviewed-By: Guy Bedford <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent a7cd40e commit 33cad27

File tree

5 files changed

+104
-80
lines changed

5 files changed

+104
-80
lines changed

Diff for: lib/internal/errors.js

+75-67
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ const {
3333
Number,
3434
NumberIsInteger,
3535
ObjectDefineProperty,
36+
ObjectDefineProperties,
3637
ObjectIsExtensible,
3738
ObjectGetOwnPropertyDescriptor,
3839
ObjectKeys,
@@ -58,6 +59,8 @@ const {
5859
URIError,
5960
} = primordials;
6061

62+
const kIsNodeError = Symbol('kIsNodeError');
63+
6164
const isWindows = process.platform === 'win32';
6265

6366
const messages = new SafeMap();
@@ -116,7 +119,12 @@ const prepareStackTrace = (globalThis, error, trace) => {
116119
// Error: Message
117120
// at function (file)
118121
// at file
119-
const errorString = ErrorPrototypeToString(error);
122+
let errorString;
123+
if (kIsNodeError in error) {
124+
errorString = `${error.name} [${error.code}]: ${error.message}`;
125+
} else {
126+
errorString = ErrorPrototypeToString(error);
127+
}
120128
if (trace.length === 0) {
121129
return errorString;
122130
}
@@ -186,27 +194,6 @@ function lazyBuffer() {
186194
return buffer;
187195
}
188196

189-
const addCodeToName = hideStackFrames(function addCodeToName(err, name, code) {
190-
// Set the stack
191-
err = captureLargerStackTrace(err);
192-
// Add the error code to the name to include it in the stack trace.
193-
err.name = `${name} [${code}]`;
194-
// Access the stack to generate the error message including the error code
195-
// from the name.
196-
err.stack; // eslint-disable-line no-unused-expressions
197-
// Reset the name to the actual name.
198-
if (name === 'SystemError') {
199-
ObjectDefineProperty(err, 'name', {
200-
value: name,
201-
enumerable: false,
202-
writable: true,
203-
configurable: true
204-
});
205-
} else {
206-
delete err.name;
207-
}
208-
});
209-
210197
function isErrorStackTraceLimitWritable() {
211198
const desc = ObjectGetOwnPropertyDescriptor(Error, 'stackTraceLimit');
212199
if (desc === undefined) {
@@ -242,43 +229,55 @@ class SystemError extends Error {
242229
if (context.dest !== undefined)
243230
message += ` => ${context.dest}`;
244231

245-
ObjectDefineProperty(this, 'message', {
246-
value: message,
247-
enumerable: false,
248-
writable: true,
249-
configurable: true
250-
});
251-
addCodeToName(this, 'SystemError', key);
232+
captureLargerStackTrace(this);
252233

253234
this.code = key;
254235

255-
ObjectDefineProperty(this, 'info', {
256-
value: context,
257-
enumerable: true,
258-
configurable: true,
259-
writable: false
260-
});
261-
262-
ObjectDefineProperty(this, 'errno', {
263-
get() {
264-
return context.errno;
236+
ObjectDefineProperties(this, {
237+
[kIsNodeError]: {
238+
value: true,
239+
enumerable: false,
240+
writable: false,
241+
configurable: true,
265242
},
266-
set: (value) => {
267-
context.errno = value;
243+
name: {
244+
value: 'SystemError',
245+
enumerable: false,
246+
writable: true,
247+
configurable: true,
268248
},
269-
enumerable: true,
270-
configurable: true
271-
});
272-
273-
ObjectDefineProperty(this, 'syscall', {
274-
get() {
275-
return context.syscall;
249+
message: {
250+
value: message,
251+
enumerable: false,
252+
writable: true,
253+
configurable: true,
254+
},
255+
info: {
256+
value: context,
257+
enumerable: true,
258+
configurable: true,
259+
writable: false,
276260
},
277-
set: (value) => {
278-
context.syscall = value;
261+
errno: {
262+
get() {
263+
return context.errno;
264+
},
265+
set: (value) => {
266+
context.errno = value;
267+
},
268+
enumerable: true,
269+
configurable: true,
270+
},
271+
syscall: {
272+
get() {
273+
return context.syscall;
274+
},
275+
set: (value) => {
276+
context.syscall = value;
277+
},
278+
enumerable: true,
279+
configurable: true,
279280
},
280-
enumerable: true,
281-
configurable: true
282281
});
283282

284283
if (context.path !== undefined) {
@@ -346,21 +345,29 @@ function makeNodeErrorWithCode(Base, key) {
346345
// Reset the limit and setting the name property.
347346
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = limit;
348347
const message = getMessage(key, args, error);
349-
ObjectDefineProperty(error, 'message', {
350-
value: message,
351-
enumerable: false,
352-
writable: true,
353-
configurable: true,
354-
});
355-
ObjectDefineProperty(error, 'toString', {
356-
value() {
357-
return `${this.name} [${key}]: ${this.message}`;
348+
ObjectDefineProperties(error, {
349+
[kIsNodeError]: {
350+
value: true,
351+
enumerable: false,
352+
writable: false,
353+
configurable: true,
354+
},
355+
message: {
356+
value: message,
357+
enumerable: false,
358+
writable: true,
359+
configurable: true,
360+
},
361+
toString: {
362+
value() {
363+
return `${this.name} [${key}]: ${this.message}`;
364+
},
365+
enumerable: false,
366+
writable: true,
367+
configurable: true,
358368
},
359-
enumerable: false,
360-
writable: true,
361-
configurable: true,
362369
});
363-
addCodeToName(error, Base.name, key);
370+
captureLargerStackTrace(error);
364371
error.code = key;
365372
return error;
366373
};
@@ -792,7 +799,6 @@ class AbortError extends Error {
792799
}
793800
}
794801
module.exports = {
795-
addCodeToName, // Exported for NghttpError
796802
aggregateTwoErrors,
797803
codes,
798804
dnsException,
@@ -815,7 +821,9 @@ module.exports = {
815821
maybeOverridePrepareStackTrace,
816822
overrideStackTrace,
817823
kEnhanceStackBeforeInspector,
818-
fatalExceptionStackEnhancers
824+
fatalExceptionStackEnhancers,
825+
kIsNodeError,
826+
captureLargerStackTrace,
819827
};
820828

821829
// To declare an error message, use the E(sym, val, def) function above. The sym

Diff for: lib/internal/http2/util.js

+11-3
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ const {
99
MathMax,
1010
Number,
1111
ObjectCreate,
12+
ObjectDefineProperty,
1213
ObjectKeys,
1314
SafeSet,
1415
String,
@@ -28,9 +29,10 @@ const {
2829
ERR_INVALID_ARG_TYPE,
2930
ERR_INVALID_HTTP_TOKEN
3031
},
31-
addCodeToName,
32+
captureLargerStackTrace,
3233
getMessage,
33-
hideStackFrames
34+
hideStackFrames,
35+
kIsNodeError,
3436
} = require('internal/errors');
3537

3638
const kSensitiveHeaders = Symbol('nodejs.http2.sensitiveHeaders');
@@ -550,7 +552,13 @@ class NghttpError extends Error {
550552
binding.nghttp2ErrorString(integerCode));
551553
this.code = customErrorCode || 'ERR_HTTP2_ERROR';
552554
this.errno = integerCode;
553-
addCodeToName(this, super.name, this.code);
555+
captureLargerStackTrace(this);
556+
ObjectDefineProperty(this, kIsNodeError, {
557+
value: true,
558+
enumerable: false,
559+
writable: false,
560+
configurable: true,
561+
});
554562
}
555563

556564
toString() {

Diff for: lib/internal/source_map/prepare_stack_trace.js

+8-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ const { findSourceMap } = require('internal/source_map/source_map_cache');
2121
const {
2222
kNoOverride,
2323
overrideStackTrace,
24-
maybeOverridePrepareStackTrace
24+
maybeOverridePrepareStackTrace,
25+
kIsNodeError,
2526
} = require('internal/errors');
2627
const { fileURLToPath } = require('internal/url');
2728

@@ -41,7 +42,12 @@ const prepareStackTrace = (globalThis, error, trace) => {
4142
maybeOverridePrepareStackTrace(globalThis, error, trace);
4243
if (globalOverride !== kNoOverride) return globalOverride;
4344

44-
const errorString = ErrorPrototypeToString(error);
45+
let errorString;
46+
if (kIsNodeError in error) {
47+
errorString = `${error.name} [${error.code}]: ${error.message}`;
48+
} else {
49+
errorString = ErrorPrototypeToString(error);
50+
}
4551

4652
if (trace.length === 0) {
4753
return errorString;

Diff for: test/message/esm_loader_not_found.out

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
(node:*) ExperimentalWarning: --experimental-loader is an experimental feature. This feature could change at any time
22
(Use `* --trace-warnings ...` to show where the warning was created)
3-
node:internal/process/esm_loader:*
4-
internalBinding('errors').triggerUncaughtException(
5-
^
3+
node:internal/errors:*
4+
ErrorCaptureStackTrace(err);
5+
^
66
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'i-dont-exist' imported from *
77
at new NodeError (node:internal/errors:*:*)
88
at packageResolve (node:internal/modules/esm/resolve:*:*)

Diff for: test/parallel/test-repl-top-level-await.js

+7-5
Original file line numberDiff line numberDiff line change
@@ -175,15 +175,17 @@ async function ordinaryTests() {
175175

176176
async function ctrlCTest() {
177177
console.log('Testing Ctrl+C');
178-
assert.deepStrictEqual(await runAndWait([
178+
const output = await runAndWait([
179179
'await new Promise(() => {})',
180180
{ ctrl: true, name: 'c' },
181-
]), [
181+
]);
182+
assert.deepStrictEqual(output.slice(0, 3), [
182183
'await new Promise(() => {})\r',
183184
'Uncaught:',
184-
'[Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' +
185-
'Script execution was interrupted by `SIGINT`] {',
186-
" code: 'ERR_SCRIPT_EXECUTION_INTERRUPTED'",
185+
'Error [ERR_SCRIPT_EXECUTION_INTERRUPTED]: ' +
186+
'Script execution was interrupted by `SIGINT`',
187+
]);
188+
assert.deepStrictEqual(output.slice(-2), [
187189
'}',
188190
PROMPT,
189191
]);

0 commit comments

Comments
 (0)