Skip to content

Commit 1ebbbd5

Browse files
panvaaduh95
authored andcommitted
lib: optimize webidl conversion options
Replace object spread in nested WebIDL conversion options with stable-shape ordinary objects. This keeps hot dictionary and sequence conversion paths from allocating null-prototype spread results. Apply the same pattern to Web Crypto converter wrappers that override allowResizable or enable [EnforceRange]. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #62756 Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com>
1 parent 8bb5360 commit 1ebbbd5

2 files changed

Lines changed: 73 additions & 37 deletions

File tree

‎lib/internal/crypto/webidl.js‎

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,22 @@ converters['sequence<KeyUsage>'] = createSequenceConverter(converters.KeyUsage);
106106

107107
converters.HashAlgorithmIdentifier=converters.AlgorithmIdentifier;
108108

109+
/**
110+
* Builds conversion options for Web Crypto integer members that use Web IDL
111+
* [EnforceRange]. Keep this helper instead of spreading opts in each member
112+
* converter so the hot dictionary paths allocate stable-shape objects.
113+
* @param {object} opts Parent conversion options.
114+
* @returns {object}
115+
*/
116+
functionenforceRangeOptions(opts){
117+
return{
118+
prefix: opts.prefix,
119+
context: opts.context,
120+
code: opts.code,
121+
enforceRange: true,
122+
};
123+
}
124+
109125
constdictAlgorithm=[
110126
{
111127
key: 'name',
@@ -121,8 +137,9 @@ converters.Algorithm = createDictionaryConverter(
121137
// converters.BigInteger = webidl.Uint8Array;
122138
converters.BigInteger=(V,opts=kEmptyObject)=>{
123139
returnwebidl.Uint8Array(V,{
124-
__proto__: null,
125-
...opts,
140+
prefix: opts.prefix,
141+
context: opts.context,
142+
code: opts.code,
126143
allowResizable: true,
127144
allowShared: false,
128145
});
@@ -132,18 +149,20 @@ converters.BigInteger = (V, opts = kEmptyObject) => {
132149
// removing this altogether.
133150
converters.BufferSource=(V,opts=kEmptyObject)=>{
134151
returnwebidl.BufferSource(V,{
135-
__proto__: null,
136-
...opts,
152+
prefix: opts.prefix,
153+
context: opts.context,
154+
code: opts.code,
137155
allowResizable: opts.allowResizable===undefined ?
138156
true : opts.allowResizable,
157+
allowShared: opts.allowShared,
139158
});
140159
};
141160

142161
constdictRsaKeyGenParams=[
143162
{
144163
key: 'modulusLength',
145164
converter: (V,opts)=>
146-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
165+
converters['unsigned long'](V,enforceRangeOptions(opts)),
147166
required: true,
148167
},
149168
{
@@ -221,7 +240,7 @@ converters.AesKeyGenParams = createDictionaryConverter(
221240
{
222241
key: 'length',
223242
converter: (V,opts)=>
224-
converters['unsigned short'](V,{ ...opts,enforceRange: true}),
243+
converters['unsigned short'](V,enforceRangeOptions(opts)),
225244
validator: AESLengthValidator,
226245
required: true,
227246
},
@@ -244,7 +263,7 @@ converters.RsaPssParams = createDictionaryConverter(
244263
{
245264
key: 'saltLength',
246265
converter: (V,opts)=>
247-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
266+
converters['unsigned long'](V,enforceRangeOptions(opts)),
248267
required: true,
249268
},
250269
],
@@ -288,7 +307,7 @@ for (const { 0: name, 1: zeroError } of [['HmacKeyGenParams', 'OperationError'],
288307
{
289308
key: 'length',
290309
converter: (V,opts)=>
291-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
310+
converters['unsigned long'](V,enforceRangeOptions(opts)),
292311
validator: validateMacKeyLength(`${name}.length`,zeroError),
293312
},
294313
],
@@ -370,7 +389,7 @@ converters.CShakeParams = createDictionaryConverter(
370389
{
371390
key: 'outputLength',
372391
converter: (V,opts)=>
373-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
392+
converters['unsigned long'](V,enforceRangeOptions(opts)),
374393
validator: (V,opts)=>{
375394
// The Web Crypto spec allows for SHAKE output length that are not multiples of
376395
// 8. We don't.
@@ -404,7 +423,7 @@ converters.Pbkdf2Params = createDictionaryConverter(
404423
{
405424
key: 'iterations',
406425
converter: (V,opts)=>
407-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
426+
converters['unsigned long'](V,enforceRangeOptions(opts)),
408427
validator: (V,dict)=>{
409428
if(V===0)
410429
throwlazyDOMException('iterations cannot be zero','OperationError');
@@ -427,7 +446,7 @@ converters.AesDerivedKeyParams = createDictionaryConverter(
427446
{
428447
key: 'length',
429448
converter: (V,opts)=>
430-
converters['unsigned short'](V,{ ...opts,enforceRange: true}),
449+
converters['unsigned short'](V,enforceRangeOptions(opts)),
431450
validator: AESLengthValidator,
432451
required: true,
433452
},
@@ -481,7 +500,7 @@ converters.AeadParams = createDictionaryConverter(
481500
{
482501
key: 'tagLength',
483502
converter: (V,opts)=>
484-
converters.octet(V,{ ...opts,enforceRange: true}),
503+
converters.octet(V,enforceRangeOptions(opts)),
485504
validator: (V,dict)=>{
486505
switch(StringPrototypeToLowerCase(dict.name)){
487506
case'chacha20-poly1305':
@@ -524,7 +543,7 @@ converters.AesCtrParams = createDictionaryConverter(
524543
{
525544
key: 'length',
526545
converter: (V,opts)=>
527-
converters.octet(V,{ ...opts,enforceRange: true}),
546+
converters.octet(V,enforceRangeOptions(opts)),
528547
validator: (V,dict)=>{
529548
if(V===0||V>128)
530549
throwlazyDOMException(
@@ -604,7 +623,7 @@ converters.Argon2Params = createDictionaryConverter(
604623
{
605624
key: 'parallelism',
606625
converter: (V,opts)=>
607-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
626+
converters['unsigned long'](V,enforceRangeOptions(opts)),
608627
validator: (V,dict)=>{
609628
if(V===0||V>MathPow(2,24)-1){
610629
throwlazyDOMException(
@@ -617,7 +636,7 @@ converters.Argon2Params = createDictionaryConverter(
617636
{
618637
key: 'memory',
619638
converter: (V,opts)=>
620-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
639+
converters['unsigned long'](V,enforceRangeOptions(opts)),
621640
validator: (V,dict)=>{
622641
if(V<8*dict.parallelism){
623642
throwlazyDOMException(
@@ -630,7 +649,7 @@ converters.Argon2Params = createDictionaryConverter(
630649
{
631650
key: 'passes',
632651
converter: (V,opts)=>
633-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
652+
converters['unsigned long'](V,enforceRangeOptions(opts)),
634653
validator: (V)=>{
635654
if(V===0){
636655
throwlazyDOMException('passes must be > 0','OperationError');
@@ -641,7 +660,7 @@ converters.Argon2Params = createDictionaryConverter(
641660
{
642661
key: 'version',
643662
converter: (V,opts)=>
644-
converters.octet(V,{ ...opts,enforceRange: true}),
663+
converters.octet(V,enforceRangeOptions(opts)),
645664
validator: (V,dict)=>{
646665
if(V!==0x13){
647666
throwlazyDOMException(
@@ -680,7 +699,7 @@ for (const { 0: name, 1: zeroError } of [['KmacKeyGenParams', 'OperationError'],
680699
{
681700
key: 'length',
682701
converter: (V,opts)=>
683-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
702+
converters['unsigned long'](V,enforceRangeOptions(opts)),
684703
validator: validateMacKeyLength(`${name}.length`,zeroError),
685704
},
686705
],
@@ -694,7 +713,7 @@ converters.KmacParams = createDictionaryConverter(
694713
{
695714
key: 'outputLength',
696715
converter: (V,opts)=>
697-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
716+
converters['unsigned long'](V,enforceRangeOptions(opts)),
698717
validator: (V,opts)=>{
699718
// The Web Crypto spec allows for KMAC output length that are not multiples of 8. We don't.
700719
if(V%8)
@@ -716,7 +735,7 @@ converters.KangarooTwelveParams = createDictionaryConverter(
716735
{
717736
key: 'outputLength',
718737
converter: (V,opts)=>
719-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
738+
converters['unsigned long'](V,enforceRangeOptions(opts)),
720739
validator: (V,opts)=>{
721740
if(V===0||V%8)
722741
throwlazyDOMException('Invalid KangarooTwelveParams outputLength','OperationError');
@@ -737,7 +756,7 @@ converters.TurboShakeParams = createDictionaryConverter(
737756
{
738757
key: 'outputLength',
739758
converter: (V,opts)=>
740-
converters['unsigned long'](V,{ ...opts,enforceRange: true}),
759+
converters['unsigned long'](V,enforceRangeOptions(opts)),
741760
validator: (V,opts)=>{
742761
if(V===0||V%8)
743762
throwlazyDOMException('Invalid TurboShakeParams outputLength','OperationError');
@@ -747,7 +766,7 @@ converters.TurboShakeParams = createDictionaryConverter(
747766
{
748767
key: 'domainSeparation',
749768
converter: (V,opts)=>
750-
converters.octet(V,{ ...opts,enforceRange: true}),
769+
converters.octet(V,enforceRangeOptions(opts)),
751770
validator: (V)=>{
752771
if(V<0x01||V>0x7F){
753772
throwlazyDOMException(

‎lib/internal/webidl.js‎

Lines changed: 32 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,28 @@ function makeException(message, options = kEmptyObject) {
109109
);
110110
}
111111

112+
/**
113+
* Builds derived conversion options for nested converter calls and adjusted
114+
* error codes. These objects are allocated on dictionary/sequence conversion
115+
* hot paths, so keep their shape stable and avoid object spread and
116+
* null-prototype objects.
117+
* @param {ConversionOptions} options Parent conversion options.
118+
* @param {string} [context] Replacement context.
119+
* @param {string} [code] Replacement error code.
120+
* @returns {ConversionOptions}
121+
*/
122+
functionmakeOptions(options,context=options.context,code=options.code){
123+
return{
124+
prefix: options.prefix,
125+
context,
126+
code,
127+
enforceRange: options.enforceRange,
128+
clamp: options.clamp,
129+
allowShared: options.allowShared,
130+
allowResizable: options.allowResizable,
131+
};
132+
}
133+
112134
/**
113135
* Returns the ECMAScript specification type of a JavaScript value.
114136
* @see https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
@@ -376,7 +398,7 @@ function convertToInt(
376398
if(integer<lowerBound||integer>upperBound){
377399
throwmakeException(
378400
`is outside the expected range of ${lowerBound} to ${upperBound}.`,
379-
{__proto__: null, ...options,code: 'ERR_OUT_OF_RANGE'});
401+
makeOptions(options,options.context,'ERR_OUT_OF_RANGE'));
380402
}
381403

382404
returninteger;
@@ -416,7 +438,7 @@ function convertToInt(
416438
if(x<lowerBound||x>upperBound){
417439
throwmakeException(
418440
`is outside the expected range of ${lowerBound} to ${upperBound}.`,
419-
{__proto__: null, ...options,code: 'ERR_OUT_OF_RANGE'});
441+
makeOptions(options,options.context,'ERR_OUT_OF_RANGE'));
420442
}
421443

422444
returnx;
@@ -593,7 +615,7 @@ function requiredArguments(length, required, options = kEmptyObject) {
593615
`${required} argument${
594616
required===1 ? '' : 's'
595617
} required, but only ${length} present.`,
596-
{__proto__: null, ...options,context: '',code: 'ERR_MISSING_ARGS'});
618+
makeOptions(options,'','ERR_MISSING_ARGS'));
597619
}
598620
}
599621

@@ -615,7 +637,7 @@ function createEnumConverter(name, values) {
615637
if(!E.has(S)){
616638
throwmakeException(
617639
`'${S}' is not a valid enum value of type ${name}.`,
618-
{__proto__: null, ...options,code: 'ERR_INVALID_ARG_VALUE'});
640+
makeOptions(options,options.context,'ERR_INVALID_ARG_VALUE'));
619641
}
620642

621643
// Step 3: return the matching enumeration value.
@@ -715,11 +737,7 @@ function createDictionaryConverter(
715737
// Step 4.1.4.1: convert the JavaScript value to IDL.
716738
constidlMemberValue=converter(
717739
jsMemberValue,
718-
{
719-
__proto__: null,
720-
...options,
721-
context: dictionaryMemberContext(key,options),
722-
},
740+
makeOptions(options,dictionaryMemberContext(key,options)),
723741
);
724742
// Validators are a Node.js extension after conversion. They let
725743
// consumers reject known unsupported values while dictionary
@@ -736,7 +754,7 @@ function createDictionaryConverter(
736754
// Step 4.1.6: required missing members throw.
737755
throwmakeException(
738756
missingDictionaryMemberMessage(dictionaryName,key),
739-
{__proto__: null, ...options,code: 'ERR_MISSING_OPTION'});
757+
makeOptions(options,options.context,'ERR_MISSING_OPTION'));
740758
}
741759
}
742760
}
@@ -794,11 +812,10 @@ function createSequenceConverter(converter) {
794812
break;
795813
}
796814
// Step 3.3: convert next to an IDL value of type T.
797-
constidlValue=converter(next.value,{
798-
__proto__: null,
799-
...options,
800-
context: sequenceElementContext(idlSequence.length,options),
801-
});
815+
constidlValue=converter(
816+
next.value,
817+
makeOptions(options,sequenceElementContext(idlSequence.length,options)),
818+
);
802819
// Step 3.4: store the value and advance i.
803820
ArrayPrototypePush(idlSequence,idlValue);
804821
}

0 commit comments

Comments
 (0)