Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

lib: convert signals to array before validation #54714

Merged
merged 1 commit into from
Sep 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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);
});
});
Loading