mirror of
https://github.com/nodejs/node.git
synced 2024-11-21 10:59:27 +00:00
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: https://github.com/nodejs/node/pull/55489 Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Yagiz Nizipli <yagiz@nizipli.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com>
This commit is contained in:
parent
270da88228
commit
e53fe03a18
@ -2,6 +2,7 @@
|
||||
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
ArrayPrototypeToSorted,
|
||||
MathAbs,
|
||||
MathMax,
|
||||
MathMin,
|
||||
@ -272,6 +273,67 @@ function type(V) {
|
||||
}
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#js-dictionary
|
||||
function createDictionaryConverter(members) {
|
||||
// The spec requires us to operate the members of a dictionary in
|
||||
// lexicographical order. We are doing this in the outer scope to
|
||||
// reduce the overhead that could happen in the returned function.
|
||||
const sortedMembers = ArrayPrototypeToSorted(members, (a, b) => {
|
||||
if (a.key === b.key) {
|
||||
return 0;
|
||||
}
|
||||
return a.key < b.key ? -1 : 1;
|
||||
});
|
||||
|
||||
return function(
|
||||
V,
|
||||
opts = kEmptyObject,
|
||||
) {
|
||||
if (V != null && type(V) !== OBJECT) {
|
||||
throw makeException(
|
||||
'cannot be converted to a dictionary',
|
||||
opts,
|
||||
);
|
||||
}
|
||||
|
||||
const idlDict = { __proto__: null };
|
||||
for (let i = 0; i < sortedMembers.length; i++) {
|
||||
const member = sortedMembers[i];
|
||||
const key = member.key;
|
||||
let jsMemberValue;
|
||||
if (V == null) {
|
||||
jsMemberValue = undefined;
|
||||
} else {
|
||||
jsMemberValue = V[key];
|
||||
}
|
||||
|
||||
if (jsMemberValue !== undefined) {
|
||||
const memberContext = opts.context ? `${key} in ${opts.context}` : `${key}`;
|
||||
const converter = member.converter;
|
||||
const idlMemberValue = converter(
|
||||
jsMemberValue,
|
||||
{
|
||||
__proto__: null,
|
||||
prefix: opts.prefix,
|
||||
context: memberContext,
|
||||
},
|
||||
);
|
||||
idlDict[key] = idlMemberValue;
|
||||
} else if (typeof member.defaultValue === 'function') {
|
||||
const idlMemberValue = member.defaultValue();
|
||||
idlDict[key] = idlMemberValue;
|
||||
} else if (member.required) {
|
||||
throw makeException(
|
||||
`cannot be converted because of the missing '${key}'`,
|
||||
opts,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return idlDict;
|
||||
};
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#es-sequence
|
||||
function createSequenceConverter(converter) {
|
||||
return function(V, opts = kEmptyObject) {
|
||||
@ -327,6 +389,7 @@ module.exports = {
|
||||
createEnumConverter,
|
||||
createInterfaceConverter,
|
||||
createSequenceConverter,
|
||||
createDictionaryConverter,
|
||||
evenRound,
|
||||
makeException,
|
||||
};
|
||||
|
@ -5,7 +5,6 @@ const {
|
||||
} = primordials;
|
||||
const {
|
||||
codes: {
|
||||
ERR_INVALID_ARG_TYPE,
|
||||
ERR_MISSING_ARGS,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
@ -98,29 +97,31 @@ function markTransferMode(obj, cloneable = false, transferable = false) {
|
||||
obj[transfer_mode_private_symbol] = mode;
|
||||
}
|
||||
|
||||
|
||||
webidl.converters.StructuredSerializeOptions = webidl
|
||||
.createDictionaryConverter(
|
||||
[
|
||||
{
|
||||
key: 'transfer',
|
||||
converter: webidl.converters['sequence<object>'],
|
||||
defaultValue: () => [],
|
||||
},
|
||||
],
|
||||
);
|
||||
|
||||
function structuredClone(value, options) {
|
||||
if (arguments.length === 0) {
|
||||
throw new ERR_MISSING_ARGS('The value argument must be specified');
|
||||
}
|
||||
|
||||
// TODO(jazelly): implement generic webidl dictionary converter
|
||||
const prefix = 'Options';
|
||||
const optionsType = webidl.type(options);
|
||||
if (optionsType !== 'Undefined' && optionsType !== 'Null' && optionsType !== 'Object') {
|
||||
throw new ERR_INVALID_ARG_TYPE(
|
||||
prefix,
|
||||
['object', 'null', 'undefined'],
|
||||
options,
|
||||
);
|
||||
}
|
||||
const key = 'transfer';
|
||||
const idlOptions = { __proto__: null, [key]: [] };
|
||||
if (options != null && key in options && options[key] !== undefined) {
|
||||
idlOptions[key] = webidl.converters['sequence<object>'](options[key], {
|
||||
const idlOptions = webidl.converters.StructuredSerializeOptions(
|
||||
options,
|
||||
{
|
||||
__proto__: null,
|
||||
context: 'Transfer',
|
||||
});
|
||||
}
|
||||
prefix: "Failed to execute 'structuredClone'",
|
||||
context: 'Options',
|
||||
},
|
||||
);
|
||||
|
||||
const serializedData = nativeStructuredClone(value, idlOptions);
|
||||
return serializedData;
|
||||
|
@ -3,12 +3,23 @@
|
||||
require('../common');
|
||||
const assert = require('assert');
|
||||
|
||||
const prefix = "Failed to execute 'structuredClone'";
|
||||
const key = 'transfer';
|
||||
const context = 'Options';
|
||||
const memberConverterError = `${prefix}: ${key} in ${context} can not be converted to sequence.`;
|
||||
const dictionaryConverterError = `${prefix}: ${context} cannot be converted to a dictionary`;
|
||||
|
||||
assert.throws(() => structuredClone(), { code: 'ERR_MISSING_ARGS' });
|
||||
assert.throws(() => structuredClone(undefined, ''), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => structuredClone(undefined, 1), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => structuredClone(undefined, { transfer: 1 }), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => structuredClone(undefined, { transfer: '' }), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => structuredClone(undefined, { transfer: null }), { code: 'ERR_INVALID_ARG_TYPE' });
|
||||
assert.throws(() => structuredClone(undefined, ''),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE', message: dictionaryConverterError });
|
||||
assert.throws(() => structuredClone(undefined, 1),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE', message: dictionaryConverterError });
|
||||
assert.throws(() => structuredClone(undefined, { transfer: 1 }),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError });
|
||||
assert.throws(() => structuredClone(undefined, { transfer: '' }),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError });
|
||||
assert.throws(() => structuredClone(undefined, { transfer: null }),
|
||||
{ code: 'ERR_INVALID_ARG_TYPE', message: memberConverterError });
|
||||
|
||||
// Options can be null or undefined.
|
||||
assert.strictEqual(structuredClone(undefined), undefined);
|
||||
|
Loading…
Reference in New Issue
Block a user