2022-10-13 17:25:33 +00:00
|
|
|
|
'use strict';
|
|
|
|
|
|
|
|
|
|
const {
|
2024-09-06 17:40:21 +00:00
|
|
|
|
ArrayPrototypePush,
|
2024-10-20 12:07:00 +00:00
|
|
|
|
ArrayPrototypeToSorted,
|
2022-10-13 17:25:33 +00:00
|
|
|
|
MathAbs,
|
|
|
|
|
MathMax,
|
|
|
|
|
MathMin,
|
|
|
|
|
MathPow,
|
|
|
|
|
MathSign,
|
|
|
|
|
MathTrunc,
|
|
|
|
|
NumberIsNaN,
|
|
|
|
|
NumberMAX_SAFE_INTEGER,
|
|
|
|
|
NumberMIN_SAFE_INTEGER,
|
2023-11-09 11:09:17 +00:00
|
|
|
|
ObjectAssign,
|
2024-09-17 11:11:02 +00:00
|
|
|
|
ObjectPrototypeIsPrototypeOf,
|
2023-11-09 11:09:17 +00:00
|
|
|
|
SafeSet,
|
2023-04-14 10:12:28 +00:00
|
|
|
|
String,
|
2024-11-07 23:21:50 +00:00
|
|
|
|
Symbol,
|
2024-09-06 17:40:21 +00:00
|
|
|
|
SymbolIterator,
|
2023-11-09 11:09:17 +00:00
|
|
|
|
TypeError,
|
2022-10-13 17:25:33 +00:00
|
|
|
|
} = primordials;
|
|
|
|
|
|
|
|
|
|
const {
|
|
|
|
|
codes: {
|
2024-09-17 11:11:02 +00:00
|
|
|
|
ERR_INVALID_ARG_TYPE,
|
2022-10-13 17:25:33 +00:00
|
|
|
|
ERR_INVALID_ARG_VALUE,
|
|
|
|
|
},
|
|
|
|
|
} = require('internal/errors');
|
|
|
|
|
const { kEmptyObject } = require('internal/util');
|
|
|
|
|
|
2023-04-14 10:12:28 +00:00
|
|
|
|
const converters = { __proto__: null };
|
|
|
|
|
|
2024-11-07 23:21:50 +00:00
|
|
|
|
const UNDEFINED = Symbol('undefined');
|
|
|
|
|
const BOOLEAN = Symbol('boolean');
|
|
|
|
|
const STRING = Symbol('string');
|
|
|
|
|
const SYMBOL = Symbol('symbol');
|
|
|
|
|
const NUMBER = Symbol('number');
|
|
|
|
|
const BIGINT = Symbol('bigint');
|
|
|
|
|
const NULL = Symbol('null');
|
|
|
|
|
const OBJECT = Symbol('object');
|
2024-10-24 13:06:58 +00:00
|
|
|
|
|
2024-09-06 17:40:21 +00:00
|
|
|
|
/**
|
|
|
|
|
* @see https://webidl.spec.whatwg.org/#es-any
|
|
|
|
|
* @param {any} V
|
|
|
|
|
* @returns {any}
|
|
|
|
|
*/
|
|
|
|
|
converters.any = (V) => {
|
|
|
|
|
return V;
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-13 18:11:21 +00:00
|
|
|
|
converters.object = (V, opts = kEmptyObject) => {
|
2024-10-24 13:06:58 +00:00
|
|
|
|
if (type(V) !== OBJECT) {
|
2024-10-13 18:11:21 +00:00
|
|
|
|
throw makeException(
|
|
|
|
|
'is not an object',
|
|
|
|
|
kEmptyObject,
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
return V;
|
|
|
|
|
};
|
|
|
|
|
|
2022-10-13 17:25:33 +00:00
|
|
|
|
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
|
|
|
|
|
const integerPart = MathTrunc;
|
|
|
|
|
|
|
|
|
|
/* eslint-disable node-core/non-ascii-character */
|
|
|
|
|
// Round x to the nearest integer, choosing the even integer if it lies halfway
|
|
|
|
|
// between two, and choosing +0 rather than -0.
|
|
|
|
|
// This is different from Math.round, which rounds to the next integer in the
|
|
|
|
|
// direction of +∞ when the fraction portion is exactly 0.5.
|
|
|
|
|
/* eslint-enable node-core/non-ascii-character */
|
|
|
|
|
function evenRound(x) {
|
|
|
|
|
// Convert -0 to +0.
|
|
|
|
|
const i = integerPart(x) + 0;
|
|
|
|
|
const reminder = MathAbs(x % 1);
|
|
|
|
|
const sign = MathSign(i);
|
|
|
|
|
if (reminder === 0.5) {
|
|
|
|
|
return i % 2 === 0 ? i : i + sign;
|
|
|
|
|
}
|
|
|
|
|
const r = reminder < 0.5 ? i : i + sign;
|
|
|
|
|
// Convert -0 to +0.
|
|
|
|
|
if (r === 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function pow2(exponent) {
|
|
|
|
|
// << operates on 32 bit signed integers.
|
|
|
|
|
if (exponent < 31) {
|
|
|
|
|
return 1 << exponent;
|
|
|
|
|
}
|
|
|
|
|
if (exponent === 31) {
|
|
|
|
|
return 0x8000_0000;
|
|
|
|
|
}
|
|
|
|
|
if (exponent === 32) {
|
|
|
|
|
return 0x1_0000_0000;
|
|
|
|
|
}
|
|
|
|
|
return MathPow(2, exponent);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://tc39.es/ecma262/#eqn-modulo
|
|
|
|
|
// The notation “x modulo y” computes a value k of the same sign as y.
|
|
|
|
|
function modulo(x, y) {
|
|
|
|
|
const r = x % y;
|
|
|
|
|
// Convert -0 to +0.
|
|
|
|
|
if (r === 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
return r;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// https://webidl.spec.whatwg.org/#abstract-opdef-converttoint
|
|
|
|
|
function convertToInt(name, value, bitLength, options = kEmptyObject) {
|
|
|
|
|
const { signed = false, enforceRange = false, clamp = false } = options;
|
|
|
|
|
|
|
|
|
|
let upperBound;
|
|
|
|
|
let lowerBound;
|
|
|
|
|
// 1. If bitLength is 64, then:
|
|
|
|
|
if (bitLength === 64) {
|
|
|
|
|
// 1.1. Let upperBound be 2^53 − 1.
|
|
|
|
|
upperBound = NumberMAX_SAFE_INTEGER;
|
|
|
|
|
// 1.2. If signedness is "unsigned", then let lowerBound be 0.
|
|
|
|
|
// 1.3. Otherwise let lowerBound be −2^53 + 1.
|
|
|
|
|
lowerBound = !signed ? 0 : NumberMIN_SAFE_INTEGER;
|
|
|
|
|
} else if (!signed) {
|
|
|
|
|
// 2. Otherwise, if signedness is "unsigned", then:
|
|
|
|
|
// 2.1. Let lowerBound be 0.
|
|
|
|
|
// 2.2. Let upperBound be 2^bitLength − 1.
|
|
|
|
|
lowerBound = 0;
|
|
|
|
|
upperBound = pow2(bitLength) - 1;
|
|
|
|
|
} else {
|
|
|
|
|
// 3. Otherwise:
|
|
|
|
|
// 3.1. Let lowerBound be -2^(bitLength − 1).
|
|
|
|
|
// 3.2. Let upperBound be 2^(bitLength − 1) − 1.
|
|
|
|
|
lowerBound = -pow2(bitLength - 1);
|
|
|
|
|
upperBound = pow2(bitLength - 1) - 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 4. Let x be ? ToNumber(V).
|
|
|
|
|
let x = +value;
|
|
|
|
|
// 5. If x is −0, then set x to +0.
|
|
|
|
|
if (x === 0) {
|
|
|
|
|
x = 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6. If the conversion is to an IDL type associated with the [EnforceRange]
|
|
|
|
|
// extended attribute, then:
|
|
|
|
|
if (enforceRange) {
|
|
|
|
|
// 6.1. If x is NaN, +∞, or −∞, then throw a TypeError.
|
|
|
|
|
if (NumberIsNaN(x) || x === Infinity || x === -Infinity) {
|
|
|
|
|
throw new ERR_INVALID_ARG_VALUE(name, x);
|
|
|
|
|
}
|
|
|
|
|
// 6.2. Set x to IntegerPart(x).
|
|
|
|
|
x = integerPart(x);
|
|
|
|
|
|
|
|
|
|
// 6.3. If x < lowerBound or x > upperBound, then throw a TypeError.
|
|
|
|
|
if (x < lowerBound || x > upperBound) {
|
|
|
|
|
throw new ERR_INVALID_ARG_VALUE(name, x);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 6.4. Return x.
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 7. If x is not NaN and the conversion is to an IDL type associated with
|
|
|
|
|
// the [Clamp] extended attribute, then:
|
|
|
|
|
if (clamp && !NumberIsNaN(x)) {
|
|
|
|
|
// 7.1. Set x to min(max(x, lowerBound), upperBound).
|
|
|
|
|
x = MathMin(MathMax(x, lowerBound), upperBound);
|
|
|
|
|
|
|
|
|
|
// 7.2. Round x to the nearest integer, choosing the even integer if it
|
|
|
|
|
// lies halfway between two, and choosing +0 rather than −0.
|
|
|
|
|
x = evenRound(x);
|
|
|
|
|
|
|
|
|
|
// 7.3. Return x.
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 8. If x is NaN, +0, +∞, or −∞, then return +0.
|
|
|
|
|
if (NumberIsNaN(x) || x === 0 || x === Infinity || x === -Infinity) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 9. Set x to IntegerPart(x).
|
|
|
|
|
x = integerPart(x);
|
|
|
|
|
|
|
|
|
|
// 10. Set x to x modulo 2^bitLength.
|
|
|
|
|
x = modulo(x, pow2(bitLength));
|
|
|
|
|
|
|
|
|
|
// 11. If signedness is "signed" and x ≥ 2^(bitLength − 1), then return x −
|
|
|
|
|
// 2^bitLength.
|
|
|
|
|
if (signed && x >= pow2(bitLength - 1)) {
|
|
|
|
|
return x - pow2(bitLength);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 12. Otherwise, return x.
|
|
|
|
|
return x;
|
|
|
|
|
}
|
|
|
|
|
|
2023-04-14 10:12:28 +00:00
|
|
|
|
/**
|
|
|
|
|
* @see https://webidl.spec.whatwg.org/#es-DOMString
|
|
|
|
|
* @param {any} V
|
|
|
|
|
* @returns {string}
|
|
|
|
|
*/
|
|
|
|
|
converters.DOMString = function DOMString(V) {
|
|
|
|
|
if (typeof V === 'symbol') {
|
|
|
|
|
throw new ERR_INVALID_ARG_VALUE('value', V);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return String(V);
|
|
|
|
|
};
|
|
|
|
|
|
2024-10-13 18:11:21 +00:00
|
|
|
|
converters['sequence<object>'] = createSequenceConverter(converters.object);
|
|
|
|
|
|
2023-11-09 11:09:17 +00:00
|
|
|
|
function codedTypeError(message, errorProperties = kEmptyObject) {
|
|
|
|
|
// eslint-disable-next-line no-restricted-syntax
|
|
|
|
|
const err = new TypeError(message);
|
|
|
|
|
ObjectAssign(err, errorProperties);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function makeException(message, opts = kEmptyObject) {
|
|
|
|
|
const prefix = opts.prefix ? opts.prefix + ': ' : '';
|
|
|
|
|
const context = opts.context?.length === 0 ?
|
|
|
|
|
'' : (opts.context ?? 'Value') + ' ';
|
|
|
|
|
return codedTypeError(
|
|
|
|
|
`${prefix}${context}${message}`,
|
|
|
|
|
{ code: opts.code || 'ERR_INVALID_ARG_TYPE' },
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function createEnumConverter(name, values) {
|
|
|
|
|
const E = new SafeSet(values);
|
|
|
|
|
|
|
|
|
|
return function(V, opts = kEmptyObject) {
|
|
|
|
|
const S = String(V);
|
|
|
|
|
|
|
|
|
|
if (!E.has(S)) {
|
|
|
|
|
throw makeException(
|
|
|
|
|
`value '${S}' is not a valid enum value of type ${name}.`,
|
|
|
|
|
{ __proto__: null, ...opts, code: 'ERR_INVALID_ARG_VALUE' });
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return S;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:40:21 +00:00
|
|
|
|
// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
|
|
|
|
|
function type(V) {
|
|
|
|
|
switch (typeof V) {
|
|
|
|
|
case 'undefined':
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return UNDEFINED;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
case 'boolean':
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return BOOLEAN;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
case 'number':
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return NUMBER;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
case 'string':
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return STRING;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
case 'symbol':
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return SYMBOL;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
case 'bigint':
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return BIGINT;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
case 'object': // Fall through
|
|
|
|
|
case 'function': // Fall through
|
|
|
|
|
default:
|
2024-10-24 13:06:58 +00:00
|
|
|
|
if (V === null) {
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
2024-09-25 14:35:18 +00:00
|
|
|
|
// Per ES spec, typeof returns an implementation-defined value that is not
|
2024-09-06 17:40:21 +00:00
|
|
|
|
// any of the existing ones for uncallable non-standard exotic objects.
|
|
|
|
|
// Yet Type() which the Web IDL spec depends on returns Object for such
|
|
|
|
|
// cases. So treat the default case as an object.
|
2024-10-24 13:06:58 +00:00
|
|
|
|
return OBJECT;
|
2024-09-06 17:40:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-20 12:07:00 +00:00
|
|
|
|
// 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;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:40:21 +00:00
|
|
|
|
// https://webidl.spec.whatwg.org/#es-sequence
|
|
|
|
|
function createSequenceConverter(converter) {
|
|
|
|
|
return function(V, opts = kEmptyObject) {
|
2024-10-24 13:06:58 +00:00
|
|
|
|
if (type(V) !== OBJECT) {
|
2024-09-06 17:40:21 +00:00
|
|
|
|
throw makeException(
|
|
|
|
|
'can not be converted to sequence.',
|
|
|
|
|
opts);
|
|
|
|
|
}
|
|
|
|
|
const iter = V?.[SymbolIterator]?.();
|
|
|
|
|
if (iter === undefined) {
|
|
|
|
|
throw makeException(
|
|
|
|
|
'can not be converted to sequence.',
|
|
|
|
|
opts);
|
|
|
|
|
}
|
|
|
|
|
const array = [];
|
|
|
|
|
while (true) {
|
|
|
|
|
const res = iter?.next?.();
|
|
|
|
|
if (res === undefined) {
|
|
|
|
|
throw makeException(
|
|
|
|
|
'can not be converted to sequence.',
|
|
|
|
|
opts);
|
|
|
|
|
}
|
|
|
|
|
if (res.done === true) break;
|
|
|
|
|
const val = converter(res.value, {
|
|
|
|
|
__proto__: null,
|
|
|
|
|
...opts,
|
2024-09-17 11:11:02 +00:00
|
|
|
|
context: `${opts.context}[${array.length}]`,
|
2024-09-06 17:40:21 +00:00
|
|
|
|
});
|
|
|
|
|
ArrayPrototypePush(array, val);
|
|
|
|
|
};
|
|
|
|
|
return array;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-17 11:11:02 +00:00
|
|
|
|
// https://webidl.spec.whatwg.org/#js-interface
|
|
|
|
|
function createInterfaceConverter(name, I) {
|
|
|
|
|
return (V, opts = kEmptyObject) => {
|
|
|
|
|
// 1. If V implements I, then return the IDL interface type value that
|
|
|
|
|
// represents a reference to that platform object.
|
|
|
|
|
if (ObjectPrototypeIsPrototypeOf(I, V)) return V;
|
|
|
|
|
// 2. Throw a TypeError.
|
|
|
|
|
throw new ERR_INVALID_ARG_TYPE(
|
|
|
|
|
typeof opts.context === 'string' ? opts.context : 'value', name, V,
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-06 17:40:21 +00:00
|
|
|
|
|
2022-10-13 17:25:33 +00:00
|
|
|
|
module.exports = {
|
2024-09-06 17:40:21 +00:00
|
|
|
|
type,
|
2023-11-09 11:09:17 +00:00
|
|
|
|
converters,
|
2022-10-13 17:25:33 +00:00
|
|
|
|
convertToInt,
|
2023-11-09 11:09:17 +00:00
|
|
|
|
createEnumConverter,
|
2024-09-17 11:11:02 +00:00
|
|
|
|
createInterfaceConverter,
|
2024-09-06 17:40:21 +00:00
|
|
|
|
createSequenceConverter,
|
2024-10-20 12:07:00 +00:00
|
|
|
|
createDictionaryConverter,
|
2022-10-13 17:25:33 +00:00
|
|
|
|
evenRound,
|
2023-11-09 11:09:17 +00:00
|
|
|
|
makeException,
|
2022-10-13 17:25:33 +00:00
|
|
|
|
};
|