Skip to content

Commit bb7d7f3

Browse files
Uzlopakrichardlau
authored andcommitted
errors: fix stacktrace of SystemError
PR-URL: #49956 Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 2ce8b97 commit bb7d7f3

File tree

2 files changed

+344
-5
lines changed

2 files changed

+344
-5
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const assert = require('assert');
5+
6+
const bench = common.createBenchmark(main, {
7+
n: [1e6],
8+
code: [
9+
'built-in',
10+
'ERR_FS_CP_DIR_TO_NON_DIR',
11+
],
12+
stackTraceLimit: [0, 10],
13+
}, {
14+
flags: ['--expose-internals'],
15+
});
16+
17+
function getErrorFactory(code) {
18+
const {
19+
ERR_FS_CP_DIR_TO_NON_DIR,
20+
} = require('internal/errors').codes;
21+
22+
switch (code) {
23+
case 'built-in':
24+
return (n) => new Error();
25+
case 'ERR_FS_CP_DIR_TO_NON_DIR':
26+
return (n) => new ERR_FS_CP_DIR_TO_NON_DIR({
27+
message: 'cannot overwrite directory',
28+
path: 'dest',
29+
syscall: 'cp',
30+
errno: 21,
31+
code: 'EISDIR',
32+
});
33+
default:
34+
throw new Error(`${code} not supported`);
35+
}
36+
}
37+
38+
function main({ n, code, stackTraceLimit }) {
39+
const getError = getErrorFactory(code);
40+
41+
Error.stackTraceLimit = stackTraceLimit;
42+
43+
// Warm up.
44+
const length = 1024;
45+
const array = [];
46+
for (let i = 0; i < length; ++i) {
47+
array.push(getError(i));
48+
}
49+
50+
bench.start();
51+
52+
for (let i = 0; i < n; ++i) {
53+
const index = i % length;
54+
array[index] = getError(index);
55+
}
56+
57+
bench.end(n);
58+
59+
// Verify the entries to prevent dead code elimination from making
60+
// the benchmark invalid.
61+
for (let i = 0; i < length; ++i) {
62+
assert.strictEqual(typeof array[i], 'object');
63+
}
64+
}

test/parallel/test-errors-systemerror.js

+280-5
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33

44
require('../common');
55
const assert = require('assert');
6-
const { E, SystemError, codes } = require('internal/errors');
6+
const { E, SystemError, codes, kIsNodeError } = require('internal/errors');
7+
const { inspect } = require('internal/util/inspect');
78

89
assert.throws(
910
() => { new SystemError(); },
@@ -31,7 +32,7 @@ const { ERR_TEST } = codes;
3132
code: 'ERR_TEST',
3233
name: 'SystemError',
3334
message: 'custom message: syscall_test returned ETEST (code message)' +
34-
' /str => /str2',
35+
' /str => /str2',
3536
info: ctx
3637
}
3738
);
@@ -51,7 +52,7 @@ const { ERR_TEST } = codes;
5152
code: 'ERR_TEST',
5253
name: 'SystemError',
5354
message: 'custom message: syscall_test returned ETEST (code message)' +
54-
' /buf => /str2',
55+
' /buf => /str2',
5556
info: ctx
5657
}
5758
);
@@ -71,7 +72,7 @@ const { ERR_TEST } = codes;
7172
code: 'ERR_TEST',
7273
name: 'SystemError',
7374
message: 'custom message: syscall_test returned ETEST (code message)' +
74-
' /buf => /buf2',
75+
' /buf => /buf2',
7576
info: ctx
7677
}
7778
);
@@ -110,6 +111,11 @@ const { ERR_TEST } = codes;
110111
assert.strictEqual(err.syscall, 'test');
111112
assert.strictEqual(err.path, 'path');
112113
assert.strictEqual(err.dest, 'path');
114+
115+
assert.strictEqual(err.info.errno, 321);
116+
assert.strictEqual(err.info.dest.toString(), 'path');
117+
assert.strictEqual(err.info.path.toString(), 'path');
118+
assert.strictEqual(err.info.syscall, 'test');
113119
}
114120

115121
{
@@ -128,8 +134,277 @@ const { ERR_TEST } = codes;
128134
code: 'ERR_TEST',
129135
name: 'Foobar',
130136
message: 'custom message: syscall_test returned ERR_TEST ' +
131-
'(Error occurred)',
137+
'(Error occurred)',
132138
info: ctx
133139
}
134140
);
135141
}
142+
143+
{
144+
const ctx = {
145+
code: 'ERR',
146+
errno: 123,
147+
message: 'something happened',
148+
syscall: 'syscall_test',
149+
};
150+
const err = new ERR_TEST(ctx);
151+
152+
// is set to true
153+
assert.strictEqual(err[kIsNodeError], true);
154+
155+
// is not writable
156+
assert.throws(
157+
() => { err[kIsNodeError] = false; },
158+
{
159+
name: 'TypeError',
160+
message: /Symbol\(kIsNodeError\)/,
161+
}
162+
);
163+
164+
// is not enumerable
165+
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(err, kIsNodeError), false);
166+
167+
// is configurable
168+
delete err[kIsNodeError];
169+
assert.strictEqual(kIsNodeError in err, false);
170+
}
171+
172+
{
173+
const ctx = {
174+
code: 'ERR',
175+
errno: 123,
176+
message: 'something happened',
177+
syscall: 'syscall_test',
178+
};
179+
const err = new ERR_TEST(ctx);
180+
181+
// is set to true
182+
assert.strictEqual(err.name, 'SystemError');
183+
184+
// is writable
185+
err.name = 'CustomError';
186+
assert.strictEqual(err.name, 'CustomError');
187+
188+
// is not enumerable
189+
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(err, 'name'), false);
190+
191+
// is configurable
192+
delete err.name;
193+
assert.strictEqual(err.name, 'Error');
194+
}
195+
196+
{
197+
const ctx = {
198+
code: 'ERR',
199+
errno: 123,
200+
message: 'something happened',
201+
syscall: 'syscall_test',
202+
};
203+
const err = new ERR_TEST(ctx);
204+
205+
// Is set with the correct message
206+
assert.strictEqual(err.message, 'custom message: syscall_test returned ERR (something happened)');
207+
208+
// is writable
209+
err.message = 'custom message';
210+
assert.strictEqual(err.message, 'custom message');
211+
212+
// is not enumerable
213+
assert.strictEqual(Object.prototype.propertyIsEnumerable.call(err, 'message'), false);
214+
215+
// is configurable
216+
delete err.message;
217+
assert.strictEqual(err.message, '');
218+
}
219+
220+
{
221+
const ctx = {
222+
code: 'ERR',
223+
errno: 123,
224+
message: 'something happened',
225+
syscall: 'syscall_test',
226+
};
227+
const err = new ERR_TEST(ctx);
228+
229+
// Is set to the correct syscall
230+
assert.strictEqual(err.syscall, 'syscall_test');
231+
232+
// is writable
233+
err.syscall = 'custom syscall';
234+
assert.strictEqual(err.syscall, 'custom syscall');
235+
236+
// is enumerable
237+
assert(Object.prototype.propertyIsEnumerable.call(err, 'syscall'));
238+
239+
// is configurable
240+
delete err.syscall;
241+
assert.strictEqual('syscall' in err, false);
242+
}
243+
244+
{
245+
const ctx = {
246+
code: 'ERR',
247+
errno: 123,
248+
message: 'something happened',
249+
syscall: 'syscall_test',
250+
};
251+
const err = new ERR_TEST(ctx);
252+
253+
// Is set to the correct errno
254+
assert.strictEqual(err.errno, 123);
255+
256+
// is writable
257+
err.errno = 'custom errno';
258+
assert.strictEqual(err.errno, 'custom errno');
259+
260+
// is enumerable
261+
assert(Object.prototype.propertyIsEnumerable.call(err, 'errno'));
262+
263+
// is configurable
264+
delete err.errno;
265+
assert.strictEqual('errno' in err, false);
266+
}
267+
268+
{
269+
const ctx = {
270+
code: 'ERR',
271+
errno: 123,
272+
message: 'something happened',
273+
syscall: 'syscall_test',
274+
};
275+
const err = new ERR_TEST(ctx);
276+
277+
// Is set to the correct info
278+
assert.strictEqual(Object.keys(err.info).length, 4);
279+
assert.strictEqual(err.info.errno, 123);
280+
assert.strictEqual(err.info.code, 'ERR');
281+
assert.strictEqual(err.info.message, 'something happened');
282+
assert.strictEqual(err.info.syscall, 'syscall_test');
283+
284+
// is not writable
285+
assert.throws(
286+
() => {
287+
err.info = {
288+
...ctx,
289+
errno: 124
290+
};
291+
},
292+
{
293+
name: 'TypeError',
294+
message: /'info'/,
295+
}
296+
);
297+
298+
assert.strictEqual(Object.keys(err.info).length, 4);
299+
assert.strictEqual(err.info.errno, 123);
300+
assert.strictEqual(err.info.code, 'ERR');
301+
assert.strictEqual(err.info.message, 'something happened');
302+
assert.strictEqual(err.info.syscall, 'syscall_test');
303+
304+
// is enumerable
305+
assert(Object.prototype.propertyIsEnumerable.call(err, 'info'));
306+
307+
// is configurable
308+
delete err.info;
309+
310+
assert.strictEqual('info' in err, false);
311+
}
312+
313+
{
314+
// Make sure the stack trace does not contain SystemError
315+
try {
316+
throw new ERR_TEST({
317+
code: 'ERR',
318+
errno: 123,
319+
message: 'something happened',
320+
syscall: 'syscall_test',
321+
});
322+
} catch (e) {
323+
assert.doesNotMatch(e.stack, /at new SystemError/);
324+
assert.match(e.stack.split('\n')[1], /test-errors-systemerror\.js/);
325+
}
326+
}
327+
328+
{
329+
// Make sure the stack trace has the correct number of frames
330+
const limit = Error.stackTraceLimit;
331+
Error.stackTraceLimit = 3;
332+
function a() {
333+
b();
334+
}
335+
336+
function b() {
337+
c();
338+
}
339+
340+
function c() {
341+
throw new ERR_TEST({
342+
code: 'ERR',
343+
errno: 123,
344+
message: 'something happened',
345+
syscall: 'syscall_test',
346+
});
347+
}
348+
try {
349+
a();
350+
} catch (e) {
351+
assert.doesNotMatch(e.stack, /at new SystemError/);
352+
assert.match(e.stack.split('\n')[1], /test-errors-systemerror\.js/);
353+
assert.match(e.stack.split('\n')[1], /at c \(/);
354+
assert.match(e.stack.split('\n')[2], /test-errors-systemerror\.js/);
355+
assert.match(e.stack.split('\n')[2], /at b \(/);
356+
assert.match(e.stack.split('\n')[3], /test-errors-systemerror\.js/);
357+
assert.match(e.stack.split('\n')[3], /at a \(/);
358+
assert.strictEqual(e.stack.split('\n').length, 4);
359+
} finally {
360+
Error.stackTraceLimit = limit;
361+
}
362+
}
363+
364+
{
365+
// Inspect should return the correct string
366+
const err = new ERR_TEST({
367+
code: 'ERR',
368+
errno: 123,
369+
message: 'something happened',
370+
syscall: 'syscall_test',
371+
custom: 'custom'
372+
});
373+
let inspectedErr = inspect(err);
374+
375+
assert.ok(inspectedErr.includes(`info: {
376+
code: 'ERR',
377+
errno: 123,
378+
message: 'something happened',
379+
syscall: 'syscall_test',
380+
custom: 'custom'
381+
},`));
382+
383+
err.syscall = 'custom_syscall';
384+
385+
inspectedErr = inspect(err);
386+
387+
assert.ok(inspectedErr.includes(`info: {
388+
code: 'ERR',
389+
errno: 123,
390+
message: 'something happened',
391+
syscall: 'custom_syscall',
392+
custom: 'custom'
393+
},`));
394+
}
395+
396+
{
397+
// toString should return the correct string
398+
399+
const err = new ERR_TEST({
400+
code: 'ERR',
401+
errno: 123,
402+
message: 'something happened',
403+
syscall: 'syscall_test',
404+
});
405+
406+
assert.strictEqual(
407+
err.toString(),
408+
'SystemError [ERR_TEST]: custom message: syscall_test returned ERR (something happened)'
409+
);
410+
}

0 commit comments

Comments
 (0)