Skip to content

Commit

Permalink
lib: convert signals to array before validation
Browse files Browse the repository at this point in the history
Co-authored-by: Jake Yuesong Li <[email protected]>
PR-URL: #54714
Fixes: #54674
Reviewed-By: Matteo Collina <[email protected]>
Reviewed-By: Benjamin Gruenbaum <[email protected]>
Reviewed-By: Matthew Aitken <[email protected]>
Reviewed-By: Ethan Arrowood <[email protected]>
Reviewed-By: Antoine du Hamel <[email protected]>
  • Loading branch information
2 people authored and aduh95 committed Sep 12, 2024
1 parent 1fb67af commit fc78ced
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 38 deletions.
16 changes: 12 additions & 4 deletions lib/internal/abort_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,10 @@ const {
ERR_INVALID_THIS,
},
} = require('internal/errors');
const {
converters,
createSequenceConverter,
} = require('internal/webidl');

const {
validateAbortSignal,
Expand Down Expand Up @@ -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;
Expand Down
35 changes: 1 addition & 34 deletions lib/internal/crypto/webidl.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ const {
ObjectPrototypeIsPrototypeOf,
SafeArrayIterator,
String,
SymbolIterator,
TypedArrayPrototypeGetBuffer,
TypedArrayPrototypeGetSymbolToStringTag,
globalThis: {
Expand All @@ -33,6 +32,7 @@ const {
const {
makeException,
createEnumConverter,
createSequenceConverter,
} = require('internal/webidl');

const {
Expand Down Expand Up @@ -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)) {
Expand Down
77 changes: 77 additions & 0 deletions lib/internal/webidl.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const {
ArrayPrototypePush,
MathAbs,
MathMax,
MathMin,
Expand All @@ -13,6 +14,7 @@ const {
ObjectAssign,
SafeSet,
String,
SymbolIterator,
TypeError,
} = primordials;

Expand All @@ -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;

Expand Down Expand Up @@ -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,
};
17 changes: 17 additions & 0 deletions test/parallel/test-abortsignal-any.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
});
});

0 comments on commit fc78ced

Please sign in to comment.