Skip to content

Commit 37685b5

Browse files
committed
errors: improve hideStackFrames
1 parent 95b8f5d commit 37685b5

22 files changed

+599
-249
lines changed
+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const assert = require('assert');
5+
6+
const bench = common.createBenchmark(main, {
7+
type: [
8+
'hide-stackframes',
9+
'direct-call',
10+
],
11+
n: [1e7],
12+
}, {
13+
flags: ['--expose-internals'],
14+
});
15+
16+
function main({ n, type }) {
17+
const {
18+
hideStackFrames,
19+
codes: {
20+
ERR_INVALID_ARG_TYPE,
21+
},
22+
} = require('internal/errors');
23+
24+
const testfn = (value) => {
25+
if (typeof value !== 'number') {
26+
throw new ERR_INVALID_ARG_TYPE('Benchmark', 'number', value);
27+
}
28+
};
29+
30+
const hideStackFramesTestfn = hideStackFrames((value) => {
31+
if (typeof value !== 'number') {
32+
throw new ERR_INVALID_ARG_TYPE.HideStackFrameError('Benchmark', 'number', value);
33+
}
34+
});
35+
36+
const fn = type === 'hide-stackframe' ? hideStackFramesTestfn : testfn;
37+
38+
const value = 42;
39+
40+
const length = 1024;
41+
const array = [];
42+
const errCase = false;
43+
44+
for (let i = 0; i < length; ++i) {
45+
array.push(fn(value));
46+
}
47+
48+
bench.start();
49+
50+
for (let i = 0; i < n; i++) {
51+
const index = i % length;
52+
array[index] = fn(value);
53+
}
54+
55+
bench.end(n);
56+
57+
// Verify the entries to prevent dead code elimination from making
58+
// the benchmark invalid.
59+
for (let i = 0; i < length; ++i) {
60+
assert.strictEqual(typeof array[i], errCase ? 'object' : 'undefined');
61+
}
62+
}
+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
'use strict';
2+
3+
const common = require('../common.js');
4+
const assert = require('assert');
5+
6+
const bench = common.createBenchmark(main, {
7+
type: [
8+
'hide-stackframes',
9+
'direct-call',
10+
],
11+
double: ['true', 'false'],
12+
n: [1e5],
13+
}, {
14+
flags: ['--expose-internals'],
15+
});
16+
17+
function main({ n, type, double }) {
18+
const {
19+
hideStackFrames,
20+
codes: {
21+
ERR_INVALID_ARG_TYPE,
22+
},
23+
} = require('internal/errors');
24+
25+
const value = 'err';
26+
27+
const testfn = (value) => {
28+
if (typeof value !== 'number') {
29+
throw new ERR_INVALID_ARG_TYPE('Benchmark', 'number', value);
30+
}
31+
};
32+
33+
const hideStackFramesTestfn = hideStackFrames((value) => {
34+
if (typeof value !== 'number') {
35+
throw new ERR_INVALID_ARG_TYPE.HideStackFrameError('Benchmark', 'number', value);
36+
}
37+
});
38+
39+
function doubleTestfn(value) {
40+
testfn(value);
41+
}
42+
43+
const doubleHideStackFramesTestfn = hideStackFrames((value) => {
44+
hideStackFramesTestfn.withoutStackTrace(value);
45+
});
46+
47+
const fn = type === 'hide-stackframe' ?
48+
double === 'true' ?
49+
doubleHideStackFramesTestfn :
50+
hideStackFramesTestfn :
51+
double === 'true' ?
52+
doubleTestfn :
53+
testfn;
54+
55+
const length = 1024;
56+
const array = [];
57+
let errCase = false;
58+
59+
// Warm up.
60+
for (let i = 0; i < length; ++i) {
61+
try {
62+
fn(value);
63+
} catch (e) {
64+
errCase = true;
65+
array.push(e);
66+
}
67+
}
68+
69+
bench.start();
70+
71+
for (let i = 0; i < n; i++) {
72+
const index = i % length;
73+
try {
74+
fn(value);
75+
} catch (e) {
76+
array[index] = e;
77+
}
78+
}
79+
80+
bench.end(n);
81+
82+
// Verify the entries to prevent dead code elimination from making
83+
// the benchmark invalid.
84+
for (let i = 0; i < length; ++i) {
85+
assert.strictEqual(typeof array[i], errCase ? 'object' : 'undefined');
86+
}
87+
}

benchmark/error/hidestackframes.js

-45
This file was deleted.

lib/.eslintrc.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ rules:
1919
- selector: ThrowStatement > CallExpression[callee.name=/Error$/]
2020
message: Use new keyword when throwing an Error.
2121
# Config specific to lib
22-
- selector: NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError)$/])
22+
- selector: NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError)$/])
2323
message: Use an error exported by the internal/errors module.
2424
- selector: CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']
2525
message: Please use `require('internal/errors').hideStackFrames()` instead.

lib/_http_outgoing.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -619,17 +619,17 @@ function matchHeader(self, state, field, value) {
619619

620620
const validateHeaderName = hideStackFrames((name, label) => {
621621
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) {
622-
throw new ERR_INVALID_HTTP_TOKEN(label || 'Header name', name);
622+
throw new ERR_INVALID_HTTP_TOKEN.HideStackFramesError(label || 'Header name', name);
623623
}
624624
});
625625

626626
const validateHeaderValue = hideStackFrames((name, value) => {
627627
if (value === undefined) {
628-
throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name);
628+
throw new ERR_HTTP_INVALID_HEADER_VALUE.HideStackFramesError(value, name);
629629
}
630630
if (checkInvalidHeaderChar(value)) {
631631
debug('Header "%s" contains invalid characters', name);
632-
throw new ERR_INVALID_CHAR('header content', name);
632+
throw new ERR_INVALID_CHAR.HideStackFramesError('header content', name);
633633
}
634634
});
635635

lib/buffer.js

+6-14
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,6 @@ const {
108108
ERR_UNKNOWN_ENCODING,
109109
},
110110
genericNodeError,
111-
hideStackFrames,
112111
} = require('internal/errors');
113112
const {
114113
validateArray,
@@ -386,19 +385,12 @@ Buffer.of = of;
386385

387386
ObjectSetPrototypeOf(Buffer, Uint8Array);
388387

389-
// The 'assertSize' method will remove itself from the callstack when an error
390-
// occurs. This is done simply to keep the internal details of the
391-
// implementation from bleeding out to users.
392-
const assertSize = hideStackFrames((size) => {
393-
validateNumber(size, 'size', 0, kMaxLength);
394-
});
395-
396388
/**
397389
* Creates a new filled Buffer instance.
398390
* alloc(size[, fill[, encoding]])
399391
*/
400392
Buffer.alloc = function alloc(size, fill, encoding) {
401-
assertSize(size);
393+
validateNumber(size, 'size', 0, kMaxLength);
402394
if (fill !== undefined && fill !== 0 && size > 0) {
403395
const buf = createUnsafeBuffer(size);
404396
return _fill(buf, fill, 0, buf.length, encoding);
@@ -411,7 +403,7 @@ Buffer.alloc = function alloc(size, fill, encoding) {
411403
* instance. If `--zero-fill-buffers` is set, will zero-fill the buffer.
412404
*/
413405
Buffer.allocUnsafe = function allocUnsafe(size) {
414-
assertSize(size);
406+
validateNumber(size, 'size', 0, kMaxLength);
415407
return allocate(size);
416408
};
417409

@@ -421,15 +413,15 @@ Buffer.allocUnsafe = function allocUnsafe(size) {
421413
* If `--zero-fill-buffers` is set, will zero-fill the buffer.
422414
*/
423415
Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) {
424-
assertSize(size);
416+
validateNumber(size, 'size', 0, kMaxLength);
425417
return createUnsafeBuffer(size);
426418
};
427419

428420
// If --zero-fill-buffers command line argument is set, a zero-filled
429421
// buffer is returned.
430-
function SlowBuffer(length) {
431-
assertSize(length);
432-
return createUnsafeBuffer(length);
422+
function SlowBuffer(size) {
423+
validateNumber(size, 'size', 0, kMaxLength);
424+
return createUnsafeBuffer(size);
433425
}
434426

435427
ObjectSetPrototypeOf(SlowBuffer.prototype, Uint8ArrayPrototype);

lib/internal/crypto/hkdf.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -49,15 +49,15 @@ const {
4949
} = require('internal/errors');
5050

5151
const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
52-
validateString(hash, 'digest');
52+
validateString.withoutStackTrace(hash, 'digest');
5353
key = prepareKey(key);
54-
salt = validateByteSource(salt, 'salt');
55-
info = validateByteSource(info, 'info');
54+
salt = validateByteSource.withoutStackTrace(salt, 'salt');
55+
info = validateByteSource.withoutStackTrace(info, 'info');
5656

57-
validateInteger(length, 'length', 0, kMaxLength);
57+
validateInteger.withoutStackTrace(length, 'length', 0, kMaxLength);
5858

5959
if (info.byteLength > 1024) {
60-
throw new ERR_OUT_OF_RANGE(
60+
throw new ERR_OUT_OF_RANGE.HideStackFramesError(
6161
'info',
6262
'must not contain more than 1024 bytes',
6363
info.byteLength);

lib/internal/crypto/util.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ const getArrayBufferOrView = hideStackFrames((buffer, name, encoding) => {
116116
return Buffer.from(buffer, encoding);
117117
}
118118
if (!isArrayBufferView(buffer)) {
119-
throw new ERR_INVALID_ARG_TYPE(
119+
throw new ERR_INVALID_ARG_TYPE.HideStackFramesError(
120120
name,
121121
[
122122
'string',
@@ -403,7 +403,7 @@ const validateByteSource = hideStackFrames((val, name) => {
403403
if (isAnyArrayBuffer(val) || isArrayBufferView(val))
404404
return val;
405405

406-
throw new ERR_INVALID_ARG_TYPE(
406+
throw new ERR_INVALID_ARG_TYPE.HideStackFramesError(
407407
name,
408408
[
409409
'string',

0 commit comments

Comments
 (0)