GeezerVison is showing github.com — errors: improve performance of instantiation · nodejs/node@c71e548
File treeExpand file treeCollapse file tree Open diff view settings fixtures/test-runner/output
Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboard File renamed without changes. Collapse file Copy file name to clipboard +66Lines changed: 66 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 Copy file name to clipboard +62Lines changed: 62 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 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 Copy file name to clipboardExpand all lines: benchmark/error/node-error.js -21Lines changed: 0 additions & 21 deletions Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/hkdf.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -57,7 +57,7 @@ const validateParameters = hideStackFrames((hash, key, salt, info, length) => { | 57 | 57 | validateInteger(length, 'length', 0, kMaxLength); | 58 | 58 |
| 59 | 59 | if (info.byteLength > 1024) { | 60 | | - throw ERR_OUT_OF_RANGE( | | 60 | + throw new ERR_OUT_OF_RANGE( | 61 | 61 | 'info', | 62 | 62 | 'must not contain more than 1024 bytes', | 63 | 63 | info.byteLength); | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/errors.js +107-43Lines changed: 107 additions & 43 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -175,9 +175,10 @@ const aggregateErrors = hideStackFrames((errors, message, code) => { | 175 | 175 | return err; | 176 | 176 | }); | 177 | 177 |
| | 178 | +const assert = require('internal/assert'); | | 179 | + | 178 | 180 | // Lazily loaded | 179 | 181 | let util; | 180 | | -let assert; | 181 | 182 |
| 182 | 183 | let internalUtil = null; | 183 | 184 | function lazyInternalUtil() { | @@ -371,42 +372,103 @@ function makeSystemErrorWithCode(key) { | 371 | 372 | } | 372 | 373 |
| 373 | 374 | 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() { | 399 | 403 | 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 | + } | 410 | 472 | } | 411 | 473 |
| 412 | 474 | /** | @@ -443,11 +505,16 @@ function E(sym, val, def, ...otherClasses) { | 443 | 505 | codes[sym] = def; | 444 | 506 | } | 445 | 507 |
| | 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 | + | 446 | 515 | function getMessage(key, args, self) { | 447 | 516 | const msg = messages.get(key); | 448 | 517 |
| 449 | | - assert ??= require('internal/assert'); | 450 | | - | 451 | 518 | if (typeof msg === 'function') { | 452 | 519 | assert( | 453 | 520 | msg.length <= args.length, // Default options do not count. | @@ -457,9 +524,7 @@ function getMessage(key, args, self) { | 457 | 524 | return ReflectApply(msg, self, args); | 458 | 525 | } | 459 | 526 |
| 460 | | - const regex = /%[dfijoOs]/g; | 461 | | - let expectedLength = 0; | 462 | | - while (RegExpPrototypeExec(regex, msg) !== null) expectedLength++; | | 527 | + const expectedLength = getExpectedArgumentLength(msg); | 463 | 528 | assert( | 464 | 529 | expectedLength === args.length, | 465 | 530 | `Code: ${key}; The provided arguments length (${args.length}) does not ` + | @@ -1476,8 +1541,7 @@ E('ERR_NETWORK_IMPORT_DISALLOWED', | 1476 | 1541 | "import of '%s' by %s is not supported: %s", Error); | 1477 | 1542 | E('ERR_NOT_BUILDING_SNAPSHOT', | 1478 | 1543 | '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); | 1481 | 1545 | E('ERR_NO_CRYPTO', | 1482 | 1546 | 'Node.js is not compiled with OpenSSL crypto support', Error); | 1483 | 1547 | E('ERR_NO_ICU', | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/fs/streams.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -143,8 +143,8 @@ function importFd(stream, options) { | 143 | 143 | return options.fd.fd; | 144 | 144 | } | 145 | 145 |
| 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); | 148 | 148 | } | 149 | 149 |
| 150 | 150 | function ReadStream(path, options) { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/modules/esm/hooks.js +2-2Lines changed: 2 additions & 2 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -471,7 +471,7 @@ class Hooks { | 471 | 471 | !isAnyArrayBuffer(source) && | 472 | 472 | !isArrayBufferView(source) | 473 | 473 | ) { | 474 | | - throw ERR_INVALID_RETURN_PROPERTY_VALUE( | | 474 | + throw new ERR_INVALID_RETURN_PROPERTY_VALUE( | 475 | 475 | 'a string, an ArrayBuffer, or a TypedArray', | 476 | 476 | hookErrIdentifier, | 477 | 477 | 'source', | @@ -662,7 +662,7 @@ class HooksProxy { | 662 | 662 | if (status === 'error') { | 663 | 663 | if (body == null || typeof body !== 'object') { throw body; } | 664 | 664 | if (body.serializationFailed || body.serialized == null) { | 665 | | - throw ERR_WORKER_UNSERIALIZABLE_ERROR(); | | 665 | + throw new ERR_WORKER_UNSERIALIZABLE_ERROR(); | 666 | 666 | } | 667 | 667 |
| 668 | 668 | // eslint-disable-next-line no-restricted-syntax | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/url.js +1-1Lines changed: 1 addition & 1 deletion | Original file line number | Diff line number | Diff line change |
|---|
@@ -847,7 +847,7 @@ class URL { | 847 | 847 | set href(value) { | 848 | 848 | value = `${value}`; | 849 | 849 | const href = bindingUrl.update(this.#context.href, updateActions.kHref, value); | 850 | | - if (!href) { throw ERR_INVALID_URL(value); } | | 850 | + if (!href) { throw new ERR_INVALID_URL(value); } | 851 | 851 | this.#updateContext(href); | 852 | 852 | } | 853 | 853 |
| |
|