Skip to content

Commit

Permalink
abort_controller: convert signals to array before validation
Browse files Browse the repository at this point in the history
Co-authored-by: Jake Yuesong Li <jake.yuesong@gmail.com>
  • Loading branch information
jazelly and jakecastelli committed Sep 2, 2024
1 parent 981c701 commit 5ad876a
Show file tree
Hide file tree
Showing 4 changed files with 101 additions and 38 deletions.
21 changes: 17 additions & 4 deletions lib/internal/abort_controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ const {
ERR_INVALID_THIS,
},
} = require('internal/errors');
const {
createSequenceConverter,
} = require('internal/webidl');

const {
validateAbortSignal,
Expand Down Expand Up @@ -96,6 +99,12 @@ const kComposite = Symbol('kComposite');
const kSourceSignals = Symbol('kSourceSignals');
const kDependantSignals = Symbol('kDependantSignals');

const converters = {};
converters.any = (V) => {
return V;
};


function customInspect(self, obj, depth, options) {
if (depth < 0)
return self;
Expand Down Expand Up @@ -225,15 +234,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
66 changes: 66 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 Down Expand Up @@ -209,10 +211,74 @@ function createEnumConverter(name, 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';
}
}

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 = '';
signal.addEventListener('abort', () => result += '1');
controller.abort();
assert.strictEqual(result, '1');
});
});

0 comments on commit 5ad876a

Please sign in to comment.