Skip to content

Commit c71e548

Browse files
Uzlopaktargos
authored andcommitted
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 7ca1228 commit c71e548

17 files changed

Lines changed: 305 additions & 137 deletions
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
'use strict';
2+
3+
constcommon=require('../common');
4+
constassert=require('assert');
5+
6+
constbench=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+
functiongetErrorFactory(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)=>newError();
29+
case'ERR_HTTP2_STREAM_SELF_DEPENDENCY':
30+
return(n)=>newERR_HTTP2_STREAM_SELF_DEPENDENCY();
31+
case'ERR_INVALID_STATE':
32+
return(n)=>newERR_INVALID_STATE(n+'');
33+
case'ERR_INVALID_URL':
34+
return(n)=>newERR_INVALID_URL({input: n+''});
35+
default:
36+
thrownewError(`${code} not supported`);
37+
}
38+
}
39+
40+
functionmain({ n, code, stackTraceLimit }){
41+
constgetError=getErrorFactory(code);
42+
43+
Error.stackTraceLimit=stackTraceLimit;
44+
45+
// Warm up.
46+
constlength=1024;
47+
constarray=[];
48+
for(leti=0;i<length;++i){
49+
array.push(getError(i));
50+
}
51+
52+
bench.start();
53+
54+
for(leti=0;i<n;++i){
55+
constindex=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(leti=0;i<length;++i){
64+
assert.strictEqual(typeofarray[i],'object');
65+
}
66+
}
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict';
2+
3+
constcommon=require('../common');
4+
constassert=require('assert');
5+
6+
constbench=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+
functiongetErrorStackFactory(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)=>newError().stack;
27+
case'ERR_HTTP2_STREAM_SELF_DEPENDENCY':
28+
return(n)=>newERR_HTTP2_STREAM_SELF_DEPENDENCY().stack;
29+
case'ERR_INVALID_STATE':
30+
return(n)=>newERR_INVALID_STATE(n+'').stack;
31+
default:
32+
thrownewError(`${code} not supported`);
33+
}
34+
}
35+
36+
functionmain({ n, code, stackTraceLimit }){
37+
constgetStack=getErrorStackFactory(code);
38+
39+
Error.stackTraceLimit=stackTraceLimit;
40+
41+
// Warm up.
42+
constlength=1024;
43+
constarray=[];
44+
for(leti=0;i<length;++i){
45+
array.push(getStack(i));
46+
}
47+
48+
bench.start();
49+
50+
for(leti=0;i<n;++i){
51+
constindex=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(leti=0;i<length;++i){
60+
assert.strictEqual(typeofarray[i],'string');
61+
}
62+
}

‎benchmark/error/node-error.js‎

Lines changed: 0 additions & 21 deletions
This file was deleted.

‎lib/internal/crypto/hkdf.js‎

Lines 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-
throwERR_OUT_OF_RANGE(
60+
thrownewERR_OUT_OF_RANGE(
6161
'info',
6262
'must not contain more than 1024 bytes',
6363
info.byteLength);

‎lib/internal/errors.js‎

Lines changed: 107 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -175,9 +175,10 @@ const aggregateErrors = hideStackFrames((errors, message, code) => {
175175
returnerr;
176176
});
177177

178+
constassert=require('internal/assert');
179+
178180
// Lazily loaded
179181
letutil;
180-
letassert;
181182

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

373374
functionmakeNodeErrorWithCode(Base,key){
374-
returnfunctionNodeError(...args){
375-
constlimit=Error.stackTraceLimit;
376-
if(isErrorStackTraceLimitWritable())Error.stackTraceLimit=0;
377-
consterror=newBase();
378-
// Reset the limit and setting the name property.
379-
if(isErrorStackTraceLimitWritable())Error.stackTraceLimit=limit;
380-
constmessage=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+
constmsg=messages.get(key);
376+
constexpectedLength=typeofmsg!=='string' ? -1 : getExpectedArgumentLength(msg);
377+
378+
switch(expectedLength){
379+
case0: {
380+
classNodeErrorextendsBase{
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+
returnBase;
396+
}
397+
398+
get[kIsNodeError](){
399+
returntrue;
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-
returnerror;
409-
};
404+
}
405+
}
406+
returnNodeError;
407+
}
408+
case-1: {
409+
classNodeErrorextendsBase{
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+
returnBase;
427+
}
428+
429+
get[kIsNodeError](){
430+
returntrue;
431+
}
432+
433+
toString(){
434+
return`${this.name} [${key}]: ${this.message}`;
435+
}
436+
}
437+
returnNodeError;
438+
}
439+
default: {
440+
441+
classNodeErrorextendsBase{
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+
returnBase;
459+
}
460+
461+
get[kIsNodeError](){
462+
returntrue;
463+
}
464+
465+
toString(){
466+
return`${this.name} [${key}]: ${this.message}`;
467+
}
468+
}
469+
returnNodeError;
470+
}
471+
}
410472
}
411473

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

508+
functiongetExpectedArgumentLength(msg){
509+
letexpectedLength=0;
510+
constregex=/%[dfijoOs]/g;
511+
while(RegExpPrototypeExec(regex,msg)!==null)expectedLength++;
512+
returnexpectedLength;
513+
}
514+
446515
functiongetMessage(key,args,self){
447516
constmsg=messages.get(key);
448517

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

460-
constregex=/%[dfijoOs]/g;
461-
letexpectedLength=0;
462-
while(RegExpPrototypeExec(regex,msg)!==null)expectedLength++;
527+
constexpectedLength=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',

‎lib/internal/fs/streams.js‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ function importFd(stream, options) {
143143
returnoptions.fd.fd;
144144
}
145145

146-
throwERR_INVALID_ARG_TYPE('options.fd',
147-
['number','FileHandle'],options.fd);
146+
thrownewERR_INVALID_ARG_TYPE('options.fd',
147+
['number','FileHandle'],options.fd);
148148
}
149149

150150
functionReadStream(path,options){

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ class Hooks {
471471
!isAnyArrayBuffer(source)&&
472472
!isArrayBufferView(source)
473473
){
474-
throwERR_INVALID_RETURN_PROPERTY_VALUE(
474+
thrownewERR_INVALID_RETURN_PROPERTY_VALUE(
475475
'a string, an ArrayBuffer, or a TypedArray',
476476
hookErrIdentifier,
477477
'source',
@@ -662,7 +662,7 @@ class HooksProxy {
662662
if(status==='error'){
663663
if(body==null||typeofbody!=='object'){throwbody;}
664664
if(body.serializationFailed||body.serialized==null){
665-
throwERR_WORKER_UNSERIALIZABLE_ERROR();
665+
thrownewERR_WORKER_UNSERIALIZABLE_ERROR();
666666
}
667667

668668
// eslint-disable-next-line no-restricted-syntax

‎lib/internal/url.js‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -847,7 +847,7 @@ class URL {
847847
sethref(value){
848848
value=`${value}`;
849849
consthref=bindingUrl.update(this.#context.href,updateActions.kHref,value);
850-
if(!href){throwERR_INVALID_URL(value);}
850+
if(!href){thrownewERR_INVALID_URL(value);}
851851
this.#updateContext(href);
852852
}
853853

0 commit comments

Comments
 (0)