lib: convert signals to array before validation
Co-authored-by: Jake Yuesong Li <jake.yuesong@gmail.com> PR-URL: https://github.com/nodejs/node/pull/54714 Fixes: https://github.com/nodejs/node/issues/54674 Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Matthew Aitken <maitken033380023@gmail.com> Reviewed-By: Ethan Arrowood <ethan@arrowood.dev> Reviewed-By: Antoine du Hamel <duhamelantoine1995@gmail.com>
This commit is contained in:
parent
1c67899ffb
commit
973144b7c6
@ -39,6 +39,10 @@ const {
|
||||
ERR_INVALID_THIS,
|
||||
},
|
||||
} = require('internal/errors');
|
||||
const {
|
||||
converters,
|
||||
createSequenceConverter,
|
||||
} = require('internal/webidl');
|
||||
|
||||
const {
|
||||
validateAbortSignal,
|
||||
@ -225,15 +229,19 @@ class AbortSignal extends EventTarget {
|
||||
* @returns {AbortSignal}
|
||||
*/
|
||||
static any(signals) {
|
||||
validateAbortSignalArray(signals, 'signals');
|
||||
const signalsArray = createSequenceConverter(
|
||||
converters.any,
|
||||
)(signals);
|
||||
|
||||
validateAbortSignalArray(signalsArray, 'signals');
|
||||
const resultSignal = new AbortSignal(kDontThrowSymbol, { composite: true });
|
||||
if (!signals.length) {
|
||||
if (!signalsArray.length) {
|
||||
return resultSignal;
|
||||
}
|
||||
const resultSignalWeakRef = new WeakRef(resultSignal);
|
||||
resultSignal[kSourceSignals] = new SafeSet();
|
||||
for (let i = 0; i < signals.length; i++) {
|
||||
const signal = signals[i];
|
||||
for (let i = 0; i < signalsArray.length; i++) {
|
||||
const signal = signalsArray[i];
|
||||
if (signal.aborted) {
|
||||
abortSignal(resultSignal, signal.reason);
|
||||
return resultSignal;
|
||||
|
@ -22,7 +22,6 @@ const {
|
||||
ObjectPrototypeIsPrototypeOf,
|
||||
SafeArrayIterator,
|
||||
String,
|
||||
SymbolIterator,
|
||||
TypedArrayPrototypeGetBuffer,
|
||||
TypedArrayPrototypeGetSymbolToStringTag,
|
||||
globalThis: {
|
||||
@ -33,6 +32,7 @@ const {
|
||||
const {
|
||||
makeException,
|
||||
createEnumConverter,
|
||||
createSequenceConverter,
|
||||
} = require('internal/webidl');
|
||||
|
||||
const {
|
||||
@ -293,39 +293,6 @@ function createDictionaryConverter(name, dictionaries) {
|
||||
};
|
||||
}
|
||||
|
||||
function createSequenceConverter(converter) {
|
||||
return function(V, opts = kEmptyObject) {
|
||||
if (type(V) !== 'Object') {
|
||||
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,
|
||||
context: `${opts.context}, index ${array.length}`,
|
||||
});
|
||||
ArrayPrototypePush(array, val);
|
||||
}
|
||||
return array;
|
||||
};
|
||||
}
|
||||
|
||||
function createInterfaceConverter(name, prototype) {
|
||||
return (V, opts) => {
|
||||
if (!ObjectPrototypeIsPrototypeOf(prototype, V)) {
|
||||
|
@ -1,6 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const {
|
||||
ArrayPrototypePush,
|
||||
MathAbs,
|
||||
MathMax,
|
||||
MathMin,
|
||||
@ -13,6 +14,7 @@ const {
|
||||
ObjectAssign,
|
||||
SafeSet,
|
||||
String,
|
||||
SymbolIterator,
|
||||
TypeError,
|
||||
} = primordials;
|
||||
|
||||
@ -25,6 +27,15 @@ const { kEmptyObject } = require('internal/util');
|
||||
|
||||
const converters = { __proto__: null };
|
||||
|
||||
/**
|
||||
* @see https://webidl.spec.whatwg.org/#es-any
|
||||
* @param {any} V
|
||||
* @returns {any}
|
||||
*/
|
||||
converters.any = (V) => {
|
||||
return V;
|
||||
};
|
||||
|
||||
// https://webidl.spec.whatwg.org/#abstract-opdef-integerpart
|
||||
const integerPart = MathTrunc;
|
||||
|
||||
@ -209,10 +220,76 @@ function createEnumConverter(name, values) {
|
||||
};
|
||||
}
|
||||
|
||||
// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values
|
||||
function type(V) {
|
||||
if (V === null)
|
||||
return 'Null';
|
||||
|
||||
switch (typeof V) {
|
||||
case 'undefined':
|
||||
return 'Undefined';
|
||||
case 'boolean':
|
||||
return 'Boolean';
|
||||
case 'number':
|
||||
return 'Number';
|
||||
case 'string':
|
||||
return 'String';
|
||||
case 'symbol':
|
||||
return 'Symbol';
|
||||
case 'bigint':
|
||||
return 'BigInt';
|
||||
case 'object': // Fall through
|
||||
case 'function': // Fall through
|
||||
default:
|
||||
// Per ES spec, typeof returns an implemention-defined value that is not
|
||||
// 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.
|
||||
return 'Object';
|
||||
}
|
||||
}
|
||||
|
||||
// https://webidl.spec.whatwg.org/#es-sequence
|
||||
function createSequenceConverter(converter) {
|
||||
return function(V, opts = kEmptyObject) {
|
||||
if (type(V) !== 'Object') {
|
||||
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,
|
||||
context: `${opts.context}, index ${array.length}`,
|
||||
});
|
||||
ArrayPrototypePush(array, val);
|
||||
};
|
||||
return array;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
module.exports = {
|
||||
type,
|
||||
converters,
|
||||
convertToInt,
|
||||
createEnumConverter,
|
||||
createSequenceConverter,
|
||||
evenRound,
|
||||
makeException,
|
||||
};
|
||||
|
@ -101,4 +101,21 @@ describe('AbortSignal.any()', { concurrency: !process.env.TEST_PARALLEL }, () =>
|
||||
controller.abort();
|
||||
assert.strictEqual(result, '01234');
|
||||
});
|
||||
|
||||
it('must accept WebIDL sequence', () => {
|
||||
const controller = new AbortController();
|
||||
const iterable = {
|
||||
*[Symbol.iterator]() {
|
||||
yield controller.signal;
|
||||
yield new AbortController().signal;
|
||||
yield new AbortController().signal;
|
||||
yield new AbortController().signal;
|
||||
},
|
||||
};
|
||||
const signal = AbortSignal.any(iterable);
|
||||
let result = 0;
|
||||
signal.addEventListener('abort', () => result += 1);
|
||||
controller.abort();
|
||||
assert.strictEqual(result, 1);
|
||||
});
|
||||
});
|
||||
|
Loading…
x
Reference in New Issue
Block a user