Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit cc725a6

Browse filesBrowse files
authored
errors: improve performance of instantiation
PR-URL: #49654 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Stephen Belanger <admin@stephenbelanger.com> Reviewed-By: Joyee Cheung <joyeec9h3@gmail.com> Reviewed-By: Raz Luvaton <rluvaton@gmail.com>
1 parent 3838b57 commit cc725a6
Copy full SHA for cc725a6

File tree

Expand file treeCollapse file tree

18 files changed

+306
-138
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

18 files changed

+306
-138
lines changed
Open diff view settings
Collapse file
+66Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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_HTTP2_STREAM_SELF_DEPENDENCY',
11+
'ERR_INVALID_STATE',
12+
'ERR_INVALID_URL',
13+
],
14+
stackTraceLimit: [0, 10],
15+
}, {
16+
flags: ['--expose-internals'],
17+
});
18+
19+
function getErrorFactory(code) {
20+
const {
21+
ERR_HTTP2_STREAM_SELF_DEPENDENCY,
22+
ERR_INVALID_STATE,
23+
ERR_INVALID_URL,
24+
} = require('internal/errors').codes;
25+
26+
switch (code) {
27+
case 'built-in':
28+
return (n) => new Error();
29+
case 'ERR_HTTP2_STREAM_SELF_DEPENDENCY':
30+
return (n) => new ERR_HTTP2_STREAM_SELF_DEPENDENCY();
31+
case 'ERR_INVALID_STATE':
32+
return (n) => new ERR_INVALID_STATE(n + '');
33+
case 'ERR_INVALID_URL':
34+
return (n) => new ERR_INVALID_URL({ input: n + '' });
35+
default:
36+
throw new Error(`${code} not supported`);
37+
}
38+
}
39+
40+
function main({ n, code, stackTraceLimit }) {
41+
const getError = getErrorFactory(code);
42+
43+
Error.stackTraceLimit = stackTraceLimit;
44+
45+
// Warm up.
46+
const length = 1024;
47+
const array = [];
48+
for (let i = 0; i < length; ++i) {
49+
array.push(getError(i));
50+
}
51+
52+
bench.start();
53+
54+
for (let i = 0; i < n; ++i) {
55+
const index = i % length;
56+
array[index] = getError(index);
57+
}
58+
59+
bench.end(n);
60+
61+
// Verify the entries to prevent dead code elimination from making
62+
// the benchmark invalid.
63+
for (let i = 0; i < length; ++i) {
64+
assert.strictEqual(typeof array[i], 'object');
65+
}
66+
}
Collapse file
+62Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
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_HTTP2_STREAM_SELF_DEPENDENCY',
11+
'ERR_INVALID_STATE',
12+
],
13+
stackTraceLimit: [0, 10],
14+
}, {
15+
flags: ['--expose-internals'],
16+
});
17+
18+
function getErrorStackFactory(code) {
19+
const {
20+
ERR_INVALID_STATE,
21+
ERR_HTTP2_STREAM_SELF_DEPENDENCY,
22+
} = require('internal/errors').codes;
23+
24+
switch (code) {
25+
case 'built-in':
26+
return (n) => new Error().stack;
27+
case 'ERR_HTTP2_STREAM_SELF_DEPENDENCY':
28+
return (n) => new ERR_HTTP2_STREAM_SELF_DEPENDENCY().stack;
29+
case 'ERR_INVALID_STATE':
30+
return (n) => new ERR_INVALID_STATE(n + '').stack;
31+
default:
32+
throw new Error(`${code} not supported`);
33+
}
34+
}
35+
36+
function main({ n, code, stackTraceLimit }) {
37+
const getStack = getErrorStackFactory(code);
38+
39+
Error.stackTraceLimit = stackTraceLimit;
40+
41+
// Warm up.
42+
const length = 1024;
43+
const array = [];
44+
for (let i = 0; i < length; ++i) {
45+
array.push(getStack(i));
46+
}
47+
48+
bench.start();
49+
50+
for (let i = 0; i < n; ++i) {
51+
const index = i % length;
52+
array[index] = getStack(index);
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], 'string');
61+
}
62+
}
Collapse file

‎benchmark/error/node-error.js‎

Copy file name to clipboardExpand all lines: benchmark/error/node-error.js
-21Lines changed: 0 additions & 21 deletions
This file was deleted.
Collapse file

‎lib/internal/crypto/hkdf.js‎

Copy file name to clipboardExpand all lines: lib/internal/crypto/hkdf.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ const validateParameters = hideStackFrames((hash, key, salt, info, length) => {
5757
validateInteger(length, 'length', 0, kMaxLength);
5858

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

‎lib/internal/errors.js‎

Copy file name to clipboardExpand all lines: lib/internal/errors.js
+107-43Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,10 @@ const aggregateErrors = hideStackFrames((errors, message, code) => {
175175
return err;
176176
});
177177

178+
const assert = require('internal/assert');
179+
178180
// Lazily loaded
179181
let util;
180-
let assert;
181182

182183
let internalUtil = null;
183184
function lazyInternalUtil() {
@@ -371,42 +372,103 @@ function makeSystemErrorWithCode(key) {
371372
}
372373

373374
function makeNodeErrorWithCode(Base, key) {
374-
return function NodeError(...args) {
375-
const limit = Error.stackTraceLimit;
376-
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = 0;
377-
const error = new Base();
378-
// Reset the limit and setting the name property.
379-
if (isErrorStackTraceLimitWritable()) Error.stackTraceLimit = limit;
380-
const message = getMessage(key, args, error);
381-
ObjectDefineProperties(error, {
382-
[kIsNodeError]: {
383-
__proto__: null,
384-
value: true,
385-
enumerable: false,
386-
writable: false,
387-
configurable: true,
388-
},
389-
message: {
390-
__proto__: null,
391-
value: message,
392-
enumerable: false,
393-
writable: true,
394-
configurable: true,
395-
},
396-
toString: {
397-
__proto__: null,
398-
value() {
375+
const msg = messages.get(key);
376+
const expectedLength = typeof msg !== 'string' ? -1 : getExpectedArgumentLength(msg);
377+
378+
switch (expectedLength) {
379+
case 0: {
380+
class NodeError extends Base {
381+
code = key;
382+
383+
constructor(...args) {
384+
assert(
385+
args.length === 0,
386+
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
387+
`match the required ones (${expectedLength}).`,
388+
);
389+
super(msg);
390+
}
391+
392+
// This is a workaround for wpt tests that expect that the error
393+
// constructor has a `name` property of the base class.
394+
get ['constructor']() {
395+
return Base;
396+
}
397+
398+
get [kIsNodeError]() {
399+
return true;
400+
}
401+
402+
toString() {
399403
return `${this.name} [${key}]: ${this.message}`;
400-
},
401-
enumerable: false,
402-
writable: true,
403-
configurable: true,
404-
},
405-
});
406-
captureLargerStackTrace(error);
407-
error.code = key;
408-
return error;
409-
};
404+
}
405+
}
406+
return NodeError;
407+
}
408+
case -1: {
409+
class NodeError extends Base {
410+
code = key;
411+
412+
constructor(...args) {
413+
super();
414+
ObjectDefineProperty(this, 'message', {
415+
__proto__: null,
416+
value: getMessage(key, args, this),
417+
enumerable: false,
418+
writable: true,
419+
configurable: true,
420+
});
421+
}
422+
423+
// This is a workaround for wpt tests that expect that the error
424+
// constructor has a `name` property of the base class.
425+
get ['constructor']() {
426+
return Base;
427+
}
428+
429+
get [kIsNodeError]() {
430+
return true;
431+
}
432+
433+
toString() {
434+
return `${this.name} [${key}]: ${this.message}`;
435+
}
436+
}
437+
return NodeError;
438+
}
439+
default: {
440+
441+
class NodeError extends Base {
442+
code = key;
443+
444+
constructor(...args) {
445+
assert(
446+
args.length === expectedLength,
447+
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
448+
`match the required ones (${expectedLength}).`,
449+
);
450+
451+
ArrayPrototypeUnshift(args, msg);
452+
super(ReflectApply(lazyInternalUtilInspect().format, null, args));
453+
}
454+
455+
// This is a workaround for wpt tests that expect that the error
456+
// constructor has a `name` property of the base class.
457+
get ['constructor']() {
458+
return Base;
459+
}
460+
461+
get [kIsNodeError]() {
462+
return true;
463+
}
464+
465+
toString() {
466+
return `${this.name} [${key}]: ${this.message}`;
467+
}
468+
}
469+
return NodeError;
470+
}
471+
}
410472
}
411473

412474
/**
@@ -443,11 +505,16 @@ function E(sym, val, def, ...otherClasses) {
443505
codes[sym] = def;
444506
}
445507

508+
function getExpectedArgumentLength(msg) {
509+
let expectedLength = 0;
510+
const regex = /%[dfijoOs]/g;
511+
while (RegExpPrototypeExec(regex, msg) !== null) expectedLength++;
512+
return expectedLength;
513+
}
514+
446515
function getMessage(key, args, self) {
447516
const msg = messages.get(key);
448517

449-
assert ??= require('internal/assert');
450-
451518
if (typeof msg === 'function') {
452519
assert(
453520
msg.length <= args.length, // Default options do not count.
@@ -457,9 +524,7 @@ function getMessage(key, args, self) {
457524
return ReflectApply(msg, self, args);
458525
}
459526

460-
const regex = /%[dfijoOs]/g;
461-
let expectedLength = 0;
462-
while (RegExpPrototypeExec(regex, msg) !== null) expectedLength++;
527+
const expectedLength = getExpectedArgumentLength(msg);
463528
assert(
464529
expectedLength === args.length,
465530
`Code: ${key}; The provided arguments length (${args.length}) does not ` +
@@ -1476,8 +1541,7 @@ E('ERR_NETWORK_IMPORT_DISALLOWED',
14761541
"import of '%s' by %s is not supported: %s", Error);
14771542
E('ERR_NOT_BUILDING_SNAPSHOT',
14781543
'Operation cannot be invoked when not building startup snapshot', Error);
1479-
E('ERR_NOT_SUPPORTED_IN_SNAPSHOT',
1480-
'%s is not supported in startup snapshot', Error);
1544+
E('ERR_NOT_SUPPORTED_IN_SNAPSHOT', '%s is not supported in startup snapshot', Error);
14811545
E('ERR_NO_CRYPTO',
14821546
'Node.js is not compiled with OpenSSL crypto support', Error);
14831547
E('ERR_NO_ICU',
Collapse file

‎lib/internal/fs/streams.js‎

Copy file name to clipboardExpand all lines: lib/internal/fs/streams.js
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ function importFd(stream, options) {
143143
return options.fd.fd;
144144
}
145145

146-
throw ERR_INVALID_ARG_TYPE('options.fd',
147-
['number', 'FileHandle'], options.fd);
146+
throw new ERR_INVALID_ARG_TYPE('options.fd',
147+
['number', 'FileHandle'], options.fd);
148148
}
149149

150150
function ReadStream(path, options) {
Collapse file

‎lib/internal/modules/esm/hooks.js‎

Copy file name to clipboardExpand all lines: lib/internal/modules/esm/hooks.js
+2-2Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ class Hooks {
407407
!isAnyArrayBuffer(source) &&
408408
!isArrayBufferView(source)
409409
) {
410-
throw ERR_INVALID_RETURN_PROPERTY_VALUE(
410+
throw new ERR_INVALID_RETURN_PROPERTY_VALUE(
411411
'a string, an ArrayBuffer, or a TypedArray',
412412
hookErrIdentifier,
413413
'source',
@@ -599,7 +599,7 @@ class HooksProxy {
599599
if (status === 'error') {
600600
if (body == null || typeof body !== 'object') { throw body; }
601601
if (body.serializationFailed || body.serialized == null) {
602-
throw ERR_WORKER_UNSERIALIZABLE_ERROR();
602+
throw new ERR_WORKER_UNSERIALIZABLE_ERROR();
603603
}
604604

605605
// eslint-disable-next-line no-restricted-syntax
Collapse file

‎lib/internal/navigator.js‎

Copy file name to clipboardExpand all lines: lib/internal/navigator.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Navigator {
2727
if (arguments[0] === kInitialize) {
2828
return;
2929
}
30-
throw ERR_ILLEGAL_CONSTRUCTOR();
30+
throw new ERR_ILLEGAL_CONSTRUCTOR();
3131
}
3232

3333
/**
Collapse file

‎lib/internal/url.js‎

Copy file name to clipboardExpand all lines: lib/internal/url.js
+1-1Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -855,7 +855,7 @@ class URL {
855855
set href(value) {
856856
value = `${value}`;
857857
const href = bindingUrl.update(this.#context.href, updateActions.kHref, value);
858-
if (!href) { throw ERR_INVALID_URL(value); }
858+
if (!href) { throw new ERR_INVALID_URL(value); }
859859
this.#updateContext(href);
860860
}
861861

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.