Skip to content

Commit

Permalink
errors: improve hideStackFrames
Browse files Browse the repository at this point in the history
  • Loading branch information
Uzlopak committed Oct 8, 2023
1 parent 95b8f5d commit 37685b5
Show file tree
Hide file tree
Showing 22 changed files with 599 additions and 249 deletions.
62 changes: 62 additions & 0 deletions benchmark/error/hidestackframes-noerr.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
'use strict';

const common = require('../common.js');
const assert = require('assert');

const bench = common.createBenchmark(main, {
type: [
'hide-stackframes',
'direct-call',
],
n: [1e7],
}, {
flags: ['--expose-internals'],
});

function main({ n, type }) {
const {
hideStackFrames,
codes: {
ERR_INVALID_ARG_TYPE,
},
} = require('internal/errors');

const testfn = (value) => {
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE('Benchmark', 'number', value);
}
};

const hideStackFramesTestfn = hideStackFrames((value) => {
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE.HideStackFrameError('Benchmark', 'number', value);
}
});

const fn = type === 'hide-stackframe' ? hideStackFramesTestfn : testfn;

const value = 42;

const length = 1024;
const array = [];
const errCase = false;

for (let i = 0; i < length; ++i) {
array.push(fn(value));
}

bench.start();

for (let i = 0; i < n; i++) {
const index = i % length;
array[index] = fn(value);
}

bench.end(n);

// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], errCase ? 'object' : 'undefined');
}
}
87 changes: 87 additions & 0 deletions benchmark/error/hidestackframes-throw.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use strict';

const common = require('../common.js');
const assert = require('assert');

const bench = common.createBenchmark(main, {
type: [
'hide-stackframes',
'direct-call',
],
double: ['true', 'false'],
n: [1e5],
}, {
flags: ['--expose-internals'],
});

function main({ n, type, double }) {
const {
hideStackFrames,
codes: {
ERR_INVALID_ARG_TYPE,
},
} = require('internal/errors');

const value = 'err';

const testfn = (value) => {
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE('Benchmark', 'number', value);
}
};

const hideStackFramesTestfn = hideStackFrames((value) => {
if (typeof value !== 'number') {
throw new ERR_INVALID_ARG_TYPE.HideStackFrameError('Benchmark', 'number', value);
}
});

function doubleTestfn(value) {
testfn(value);
}

const doubleHideStackFramesTestfn = hideStackFrames((value) => {
hideStackFramesTestfn.withoutStackTrace(value);
});

const fn = type === 'hide-stackframe' ?
double === 'true' ?
doubleHideStackFramesTestfn :
hideStackFramesTestfn :
double === 'true' ?
doubleTestfn :
testfn;

const length = 1024;
const array = [];
let errCase = false;

// Warm up.
for (let i = 0; i < length; ++i) {
try {
fn(value);
} catch (e) {
errCase = true;
array.push(e);
}
}

bench.start();

for (let i = 0; i < n; i++) {
const index = i % length;
try {
fn(value);
} catch (e) {
array[index] = e;
}
}

bench.end(n);

// Verify the entries to prevent dead code elimination from making
// the benchmark invalid.
for (let i = 0; i < length; ++i) {
assert.strictEqual(typeof array[i], errCase ? 'object' : 'undefined');
}
}
45 changes: 0 additions & 45 deletions benchmark/error/hidestackframes.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/.eslintrc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ rules:
- selector: ThrowStatement > CallExpression[callee.name=/Error$/]
message: Use new keyword when throwing an Error.
# Config specific to lib
- selector: NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError)$/])
- selector: NewExpression[callee.name=/Error$/]:not([callee.name=/^(AssertionError|NghttpError|AbortError|NodeAggregateError)$/])
message: Use an error exported by the internal/errors module.
- selector: CallExpression[callee.object.name='Error'][callee.property.name='captureStackTrace']
message: Please use `require('internal/errors').hideStackFrames()` instead.
Expand Down
6 changes: 3 additions & 3 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -619,17 +619,17 @@ function matchHeader(self, state, field, value) {

const validateHeaderName = hideStackFrames((name, label) => {
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) {
throw new ERR_INVALID_HTTP_TOKEN(label || 'Header name', name);
throw new ERR_INVALID_HTTP_TOKEN.HideStackFramesError(label || 'Header name', name);
}
});

const validateHeaderValue = hideStackFrames((name, value) => {
if (value === undefined) {
throw new ERR_HTTP_INVALID_HEADER_VALUE(value, name);
throw new ERR_HTTP_INVALID_HEADER_VALUE.HideStackFramesError(value, name);
}
if (checkInvalidHeaderChar(value)) {
debug('Header "%s" contains invalid characters', name);
throw new ERR_INVALID_CHAR('header content', name);
throw new ERR_INVALID_CHAR.HideStackFramesError('header content', name);
}
});

Expand Down
20 changes: 6 additions & 14 deletions lib/buffer.js
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ const {
ERR_UNKNOWN_ENCODING,
},
genericNodeError,
hideStackFrames,
} = require('internal/errors');
const {
validateArray,
Expand Down Expand Up @@ -386,19 +385,12 @@ Buffer.of = of;

ObjectSetPrototypeOf(Buffer, Uint8Array);

// The 'assertSize' method will remove itself from the callstack when an error
// occurs. This is done simply to keep the internal details of the
// implementation from bleeding out to users.
const assertSize = hideStackFrames((size) => {
validateNumber(size, 'size', 0, kMaxLength);
});

/**
* Creates a new filled Buffer instance.
* alloc(size[, fill[, encoding]])
*/
Buffer.alloc = function alloc(size, fill, encoding) {
assertSize(size);
validateNumber(size, 'size', 0, kMaxLength);
if (fill !== undefined && fill !== 0 && size > 0) {
const buf = createUnsafeBuffer(size);
return _fill(buf, fill, 0, buf.length, encoding);
Expand All @@ -411,7 +403,7 @@ Buffer.alloc = function alloc(size, fill, encoding) {
* instance. If `--zero-fill-buffers` is set, will zero-fill the buffer.
*/
Buffer.allocUnsafe = function allocUnsafe(size) {
assertSize(size);
validateNumber(size, 'size', 0, kMaxLength);
return allocate(size);
};

Expand All @@ -421,15 +413,15 @@ Buffer.allocUnsafe = function allocUnsafe(size) {
* If `--zero-fill-buffers` is set, will zero-fill the buffer.
*/
Buffer.allocUnsafeSlow = function allocUnsafeSlow(size) {
assertSize(size);
validateNumber(size, 'size', 0, kMaxLength);
return createUnsafeBuffer(size);
};

// If --zero-fill-buffers command line argument is set, a zero-filled
// buffer is returned.
function SlowBuffer(length) {
assertSize(length);
return createUnsafeBuffer(length);
function SlowBuffer(size) {
validateNumber(size, 'size', 0, kMaxLength);
return createUnsafeBuffer(size);
}

ObjectSetPrototypeOf(SlowBuffer.prototype, Uint8ArrayPrototype);
Expand Down
10 changes: 5 additions & 5 deletions lib/internal/crypto/hkdf.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ const {
} = require('internal/errors');

const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
validateString(hash, 'digest');
validateString.withoutStackTrace(hash, 'digest');
key = prepareKey(key);
salt = validateByteSource(salt, 'salt');
info = validateByteSource(info, 'info');
salt = validateByteSource.withoutStackTrace(salt, 'salt');
info = validateByteSource.withoutStackTrace(info, 'info');

validateInteger(length, 'length', 0, kMaxLength);
validateInteger.withoutStackTrace(length, 'length', 0, kMaxLength);

if (info.byteLength > 1024) {
throw new ERR_OUT_OF_RANGE(
throw new ERR_OUT_OF_RANGE.HideStackFramesError(
'info',
'must not contain more than 1024 bytes',
info.byteLength);
Expand Down
4 changes: 2 additions & 2 deletions lib/internal/crypto/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ const getArrayBufferOrView = hideStackFrames((buffer, name, encoding) => {
return Buffer.from(buffer, encoding);
}
if (!isArrayBufferView(buffer)) {
throw new ERR_INVALID_ARG_TYPE(
throw new ERR_INVALID_ARG_TYPE.HideStackFramesError(
name,
[
'string',
Expand Down Expand Up @@ -403,7 +403,7 @@ const validateByteSource = hideStackFrames((val, name) => {
if (isAnyArrayBuffer(val) || isArrayBufferView(val))
return val;

throw new ERR_INVALID_ARG_TYPE(
throw new ERR_INVALID_ARG_TYPE.HideStackFramesError(
name,
[
'string',
Expand Down
Loading

0 comments on commit 37685b5

Please sign in to comment.