Skip to content

Commit ef08c50

Browse files
panvaaduh95
authored andcommitted
lib: refactor internal webidl converters
Rework lib/internal/webidl.js into a documented shared converter module that follows the Web IDL conversion algorithms more closely. Improvements: - Add documented converters and helper factories for primitive values, dictionaries, enums, sequences, interfaces, required arguments, integers, `Uint8Array`, and `BufferSource`. - Move WebCrypto onto the shared converters, while keeping compatibility wrappers for its existing `BufferSource` and `BigInteger` behavior. - Use shared converters from Blob, Performance, Web Locks, and structured clone option handling. - Add benchmarks for `ConvertToInt` and WebCrypto Web IDL converter hot paths. - Add focused tests for core converters, WebCrypto converters, integer conversion, and buffer source behavior. Fixes: - Make the shared `BufferSource` and `Uint8Array` converters reject resizable `ArrayBuffer` and growable `SharedArrayBuffer` backing stores unless explicitly allowed. WebCrypto preserves its legacy resizable backing-store behavior through compatibility wrappers until a semver-major follow-up can opt in to the stricter behavior. - Use Web IDL `ToNumber` and `ToString` behavior for BigInt, Symbol, and object primitive conversion. - Use exact BigInt modulo for 64-bit `ConvertToInt` wrapping and document the final Number approximation behavior. - Normalize mathematical modulo results to `+0` where Web IDL requires it. - Process inherited dictionaries in least-derived to most-derived order, sorting members only within each dictionary level. - Use `IteratorComplete` truthiness for sequence conversion. - Cover detached buffers, resizable-backed views, growable-backed views, cross-realm buffer sources, mutation-after-call behavior, inherited dictionary member order, and sequence iterator completion behavior. Signed-off-by: Filip Skokan <panva.ip@gmail.com> PR-URL: #62979 Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent 6386914 commit ef08c50

18 files changed

Lines changed: 2813 additions & 1063 deletions

‎benchmark/misc/webcrypto-webidl.js‎

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
'use strict';
2+
3+
constcommon=require('../common.js');
4+
5+
constbench=common.createBenchmark(main,{
6+
op: [
7+
'normalizeAlgorithm-string',
8+
'normalizeAlgorithm-dict',
9+
'webidl-dict',
10+
'webidl-algorithm-identifier-string',
11+
'webidl-algorithm-identifier-object',
12+
'webidl-dict-enforce-range',
13+
'webidl-dict-ensure-sha',
14+
'webidl-dict-null',
15+
],
16+
n: [1e6],
17+
},{flags: ['--expose-internals']});
18+
19+
functionmain({ n, op }){
20+
const{ normalizeAlgorithm }=require('internal/crypto/util');
21+
22+
switch(op){
23+
case'normalizeAlgorithm-string': {
24+
// String shortcut + null dictionary (cheapest path).
25+
bench.start();
26+
for(leti=0;i<n;i++)
27+
normalizeAlgorithm('SHA-256','digest');
28+
bench.end(n);
29+
break;
30+
}
31+
case'normalizeAlgorithm-dict': {
32+
// Object input with a dictionary type and no BufferSource members.
33+
constalg={name: 'ECDSA',hash: 'SHA-256'};
34+
bench.start();
35+
for(leti=0;i<n;i++)
36+
normalizeAlgorithm(alg,'sign');
37+
bench.end(n);
38+
break;
39+
}
40+
case'webidl-dict': {
41+
// WebIDL dictionary converter in isolation.
42+
constwebidl=require('internal/crypto/webidl');
43+
constinput={name: 'AES-GCM',iv: newUint8Array(12)};
44+
constopts={prefix: 'test',context: 'test'};
45+
bench.start();
46+
for(leti=0;i<n;i++)
47+
webidl.converters.AeadParams(input,opts);
48+
bench.end(n);
49+
break;
50+
}
51+
case'webidl-algorithm-identifier-string': {
52+
// Exercises converters.AlgorithmIdentifier string path.
53+
constwebidl=require('internal/crypto/webidl');
54+
constopts={prefix: 'test',context: 'test'};
55+
bench.start();
56+
for(leti=0;i<n;i++)
57+
webidl.converters.AlgorithmIdentifier('SHA-256',opts);
58+
bench.end(n);
59+
break;
60+
}
61+
case'webidl-algorithm-identifier-object': {
62+
// Exercises converters.AlgorithmIdentifier object path.
63+
constwebidl=require('internal/crypto/webidl');
64+
constinput={name: 'SHA-256'};
65+
constopts={prefix: 'test',context: 'test'};
66+
bench.start();
67+
for(leti=0;i<n;i++)
68+
webidl.converters.AlgorithmIdentifier(input,opts);
69+
bench.end(n);
70+
break;
71+
}
72+
case'webidl-dict-enforce-range': {
73+
// Exercises [EnforceRange] integer dictionary members.
74+
constwebidl=require('internal/crypto/webidl');
75+
constinput={
76+
name: 'RSASSA-PKCS1-v1_5',
77+
modulusLength: 2048,
78+
publicExponent: newUint8Array([1,0,1]),
79+
};
80+
constopts={prefix: 'test',context: 'test'};
81+
bench.start();
82+
for(leti=0;i<n;i++)
83+
webidl.converters.RsaKeyGenParams(input,opts);
84+
bench.end(n);
85+
break;
86+
}
87+
case'webidl-dict-ensure-sha': {
88+
// Exercises ensureSHA on a hash member.
89+
constwebidl=require('internal/crypto/webidl');
90+
constinput={name: 'RSASSA-PKCS1-v1_5',hash: 'SHA-256'};
91+
constopts={prefix: 'test',context: 'test'};
92+
bench.start();
93+
for(leti=0;i<n;i++)
94+
webidl.converters.RsaHashedImportParams(input,opts);
95+
bench.end(n);
96+
break;
97+
}
98+
case'webidl-dict-null': {
99+
// Exercises the null/undefined path in createDictionaryConverter().
100+
constwebidl=require('internal/crypto/webidl');
101+
constopts={prefix: 'test',context: 'test'};
102+
bench.start();
103+
for(leti=0;i<n;i++)
104+
webidl.converters.JsonWebKey(undefined,opts);
105+
bench.end(n);
106+
break;
107+
}
108+
}
109+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
'use strict';
2+
3+
constassert=require('assert');
4+
constcommon=require('../common.js');
5+
6+
constbench=common.createBenchmark(main,{
7+
converter: [
8+
'byte',
9+
'octet',
10+
'unsigned short',
11+
'unsigned long',
12+
'long long',
13+
],
14+
input: [
15+
'integer',
16+
'fractional',
17+
'wrap',
18+
'clamp',
19+
'enforce-range',
20+
'object',
21+
],
22+
n: [1e6],
23+
},{flags: ['--expose-internals']});
24+
25+
functiongetConverter(converter){
26+
switch(converter){
27+
case'byte':
28+
return{bitLength: 8,signedness: 'signed'};
29+
case'octet':
30+
return{bitLength: 8};
31+
case'unsigned short':
32+
return{bitLength: 16};
33+
case'unsigned long':
34+
return{bitLength: 32};
35+
case'long long':
36+
return{bitLength: 64,signedness: 'signed'};
37+
default:
38+
thrownewError(`Unsupported converter: ${converter}`);
39+
}
40+
}
41+
42+
functiongetInput(input){
43+
switch(input){
44+
case'integer':
45+
return{value: 7};
46+
case'fractional':
47+
return{value: 7.9};
48+
case'wrap':
49+
return{value: 2**63+2**11};
50+
case'clamp':
51+
return{value: 300.8,options: {clamp: true}};
52+
case'enforce-range':
53+
return{value: 7.9,options: {enforceRange: true}};
54+
case'object':
55+
return{
56+
value: {
57+
valueOf(){return7;},
58+
},
59+
};
60+
default:
61+
thrownewError(`Unsupported input: ${input}`);
62+
}
63+
}
64+
65+
functionmain({ n, converter, input }){
66+
const{ convertToInt }=require('internal/webidl');
67+
const{ bitLength, signedness }=getConverter(converter);
68+
const{ value, options }=getInput(input);
69+
70+
letnoDead;
71+
bench.start();
72+
if(options===undefined){
73+
for(leti=0;i<n;i++)
74+
noDead=convertToInt(value,bitLength,signedness);
75+
}else{
76+
for(leti=0;i<n;i++)
77+
noDead=convertToInt(value,bitLength,signedness,options);
78+
}
79+
bench.end(n);
80+
81+
assert.strictEqual(typeofnoDead,'number');
82+
}

‎lib/internal/blob.js‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,6 @@ const {
5454
const{ inspect }=require('internal/util/inspect');
5555
const{
5656
converters,
57-
convertToInt,
5857
createSequenceConverter,
5958
}=require('internal/webidl');
6059

@@ -245,10 +244,14 @@ class Blob {
245244
if(!isBlob(this))
246245
thrownewERR_INVALID_THIS('Blob');
247246

248-
// Coerce values to int
249-
constopts={__proto__: null,signed: true};
250-
start=convertToInt('start',start,64,opts);
251-
end=convertToInt('end',end,64,opts);
247+
start=converters['long long'](
248+
start,
249+
{__proto__: null,context: 'start'},
250+
);
251+
end=converters['long long'](
252+
end,
253+
{__proto__: null,context: 'end'},
254+
);
252255

253256
if(start<0){
254257
start=MathMax(this[kLength]+start,0);

0 commit comments

Comments
 (0)