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 18f0f07

Browse filesBrowse files
jazellyruyadorno
authored andcommitted
lib: implement webidl dictionary converter and use it in structuredClone
This commit provides a factory to generate `dictionaryConverter` compliant with the spec. The implemented factory function is used for the `structuredClone` algorithm with updated test cases. PR-URL: #55489 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
1 parent bcead24 commit 18f0f07
Copy full SHA for 18f0f07

3 files changed

+98-23Lines changed: 98 additions & 23 deletions

File tree

Expand file treeCollapse file tree
Open diff view settings
Filter options
Expand file treeCollapse file tree
Open diff view settings
Collapse file

‎lib/internal/webidl.js‎

Copy file name to clipboardExpand all lines: lib/internal/webidl.js
+63Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const {
44
ArrayPrototypePush,
5+
ArrayPrototypeToSorted,
56
MathAbs,
67
MathMax,
78
MathMin,
@@ -270,6 +271,67 @@ function type(V) {
270271
}
271272
}
272273

274+
// https://webidl.spec.whatwg.org/#js-dictionary
275+
function createDictionaryConverter(members) {
276+
// The spec requires us to operate the members of a dictionary in
277+
// lexicographical order. We are doing this in the outer scope to
278+
// reduce the overhead that could happen in the returned function.
279+
const sortedMembers = ArrayPrototypeToSorted(members, (a, b) => {
280+
if (a.key === b.key) {
281+
return 0;
282+
}
283+
return a.key < b.key ? -1 : 1;
284+
});
285+
286+
return function(
287+
V,
288+
opts = kEmptyObject,
289+
) {
290+
if (V != null && type(V) !== OBJECT) {
291+
throw makeException(
292+
'cannot be converted to a dictionary',
293+
opts,
294+
);
295+
}
296+
297+
const idlDict = { __proto__: null };
298+
for (let i = 0; i < sortedMembers.length; i++) {
299+
const member = sortedMembers[i];
300+
const key = member.key;
301+
let jsMemberValue;
302+
if (V == null) {
303+
jsMemberValue = undefined;
304+
} else {
305+
jsMemberValue = V[key];
306+
}
307+
308+
if (jsMemberValue !== undefined) {
309+
const memberContext = opts.context ? `${key} in ${opts.context}` : `${key}`;
310+
const converter = member.converter;
311+
const idlMemberValue = converter(
312+
jsMemberValue,
313+
{
314+
__proto__: null,
315+
prefix: opts.prefix,
316+
context: memberContext,
317+
},
318+
);
319+
idlDict[key] = idlMemberValue;
320+
} else if (typeof member.defaultValue === 'function') {
321+
const idlMemberValue = member.defaultValue();
322+
idlDict[key] = idlMemberValue;
323+
} else if (member.required) {
324+
throw makeException(
325+
`cannot be converted because of the missing '${key}'`,
326+
opts,
327+
);
328+
}
329+
}
330+
331+
return idlDict;
332+
};
333+
}
334+
273335
// https://webidl.spec.whatwg.org/#es-sequence
274336
function createSequenceConverter(converter) {
275337
return function(V, opts = kEmptyObject) {
@@ -311,6 +373,7 @@ module.exports = {
311373
convertToInt,
312374
createEnumConverter,
313375
createSequenceConverter,
376+
createDictionaryConverter,
314377
evenRound,
315378
makeException,
316379
};
Collapse file

‎lib/internal/worker/js_transferable.js‎

Copy file name to clipboardExpand all lines: lib/internal/worker/js_transferable.js
+19-18Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ const {
55
} = primordials;
66
const {
77
codes: {
8-
ERR_INVALID_ARG_TYPE,
98
ERR_MISSING_ARGS,
109
},
1110
} = require('internal/errors');
@@ -98,29 +97,31 @@ function markTransferMode(obj, cloneable = false, transferable = false) {
9897
obj[transfer_mode_private_symbol] = mode;
9998
}
10099

100+
101+
webidl.converters.StructuredSerializeOptions = webidl
102+
.createDictionaryConverter(
103+
[
104+
{
105+
key: 'transfer',
106+
converter: webidl.converters['sequence<object>'],
107+
defaultValue: () => [],
108+
},
109+
],
110+
);
111+
101112
function structuredClone(value, options) {
102113
if (arguments.length === 0) {
103114
throw new ERR_MISSING_ARGS('The value argument must be specified');
104115
}
105116

106-
// TODO(jazelly): implement generic webidl dictionary converter
107-
const prefix = 'Options';
108-
const optionsType = webidl.type(options);
109-
if (optionsType !== 'Undefined' && optionsType !== 'Null' && optionsType !== 'Object') {
110-
throw new ERR_INVALID_ARG_TYPE(
111-
prefix,
112-
['object', 'null', 'undefined'],
113-
options,
114-
);
115-
}
116-
const key = 'transfer';
117-
const idlOptions = { __proto__: null, [key]: [] };
118-
if (options != null && key in options && options[key] !== undefined) {
119-
idlOptions[key] = webidl.converters['sequence<object>'](options[key], {
117+
const idlOptions = webidl.converters.StructuredSerializeOptions(
118+
options,
119+
{
120120
__proto__: null,
121-
context: 'Transfer',
122-
});
123-
}
121+
prefix: "Failed to execute 'structuredClone'",
122+
context: 'Options',
123+
},
124+
);
124125

125126
const serializedData = nativeStructuredClone(value, idlOptions);
126127
return serializedData;
Collapse file

‎test/parallel/test-structuredClone-global.js‎

Copy file name to clipboardExpand all lines: test/parallel/test-structuredClone-global.js
+16-5Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,23 @@
33
require('../common');
44
const assert = require('assert');
55

6+
const prefix = "Failed to execute 'structuredClone'";
7+
const key = 'transfer';
8+
const context = 'Options';
9+
const memberConverterError = `${prefix}: ${key} in ${context} can not be converted to sequence.`;
10+
const dictionaryConverterError = `${prefix}: ${context} cannot be converted to a dictionary`;
11+
612
assert.throws(() => structuredClone(), { code: 'ERR_MISSING_ARGS' });
7-
assert.throws(() => structuredClone(undefined, ''), { code: 'ERR_INVALID_ARG_TYPE' });
8-
assert.throws(() => structuredClone(undefined, 1), { code: 'ERR_INVALID_ARG_TYPE' });
9-
assert.throws(() => structuredClone(undefined, { transfer: 1 }), { code: 'ERR_INVALID_ARG_TYPE' });
10-
assert.throws(() => structuredClone(undefined, { transfer: '' }), { code: 'ERR_INVALID_ARG_TYPE' });
11-
assert.throws(() => structuredClone(undefined, { transfer: null }), { code: 'ERR_INVALID_ARG_TYPE' });
13+
assert.throws(() => structuredClone(undefined, ''),
14+
{ code: 'ERR_INVALID_ARG_TYPE', message: dictionaryConverterError });
15+
assert.throws(() => structuredClone(undefined, 1),
16+
{ code: 'ERR_INVALID_ARG_TYPE', message: dictionaryConverterError });
17+
assert.throws(() => structuredClone(undefined, { transfer: 1 }),
18+
{ code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError });
19+
assert.throws(() => structuredClone(undefined, { transfer: '' }),
20+
{ code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError });
21+
assert.throws(() => structuredClone(undefined, { transfer: null }),
22+
{ code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError });
1223

1324
// Options can be null or undefined.
1425
assert.strictEqual(structuredClone(undefined), undefined);

0 commit comments

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