Skip to content

Commit 26d63f0

Browse files
aduh95targos
authored andcommitted
lib: implement safe alternatives to Promise static methods
PR-URL: #43728 Reviewed-By: Benjamin Gruenbaum <[email protected]> Reviewed-By: Stephen Belanger <[email protected]> Reviewed-By: Chengzhong Wu <[email protected]>
1 parent f8f1d16 commit 26d63f0

File tree

4 files changed

+79
-9
lines changed

4 files changed

+79
-9
lines changed

lib/internal/modules/esm/module_job.js

+4-7
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,16 @@
22

33
const {
44
ArrayPrototypeJoin,
5-
ArrayPrototypeMap,
65
ArrayPrototypePush,
76
ArrayPrototypeSome,
87
FunctionPrototype,
98
ObjectCreate,
109
ObjectSetPrototypeOf,
11-
PromiseAll,
1210
PromiseResolve,
1311
PromisePrototypeCatch,
1412
ReflectApply,
1513
RegExpPrototypeTest,
16-
SafeArrayIterator,
14+
SafePromiseAll,
1715
SafeSet,
1816
StringPrototypeIncludes,
1917
StringPrototypeMatch,
@@ -83,9 +81,9 @@ class ModuleJob {
8381
});
8482

8583
if (promises !== undefined)
86-
await PromiseAll(new SafeArrayIterator(promises));
84+
await SafePromiseAll(promises);
8785

88-
return PromiseAll(new SafeArrayIterator(dependencyJobs));
86+
return SafePromiseAll(dependencyJobs);
8987
};
9088
// Promise for the list of all dependencyJobs.
9189
this.linked = link();
@@ -113,8 +111,7 @@ class ModuleJob {
113111
}
114112
jobsInGraph.add(moduleJob);
115113
const dependencyJobs = await moduleJob.linked;
116-
return PromiseAll(new SafeArrayIterator(
117-
ArrayPrototypeMap(dependencyJobs, addJobsToDependencyGraph)));
114+
return SafePromiseAll(dependencyJobs, addJobsToDependencyGraph);
118115
};
119116
await addJobsToDependencyGraph(this);
120117

lib/internal/per_context/primordials.js

+59
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,7 @@ function copyPrototype(src, dest, prefix) {
262262

263263
const {
264264
ArrayPrototypeForEach,
265+
ArrayPrototypeMap,
265266
FinalizationRegistry,
266267
FunctionPrototypeCall,
267268
Map,
@@ -434,5 +435,63 @@ primordials.AsyncIteratorPrototype =
434435
primordials.ReflectGetPrototypeOf(
435436
async function* () {}).prototype);
436437

438+
const arrayToSafePromiseIterable = (promises, mapFn) =>
439+
new primordials.SafeArrayIterator(
440+
ArrayPrototypeMap(
441+
promises,
442+
(promise, i) =>
443+
new SafePromise((a, b) => PromisePrototypeThen(mapFn == null ? promise : mapFn(promise, i), a, b))
444+
)
445+
);
446+
447+
/**
448+
* @param {Promise<any>[]} promises
449+
* @param {(v: Promise<any>, k: number) => Promise<any>} [mapFn]
450+
* @returns {Promise<any[]>}
451+
*/
452+
primordials.SafePromiseAll = (promises, mapFn) =>
453+
// Wrapping on a new Promise is necessary to not expose the SafePromise
454+
// prototype to user-land.
455+
new Promise((a, b) =>
456+
SafePromise.all(arrayToSafePromiseIterable(promises, mapFn)).then(a, b)
457+
);
458+
459+
/**
460+
* @param {Promise<any>[]} promises
461+
* @param {(v: Promise<any>, k: number) => Promise<any>} [mapFn]
462+
* @returns {Promise<PromiseSettledResult<any>[]>}
463+
*/
464+
primordials.SafePromiseAllSettled = (promises, mapFn) =>
465+
// Wrapping on a new Promise is necessary to not expose the SafePromise
466+
// prototype to user-land.
467+
new Promise((a, b) =>
468+
SafePromise.allSettled(arrayToSafePromiseIterable(promises, mapFn)).then(a, b)
469+
);
470+
471+
/**
472+
* @param {Promise<any>[]} promises
473+
* @param {(v: Promise<any>, k: number) => Promise<any>} [mapFn]
474+
* @returns {Promise<any>}
475+
*/
476+
primordials.SafePromiseAny = (promises, mapFn) =>
477+
// Wrapping on a new Promise is necessary to not expose the SafePromise
478+
// prototype to user-land.
479+
new Promise((a, b) =>
480+
SafePromise.any(arrayToSafePromiseIterable(promises, mapFn)).then(a, b)
481+
);
482+
483+
/**
484+
* @param {Promise<any>[]} promises
485+
* @param {(v: Promise<any>, k: number) => Promise<any>} [mapFn]
486+
* @returns {Promise<any>}
487+
*/
488+
primordials.SafePromiseRace = (promises, mapFn) =>
489+
// Wrapping on a new Promise is necessary to not expose the SafePromise
490+
// prototype to user-land.
491+
new Promise((a, b) =>
492+
SafePromise.race(arrayToSafePromiseIterable(promises, mapFn)).then(a, b)
493+
);
494+
495+
437496
ObjectSetPrototypeOf(primordials, null);
438497
ObjectFreeze(primordials);

lib/internal/vm/module.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ const {
1010
ObjectDefineProperty,
1111
ObjectGetPrototypeOf,
1212
ObjectSetPrototypeOf,
13-
PromiseAll,
1413
ReflectApply,
14+
SafePromiseAll,
1515
SafeWeakMap,
1616
Symbol,
1717
SymbolToStringTag,
@@ -330,7 +330,7 @@ class SourceTextModule extends Module {
330330

331331
try {
332332
if (promises !== undefined) {
333-
await PromiseAll(promises);
333+
await SafePromiseAll(promises);
334334
}
335335
} catch (e) {
336336
this.#error = e;

test/parallel/test-primordials-promise.js

+14
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@ const assert = require('assert');
77
const {
88
PromisePrototypeCatch,
99
PromisePrototypeThen,
10+
SafePromiseAll,
11+
SafePromiseAllSettled,
12+
SafePromiseAny,
1013
SafePromisePrototypeFinally,
14+
SafePromiseRace,
1115
} = require('internal/test/binding').primordials;
1216

17+
Array.prototype[Symbol.iterator] = common.mustNotCall();
18+
Promise.all = common.mustNotCall();
19+
Promise.allSettled = common.mustNotCall();
20+
Promise.any = common.mustNotCall();
21+
Promise.race = common.mustNotCall();
1322
Promise.prototype.catch = common.mustNotCall();
1423
Promise.prototype.finally = common.mustNotCall();
1524
Promise.prototype.then = common.mustNotCall();
@@ -18,6 +27,11 @@ assertIsPromise(PromisePrototypeCatch(Promise.reject(), common.mustCall()));
1827
assertIsPromise(PromisePrototypeThen(test(), common.mustCall()));
1928
assertIsPromise(SafePromisePrototypeFinally(test(), common.mustCall()));
2029

30+
assertIsPromise(SafePromiseAll([test()]));
31+
assertIsPromise(SafePromiseAllSettled([test()]));
32+
assertIsPromise(SafePromiseAny([test()]));
33+
assertIsPromise(SafePromiseRace([test()]));
34+
2135
async function test() {
2236
const catchFn = common.mustCall();
2337
const finallyFn = common.mustCall();

0 commit comments

Comments
 (0)