GeezerVison is showing github.com — lib: convert signals to array before validation · nodejs/node@fc78ced
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboardExpand all lines: lib/internal/abort_controller.js +12-4Lines changed: 12 additions & 4 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -39,6 +39,10 @@ const { | 39 | 39 | ERR_INVALID_THIS, | 40 | 40 | }, | 41 | 41 | } = require('internal/errors'); | | 42 | +const { | | 43 | + converters, | | 44 | + createSequenceConverter, | | 45 | +} = require('internal/webidl'); | 42 | 46 |
| 43 | 47 | const { | 44 | 48 | validateAbortSignal, | @@ -225,15 +229,19 @@ class AbortSignal extends EventTarget { | 225 | 229 | * @returns {AbortSignal} | 226 | 230 | */ | 227 | 231 | static any(signals) { | 228 | | - validateAbortSignalArray(signals, 'signals'); | | 232 | + const signalsArray = createSequenceConverter( | | 233 | + converters.any, | | 234 | + )(signals); | | 235 | + | | 236 | + validateAbortSignalArray(signalsArray, 'signals'); | 229 | 237 | const resultSignal = new AbortSignal(kDontThrowSymbol, { composite: true }); | 230 | | - if (!signals.length) { | | 238 | + if (!signalsArray.length) { | 231 | 239 | return resultSignal; | 232 | 240 | } | 233 | 241 | const resultSignalWeakRef = new WeakRef(resultSignal); | 234 | 242 | resultSignal[kSourceSignals] = new SafeSet(); | 235 | | - for (let i = 0; i < signals.length; i++) { | 236 | | - const signal = signals[i]; | | 243 | + for (let i = 0; i < signalsArray.length; i++) { | | 244 | + const signal = signalsArray[i]; | 237 | 245 | if (signal.aborted) { | 238 | 246 | abortSignal(resultSignal, signal.reason); | 239 | 247 | return resultSignal; | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/crypto/webidl.js +1-34Lines changed: 1 addition & 34 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -22,7 +22,6 @@ const { | 22 | 22 | ObjectPrototypeIsPrototypeOf, | 23 | 23 | SafeArrayIterator, | 24 | 24 | String, | 25 | | - SymbolIterator, | 26 | 25 | TypedArrayPrototypeGetBuffer, | 27 | 26 | TypedArrayPrototypeGetSymbolToStringTag, | 28 | 27 | globalThis: { | @@ -33,6 +32,7 @@ const { | 33 | 32 | const { | 34 | 33 | makeException, | 35 | 34 | createEnumConverter, | | 35 | + createSequenceConverter, | 36 | 36 | } = require('internal/webidl'); | 37 | 37 |
| 38 | 38 | const { | @@ -293,39 +293,6 @@ function createDictionaryConverter(name, dictionaries) { | 293 | 293 | }; | 294 | 294 | } | 295 | 295 |
| 296 | | -function createSequenceConverter(converter) { | 297 | | - return function(V, opts = kEmptyObject) { | 298 | | - if (type(V) !== 'Object') { | 299 | | - throw makeException( | 300 | | - 'can not be converted to sequence.', | 301 | | - opts); | 302 | | - } | 303 | | - const iter = V?.[SymbolIterator]?.(); | 304 | | - if (iter === undefined) { | 305 | | - throw makeException( | 306 | | - 'can not be converted to sequence.', | 307 | | - opts); | 308 | | - } | 309 | | - const array = []; | 310 | | - while (true) { | 311 | | - const res = iter?.next?.(); | 312 | | - if (res === undefined) { | 313 | | - throw makeException( | 314 | | - 'can not be converted to sequence.', | 315 | | - opts); | 316 | | - } | 317 | | - if (res.done === true) break; | 318 | | - const val = converter(res.value, { | 319 | | - __proto__: null, | 320 | | - ...opts, | 321 | | - context: `${opts.context}, index ${array.length}`, | 322 | | - }); | 323 | | - ArrayPrototypePush(array, val); | 324 | | - } | 325 | | - return array; | 326 | | - }; | 327 | | -} | 328 | | - | 329 | 296 | function createInterfaceConverter(name, prototype) { | 330 | 297 | return (V, opts) => { | 331 | 298 | if (!ObjectPrototypeIsPrototypeOf(prototype, V)) { | |
Collapse file Copy file name to clipboardExpand all lines: lib/internal/webidl.js +77Lines changed: 77 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 1 | 1 | 'use strict'; | 2 | 2 |
| 3 | 3 | const { | | 4 | + ArrayPrototypePush, | 4 | 5 | MathAbs, | 5 | 6 | MathMax, | 6 | 7 | MathMin, | @@ -13,6 +14,7 @@ const { | 13 | 14 | ObjectAssign, | 14 | 15 | SafeSet, | 15 | 16 | String, | | 17 | + SymbolIterator, | 16 | 18 | TypeError, | 17 | 19 | } = primordials; | 18 | 20 |
| @@ -25,6 +27,15 @@ const { kEmptyObject } = require('internal/util'); | 25 | 27 |
| 26 | 28 | const converters = { __proto__: null }; | 27 | 29 |
| | 30 | +/** | | 31 | + * @see https://webidl.spec.whatwg.org/#es-any | | 32 | + * @param {any} V | | 33 | + * @returns {any} | | 34 | + */ | | 35 | +converters.any = (V) => { | | 36 | + return V; | | 37 | +}; | | 38 | + | 28 | 39 | // https://webidl.spec.whatwg.org/#abstract-opdef-integerpart | 29 | 40 | const integerPart = MathTrunc; | 30 | 41 |
| @@ -209,10 +220,76 @@ function createEnumConverter(name, values) { | 209 | 220 | }; | 210 | 221 | } | 211 | 222 |
| | 223 | +// https://tc39.es/ecma262/#sec-ecmascript-data-types-and-values | | 224 | +function type(V) { | | 225 | + if (V === null) | | 226 | + return 'Null'; | | 227 | + | | 228 | + switch (typeof V) { | | 229 | + case 'undefined': | | 230 | + return 'Undefined'; | | 231 | + case 'boolean': | | 232 | + return 'Boolean'; | | 233 | + case 'number': | | 234 | + return 'Number'; | | 235 | + case 'string': | | 236 | + return 'String'; | | 237 | + case 'symbol': | | 238 | + return 'Symbol'; | | 239 | + case 'bigint': | | 240 | + return 'BigInt'; | | 241 | + case 'object': // Fall through | | 242 | + case 'function': // Fall through | | 243 | + default: | | 244 | + // Per ES spec, typeof returns an implemention-defined value that is not | | 245 | + // any of the existing ones for uncallable non-standard exotic objects. | | 246 | + // Yet Type() which the Web IDL spec depends on returns Object for such | | 247 | + // cases. So treat the default case as an object. | | 248 | + return 'Object'; | | 249 | + } | | 250 | +} | | 251 | + | | 252 | +// https://webidl.spec.whatwg.org/#es-sequence | | 253 | +function createSequenceConverter(converter) { | | 254 | + return function(V, opts = kEmptyObject) { | | 255 | + if (type(V) !== 'Object') { | | 256 | + throw makeException( | | 257 | + 'can not be converted to sequence.', | | 258 | + opts); | | 259 | + } | | 260 | + const iter = V?.[SymbolIterator]?.(); | | 261 | + if (iter === undefined) { | | 262 | + throw makeException( | | 263 | + 'can not be converted to sequence.', | | 264 | + opts); | | 265 | + } | | 266 | + const array = []; | | 267 | + while (true) { | | 268 | + const res = iter?.next?.(); | | 269 | + if (res === undefined) { | | 270 | + throw makeException( | | 271 | + 'can not be converted to sequence.', | | 272 | + opts); | | 273 | + } | | 274 | + if (res.done === true) break; | | 275 | + const val = converter(res.value, { | | 276 | + __proto__: null, | | 277 | + ...opts, | | 278 | + context: `${opts.context}, index ${array.length}`, | | 279 | + }); | | 280 | + ArrayPrototypePush(array, val); | | 281 | + }; | | 282 | + return array; | | 283 | + }; | | 284 | +} | | 285 | + | | 286 | + | 212 | 287 | module.exports = { | | 288 | + type, | 213 | 289 | converters, | 214 | 290 | convertToInt, | 215 | 291 | createEnumConverter, | | 292 | + createSequenceConverter, | 216 | 293 | evenRound, | 217 | 294 | makeException, | 218 | 295 | }; |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-abortsignal-any.mjs +17Lines changed: 17 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -101,4 +101,21 @@ describe('AbortSignal.any()', { concurrency: !process.env.TEST_PARALLEL }, () => | 101 | 101 | controller.abort(); | 102 | 102 | assert.strictEqual(result, '01234'); | 103 | 103 | }); | | 104 | + | | 105 | + it('must accept WebIDL sequence', () => { | | 106 | + const controller = new AbortController(); | | 107 | + const iterable = { | | 108 | + *[Symbol.iterator]() { | | 109 | + yield controller.signal; | | 110 | + yield new AbortController().signal; | | 111 | + yield new AbortController().signal; | | 112 | + yield new AbortController().signal; | | 113 | + }, | | 114 | + }; | | 115 | + const signal = AbortSignal.any(iterable); | | 116 | + let result = 0; | | 117 | + signal.addEventListener('abort', () => result += 1); | | 118 | + controller.abort(); | | 119 | + assert.strictEqual(result, 1); | | 120 | + }); | 104 | 121 | }); |
|