Skip to content

Commit

Permalink
lib: improve error creation performance
Browse files Browse the repository at this point in the history
In case of an error where we only care about a cleaned up stack
trace it is cheaper to reset the stack trace limit for the error
that is created. That way the stack frames do not have to be
computed twice.

PR-URL: #24747
Reviewed-By: Anna Henningsen <anna@addaleax.net>
Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
  • Loading branch information
BridgeAR authored and Trott committed Dec 9, 2018
1 parent c3dd0d0 commit a1a5c04
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 6 deletions.
12 changes: 12 additions & 0 deletions lib/_http_outgoing.js
Original file line number Diff line number Diff line change
Expand Up @@ -446,20 +446,32 @@ function matchHeader(self, state, field, value) {

function validateHeaderName(name) {
if (typeof name !== 'string' || !name || !checkIsHttpToken(name)) {
// Reducing the limit improves the performance significantly. We do not
// lose the stack frames due to the `captureStackTrace()` function that is
// called later.
const tmpLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
const err = new ERR_INVALID_HTTP_TOKEN('Header name', name);
Error.stackTraceLimit = tmpLimit;
Error.captureStackTrace(err, validateHeaderName);
throw err;
}
}

function validateHeaderValue(name, value) {
let err;
// Reducing the limit improves the performance significantly. We do not loose
// the stack frames due to the `captureStackTrace()` function that is called
// later.
const tmpLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
if (value === undefined) {
err = new ERR_HTTP_INVALID_HEADER_VALUE(value, name);
} else if (checkInvalidHeaderChar(value)) {
debug('Header "%s" contains invalid characters', name);
err = new ERR_INVALID_CHAR('header content', name);
}
Error.stackTraceLimit = tmpLimit;
if (err !== undefined) {
Error.captureStackTrace(err, validateHeaderValue);
throw err;
Expand Down
24 changes: 24 additions & 0 deletions lib/internal/errors.js
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,16 @@ function uvException(ctx) {
message += ` -> '${dest}'`;
}

// Reducing the limit improves the performance significantly. We do not loose
// the stack frames due to the `captureStackTrace()` function that is called
// later.
const tmpLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
// Pass the message to the constructor instead of setting it on the object
// to make sure it is the same as the one created in C++
// eslint-disable-next-line no-restricted-syntax
const err = new Error(message);
Error.stackTraceLimit = tmpLimit;

for (const prop of Object.keys(ctx)) {
if (prop === 'message' || prop === 'path' || prop === 'dest') {
Expand Down Expand Up @@ -307,8 +313,14 @@ function uvExceptionWithHostPort(err, syscall, address, port) {
details = ` ${address}`;
}

// Reducing the limit improves the performance significantly. We do not loose
// the stack frames due to the `captureStackTrace()` function that is called
// later.
const tmpLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
// eslint-disable-next-line no-restricted-syntax
const ex = new Error(`${message}${details}`);
Error.stackTraceLimit = tmpLimit;
ex.code = code;
ex.errno = code;
ex.syscall = syscall;
Expand Down Expand Up @@ -377,9 +389,15 @@ function exceptionWithHostPort(err, syscall, address, port, additional) {
details += ` - Local (${additional})`;
}

// Reducing the limit improves the performance significantly. We do not loose
// the stack frames due to the `captureStackTrace()` function that is called
// later.
const tmpLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
// eslint-disable-next-line no-restricted-syntax
const ex = new Error(`${syscall} ${code}${details}`);
// TODO(joyeecheung): errno is supposed to err, like in uvException
Error.stackTraceLimit = tmpLimit;
ex.code = ex.errno = code;
ex.syscall = syscall;
ex.address = address;
Expand Down Expand Up @@ -410,9 +428,15 @@ function dnsException(code, syscall, hostname) {
}
}
const message = `${syscall} ${code}${hostname ? ` ${hostname}` : ''}`;
// Reducing the limit improves the performance significantly. We do not loose
// the stack frames due to the `captureStackTrace()` function that is called
// later.
const tmpLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
// eslint-disable-next-line no-restricted-syntax
const ex = new Error(message);
// TODO(joyeecheung): errno is supposed to be a number / err, like in
Error.stackTraceLimit = tmpLimit;
// uvException.
ex.errno = code;
ex.code = code;
Expand Down
16 changes: 10 additions & 6 deletions lib/internal/fs/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -190,23 +190,27 @@ function nullCheck(path, propName, throwError = true) {
const pathIsUint8Array = isUint8Array(path);

// We can only perform meaningful checks on strings and Uint8Arrays.
if (!pathIsString && !pathIsUint8Array) {
if (!pathIsString && !pathIsUint8Array ||
pathIsString && path.indexOf('\u0000') === -1 ||
pathIsUint8Array && path.indexOf(0) === -1) {
return;
}

if (pathIsString && path.indexOf('\u0000') === -1) {
return;
} else if (pathIsUint8Array && path.indexOf(0) === -1) {
return;
// Reducing the limit improves the performance significantly. We do not loose
// the stack frames due to the `captureStackTrace()` function that is called
// later.
const tmpLimit = Error.stackTraceLimit;
if (throwError) {
Error.stackTraceLimit = 0;
}

const err = new ERR_INVALID_ARG_VALUE(
propName,
path,
'must be a string or Uint8Array without null bytes'
);

if (throwError) {
Error.stackTraceLimit = tmpLimit;
Error.captureStackTrace(err, nullCheck);
throw err;
}
Expand Down
5 changes: 5 additions & 0 deletions lib/internal/process/warning.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,13 @@ function setupProcessWarnings() {
throw new ERR_INVALID_ARG_TYPE('code', 'string', code);
}
if (typeof warning === 'string') {
// Improve error creation performance by skipping the error frames.
// They are added in the `captureStackTrace()` function below.
const tmpStackLimit = Error.stackTraceLimit;
Error.stackTraceLimit = 0;
// eslint-disable-next-line no-restricted-syntax
warning = new Error(warning);
Error.stackTraceLimit = tmpStackLimit;
warning.name = String(type || 'Warning');
if (code !== undefined) warning.code = code;
if (detail !== undefined) warning.detail = detail;
Expand Down

0 comments on commit a1a5c04

Please sign in to comment.