From 85ec0d475a7244d3180e914d1bede246180f9176 Mon Sep 17 00:00:00 2001 From: dasaria Date: Sat, 9 May 2026 18:09:53 -0400 Subject: [PATCH] [await-dictionary] add failure case and edge case tests Cover remaining items from the testing plan (#4886): - NewPromiseCapability: constructor throws, executor not callable - NewPromiseCapability: validate Construct (new.target, executor arity) - GetPromiseResolve: Get accessor throws, promiseResolve call throws - Invoke(then): Get accessor throws, call throws - allKeyed rejection ordering (2nd and last to settle) - Synchronous thenables hitting remainingElementsCount step 8 - Exotic object: [[OwnPropertyKeys]] throws - Exotic object: [[GetOwnProperty]] throws / returns undefined - Property descriptor filtering: non-enumerable keys skipped --- .../capability-executor-not-callable.js | 107 ++++++++++++++++++ .../Promise/allKeyed/ctx-ctor-constructed.js | 35 ++++++ .../Promise/allKeyed/ctx-ctor-throws.js | 28 +++++ .../allKeyed/getownproperty-not-enumerable.js | 48 ++++++++ .../getownproperty-returns-undefined.js | 35 ++++++ .../Promise/allKeyed/getownproperty-throws.js | 42 +++++++ .../allKeyed/invoke-resolve-error-reject.js | 39 +++++++ .../invoke-resolve-get-error-reject.js | 37 ++++++ .../allKeyed/invoke-then-error-reject.js | 39 +++++++ .../allKeyed/invoke-then-get-error-reject.js | 39 +++++++ .../Promise/allKeyed/ownkeys-throws.js | 37 ++++++ .../built-ins/Promise/allKeyed/reject-last.js | 46 ++++++++ .../Promise/allKeyed/reject-second.js | 46 ++++++++ .../allKeyed/resolve-before-loop-exit.js | 79 +++++++++++++ .../capability-executor-not-callable.js | 107 ++++++++++++++++++ .../allSettledKeyed/ctx-ctor-constructed.js | 35 ++++++ .../allSettledKeyed/ctx-ctor-throws.js | 28 +++++ .../getownproperty-not-enumerable.js | 50 ++++++++ .../getownproperty-returns-undefined.js | 35 ++++++ .../allSettledKeyed/getownproperty-throws.js | 42 +++++++ .../invoke-resolve-error-reject.js | 39 +++++++ .../invoke-resolve-get-error-reject.js | 37 ++++++ .../invoke-then-error-reject.js | 39 +++++++ .../invoke-then-get-error-reject.js | 39 +++++++ .../Promise/allSettledKeyed/ownkeys-throws.js | 37 ++++++ .../resolve-before-loop-exit.js | 82 ++++++++++++++ 26 files changed, 1227 insertions(+) create mode 100644 test/built-ins/Promise/allKeyed/capability-executor-not-callable.js create mode 100644 test/built-ins/Promise/allKeyed/ctx-ctor-constructed.js create mode 100644 test/built-ins/Promise/allKeyed/ctx-ctor-throws.js create mode 100644 test/built-ins/Promise/allKeyed/getownproperty-not-enumerable.js create mode 100644 test/built-ins/Promise/allKeyed/getownproperty-returns-undefined.js create mode 100644 test/built-ins/Promise/allKeyed/getownproperty-throws.js create mode 100644 test/built-ins/Promise/allKeyed/invoke-resolve-error-reject.js create mode 100644 test/built-ins/Promise/allKeyed/invoke-resolve-get-error-reject.js create mode 100644 test/built-ins/Promise/allKeyed/invoke-then-error-reject.js create mode 100644 test/built-ins/Promise/allKeyed/invoke-then-get-error-reject.js create mode 100644 test/built-ins/Promise/allKeyed/ownkeys-throws.js create mode 100644 test/built-ins/Promise/allKeyed/reject-last.js create mode 100644 test/built-ins/Promise/allKeyed/reject-second.js create mode 100644 test/built-ins/Promise/allKeyed/resolve-before-loop-exit.js create mode 100644 test/built-ins/Promise/allSettledKeyed/capability-executor-not-callable.js create mode 100644 test/built-ins/Promise/allSettledKeyed/ctx-ctor-constructed.js create mode 100644 test/built-ins/Promise/allSettledKeyed/ctx-ctor-throws.js create mode 100644 test/built-ins/Promise/allSettledKeyed/getownproperty-not-enumerable.js create mode 100644 test/built-ins/Promise/allSettledKeyed/getownproperty-returns-undefined.js create mode 100644 test/built-ins/Promise/allSettledKeyed/getownproperty-throws.js create mode 100644 test/built-ins/Promise/allSettledKeyed/invoke-resolve-error-reject.js create mode 100644 test/built-ins/Promise/allSettledKeyed/invoke-resolve-get-error-reject.js create mode 100644 test/built-ins/Promise/allSettledKeyed/invoke-then-error-reject.js create mode 100644 test/built-ins/Promise/allSettledKeyed/invoke-then-get-error-reject.js create mode 100644 test/built-ins/Promise/allSettledKeyed/ownkeys-throws.js create mode 100644 test/built-ins/Promise/allSettledKeyed/resolve-before-loop-exit.js diff --git a/test/built-ins/Promise/allKeyed/capability-executor-not-callable.js b/test/built-ins/Promise/allKeyed/capability-executor-not-callable.js new file mode 100644 index 00000000000..54af65cb3fe --- /dev/null +++ b/test/built-ins/Promise/allKeyed/capability-executor-not-callable.js @@ -0,0 +1,107 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allkeyed +description: > + Throws a TypeError if either resolve or reject capability is not callable. +info: | + Promise.allKeyed ( promises ) + + ... + 2. Let promiseCapability be ? NewPromiseCapability(C). + + NewPromiseCapability ( C ) + + ... + 4. Let executor be a new built-in function object as defined in + GetCapabilitiesExecutor Functions. + 5. Set the [[Capability]] internal slot of executor to promiseCapability. + 6. Let promise be ? Construct(C, « executor »). + ... + 8. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception. + 9. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception. +features: [await-dictionary] +---*/ + +var checkPoint = ""; +function fn1(executor) { + checkPoint += "a"; +} +Object.defineProperty(fn1, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allKeyed.call(fn1, {}); +}, "executor not called at all"); +assert.sameValue(checkPoint, "a", "executor not called at all"); + +checkPoint = ""; +function fn2(executor) { + checkPoint += "a"; + executor(); + checkPoint += "b"; +} +Object.defineProperty(fn2, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allKeyed.call(fn2, {}); +}, "executor called with no arguments"); +assert.sameValue(checkPoint, "ab", "executor called with no arguments"); + +checkPoint = ""; +function fn3(executor) { + checkPoint += "a"; + executor(undefined, undefined); + checkPoint += "b"; +} +Object.defineProperty(fn3, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allKeyed.call(fn3, {}); +}, "executor called with (undefined, undefined)"); +assert.sameValue(checkPoint, "ab", "executor called with (undefined, undefined)"); + +checkPoint = ""; +function fn4(executor) { + checkPoint += "a"; + executor(undefined, function() {}); + checkPoint += "b"; +} +Object.defineProperty(fn4, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allKeyed.call(fn4, {}); +}, "executor called with (undefined, function)"); +assert.sameValue(checkPoint, "ab", "executor called with (undefined, function)"); + +checkPoint = ""; +function fn5(executor) { + checkPoint += "a"; + executor(function() {}, undefined); + checkPoint += "b"; +} +Object.defineProperty(fn5, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allKeyed.call(fn5, {}); +}, "executor called with (function, undefined)"); +assert.sameValue(checkPoint, "ab", "executor called with (function, undefined)"); + +checkPoint = ""; +function fn6(executor) { + checkPoint += "a"; + executor(123, "invalid value"); + checkPoint += "b"; +} +Object.defineProperty(fn6, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allKeyed.call(fn6, {}); +}, "executor called with (Number, String)"); +assert.sameValue(checkPoint, "ab", "executor called with (Number, String)"); diff --git a/test/built-ins/Promise/allKeyed/ctx-ctor-constructed.js b/test/built-ins/Promise/allKeyed/ctx-ctor-constructed.js new file mode 100644 index 00000000000..f6a8c0e9d43 --- /dev/null +++ b/test/built-ins/Promise/allKeyed/ctx-ctor-constructed.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allkeyed +description: > + Promise.allKeyed invokes the constructor via Construct (not Call), passing + a single executor argument that is a function with length 2. +info: | + Promise.allKeyed ( promises ) + + 1. Let C be the this value. + 2. Let promiseCapability be ? NewPromiseCapability(C). + + NewPromiseCapability ( C ) + + ... + 6. Let promise be Construct(C, « executor »). +features: [await-dictionary] +---*/ + +var error = new Test262Error(); + +function Constructor(executor) { + if (new.target !== Constructor) { + throw error; + } + assert.sameValue(arguments.length, 1, "expected exactly one argument"); + assert.sameValue(typeof executor, "function", "executor is a function"); + assert.sameValue(executor.length, 2, "executor.length === 2"); + executor(function() {}, function() {}); +} +Constructor.resolve = function(v) { return v; }; + +Promise.allKeyed.call(Constructor, { a: 1 }); diff --git a/test/built-ins/Promise/allKeyed/ctx-ctor-throws.js b/test/built-ins/Promise/allKeyed/ctx-ctor-throws.js new file mode 100644 index 00000000000..9f4efb0f3ec --- /dev/null +++ b/test/built-ins/Promise/allKeyed/ctx-ctor-throws.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allkeyed +description: > + Promise.allKeyed invoked on a constructor value that throws an error +info: | + Promise.allKeyed ( promises ) + + 1. Let C be the this value. + 2. Let promiseCapability be ? NewPromiseCapability(C). + + NewPromiseCapability ( C ) + + ... + 6. Let promise be Construct(C, « executor »). + 7. ReturnIfAbrupt(promise). +features: [await-dictionary] +---*/ + +var CustomPromise = function() { + throw new Test262Error(); +}; + +assert.throws(Test262Error, function() { + Promise.allKeyed.call(CustomPromise, {}); +}); diff --git a/test/built-ins/Promise/allKeyed/getownproperty-not-enumerable.js b/test/built-ins/Promise/allKeyed/getownproperty-not-enumerable.js new file mode 100644 index 00000000000..07330e9c424 --- /dev/null +++ b/test/built-ins/Promise/allKeyed/getownproperty-not-enumerable.js @@ -0,0 +1,48 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Keys whose property descriptor has [[Enumerable]] false are skipped +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + a. Let desc be ? promises.[[GetOwnProperty]](key). + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var input = Object.create(null); +Object.defineProperty(input, "a", { + value: Promise.resolve(1), + enumerable: true, + configurable: true +}); +Object.defineProperty(input, "b", { + value: Promise.resolve(2), + enumerable: false, + configurable: true +}); +Object.defineProperty(input, "c", { + value: Promise.resolve(3), + enumerable: true, + configurable: true +}); + +asyncTest(function() { + return Promise.allKeyed(input).then(function(result) { + assert.sameValue(Object.getPrototypeOf(result), null, "result is null-prototype"); + var keys = Reflect.ownKeys(result); + assert.sameValue(keys.length, 2, "only enumerable keys are present"); + assert.sameValue(keys[0], "a", "first key"); + assert.sameValue(keys[1], "c", "second key"); + assert.sameValue(result.a, 1, "result.a"); + assert.sameValue(result.c, 3, "result.c"); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/getownproperty-returns-undefined.js b/test/built-ins/Promise/allKeyed/getownproperty-returns-undefined.js new file mode 100644 index 00000000000..6da1586b3ce --- /dev/null +++ b/test/built-ins/Promise/allKeyed/getownproperty-returns-undefined.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Keys where [[GetOwnProperty]] returns undefined are skipped +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + a. Let desc be ? promises.[[GetOwnProperty]](key). + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary, Proxy] +---*/ + +var input = new Proxy({ key: Promise.resolve(1) }, { + ownKeys: function() { + return ['key']; + }, + getOwnPropertyDescriptor: function() { + return undefined; + } +}); + +asyncTest(function() { + return Promise.allKeyed(input).then(function(result) { + assert.sameValue(Object.getPrototypeOf(result), null, "result is null-prototype"); + assert.sameValue(Reflect.ownKeys(result).length, 0); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/getownproperty-throws.js b/test/built-ins/Promise/allKeyed/getownproperty-throws.js new file mode 100644 index 00000000000..b84be770c7d --- /dev/null +++ b/test/built-ins/Promise/allKeyed/getownproperty-throws.js @@ -0,0 +1,42 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Rejects when [[GetOwnProperty]] on the promises object throws +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + a. Let desc be ? promises.[[GetOwnProperty]](key). + ... + + Promise.allKeyed ( promises ) + + ... + 6. Let result be Completion(PerformPromiseAllKeyed(...)). + 7. IfAbruptRejectPromise(result, promiseCapability). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary, Proxy] +---*/ + +var error = new Test262Error(); +var input = new Proxy({ key: 1 }, { + ownKeys: function() { + return ['key']; + }, + getOwnPropertyDescriptor: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allKeyed(input).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/invoke-resolve-error-reject.js b/test/built-ins/Promise/allKeyed/invoke-resolve-error-reject.js new file mode 100644 index 00000000000..33289e4bcbb --- /dev/null +++ b/test/built-ins/Promise/allKeyed/invoke-resolve-error-reject.js @@ -0,0 +1,39 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Calling promiseResolve throws, resulting in promise rejection +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + iv. Let nextPromise be ? Call(promiseResolve, constructor, « value »). + + Promise.allKeyed ( promises ) + + ... + 6. Let result be Completion(PerformPromiseAllKeyed(...)). + 7. IfAbruptRejectPromise(result, promiseCapability). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +Promise.resolve = function() { + throw error; +}; + +asyncTest(function() { + return Promise.allKeyed({ key: 1 }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/invoke-resolve-get-error-reject.js b/test/built-ins/Promise/allKeyed/invoke-resolve-get-error-reject.js new file mode 100644 index 00000000000..61805ad7ff8 --- /dev/null +++ b/test/built-ins/Promise/allKeyed/invoke-resolve-get-error-reject.js @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allkeyed +description: > + Error retrieving the constructor's `resolve` method rejects the promise +info: | + Promise.allKeyed ( promises ) + + ... + 3. Let promiseResolve be Completion(GetPromiseResolve(C)). + 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). + + GetPromiseResolve ( promiseConstructor ) + + 1. Let promiseResolve be ? Get(promiseConstructor, "resolve"). + ... +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +Object.defineProperty(Promise, 'resolve', { + get: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allKeyed({ key: 1 }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/invoke-then-error-reject.js b/test/built-ins/Promise/allKeyed/invoke-then-error-reject.js new file mode 100644 index 00000000000..d85f016aaed --- /dev/null +++ b/test/built-ins/Promise/allKeyed/invoke-then-error-reject.js @@ -0,0 +1,39 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Error thrown when invoking the instance's `then` method (rejecting Promise) +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + iv. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). + ... + 11. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +var promise = new Promise(function() {}); + +Object.defineProperty(promise, "then", { + value: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allKeyed({ key: promise }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/invoke-then-get-error-reject.js b/test/built-ins/Promise/allKeyed/invoke-then-get-error-reject.js new file mode 100644 index 00000000000..d0530d7f17e --- /dev/null +++ b/test/built-ins/Promise/allKeyed/invoke-then-get-error-reject.js @@ -0,0 +1,39 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Error thrown when accessing the instance's `then` method (rejecting Promise) +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + iv. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). + ... + 11. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +var promise = new Promise(function() {}); + +Object.defineProperty(promise, "then", { + get: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allKeyed({ key: promise }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/ownkeys-throws.js b/test/built-ins/Promise/allKeyed/ownkeys-throws.js new file mode 100644 index 00000000000..a0814d8a2f5 --- /dev/null +++ b/test/built-ins/Promise/allKeyed/ownkeys-throws.js @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Rejects when [[OwnPropertyKeys]] on the promises object throws +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + 1. Let allKeys be ? promises.[[OwnPropertyKeys]](). + ... + + Promise.allKeyed ( promises ) + + ... + 6. Let result be Completion(PerformPromiseAllKeyed(...)). + 7. IfAbruptRejectPromise(result, promiseCapability). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary, Proxy] +---*/ + +var error = new Test262Error(); +var input = new Proxy({}, { + ownKeys: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allKeyed(input).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/reject-last.js b/test/built-ins/Promise/allKeyed/reject-last.js new file mode 100644 index 00000000000..ea32775c3c4 --- /dev/null +++ b/test/built-ins/Promise/allKeyed/reject-last.js @@ -0,0 +1,46 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Promise.allKeyed rejects when the last promise to settle is rejected +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + 8. If variant is all, then + a. Let onRejected be resultCapability.[[Reject]]. + ... + 11. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +var resolveFirst, resolveSecond, rejectThird; + +var input = { + first: new Promise(function(resolve) { resolveFirst = resolve; }), + second: new Promise(function(resolve) { resolveSecond = resolve; }), + third: new Promise(function(_, reject) { rejectThird = reject; }) +}; + +var combined = Promise.allKeyed(input); + +resolveFirst('a'); +resolveSecond('b'); +rejectThird(error); + +asyncTest(function() { + return combined.then(function() { + throw new Test262Error('The promise should not be fulfilled.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/reject-second.js b/test/built-ins/Promise/allKeyed/reject-second.js new file mode 100644 index 00000000000..f4e923f61fc --- /dev/null +++ b/test/built-ins/Promise/allKeyed/reject-second.js @@ -0,0 +1,46 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Promise.allKeyed rejects when the second promise to settle is rejected +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + 8. If variant is all, then + a. Let onRejected be resultCapability.[[Reject]]. + ... + 11. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +var resolveFirst, rejectSecond, resolveThird; + +var input = { + first: new Promise(function(resolve) { resolveFirst = resolve; }), + second: new Promise(function(_, reject) { rejectSecond = reject; }), + third: new Promise(function(resolve) { resolveThird = resolve; }) +}; + +var combined = Promise.allKeyed(input); + +resolveFirst('a'); +rejectSecond(error); +resolveThird('c'); + +asyncTest(function() { + return combined.then(function() { + throw new Test262Error('The promise should not be fulfilled.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allKeyed/resolve-before-loop-exit.js b/test/built-ins/Promise/allKeyed/resolve-before-loop-exit.js new file mode 100644 index 00000000000..166943f1b17 --- /dev/null +++ b/test/built-ins/Promise/allKeyed/resolve-before-loop-exit.js @@ -0,0 +1,79 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Cannot tamper remainingElementsCount when resolve element functions are called + synchronously from thenables during the loop. +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 4. Let remainingElementsCount be the Record { [[Value]]: 1 }. + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + vi. Let onFulfilled be a new Abstract Closure ... + ... + 5. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + 6. If remainingElementsCount.[[Value]] = 0, then + ... + ... + 7. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + 8. If remainingElementsCount.[[Value]] = 0, then + ... + NOTE: This can happen even if keys was non-empty if an ill-behaved thenable + synchronously invoked the callback passed to its "then" method. +features: [await-dictionary] +---*/ + +var resolveStartCount = 0; +var resolveEndCount = 0; + +function Constructor(executor) { + function resolve(result) { + resolveStartCount += 1; + assert.sameValue(Object.getPrototypeOf(result), null, "result is null-prototype"); + var keys = Reflect.ownKeys(result); + assert.sameValue(keys.length, 3, "result has 3 keys"); + assert.sameValue(result.a, "a-fulfill", "result.a"); + assert.sameValue(result.b, "b-fulfill", "result.b"); + assert.sameValue(result.c, "c-fulfill", "result.c"); + resolveEndCount += 1; + } + executor(resolve, Test262Error.thrower); +} +Constructor.resolve = function(v) { + return v; +}; + +var aOnFulfilled; + +var input = { + a: { + then: function(onFulfilled, onRejected) { + aOnFulfilled = onFulfilled; + } + }, + b: { + then: function(onFulfilled, onRejected) { + aOnFulfilled("a-fulfill"); + onFulfilled("b-fulfill"); + } + }, + c: { + then: function(onFulfilled, onRejected) { + onFulfilled("c-fulfill"); + } + } +}; + +assert.sameValue(resolveStartCount, 0, "resolveStartCount before call to allKeyed()"); + +Promise.allKeyed.call(Constructor, input); + +assert.sameValue(resolveStartCount, 1, "resolve callback entered once"); +assert.sameValue(resolveEndCount, 1, "resolve callback completed once"); diff --git a/test/built-ins/Promise/allSettledKeyed/capability-executor-not-callable.js b/test/built-ins/Promise/allSettledKeyed/capability-executor-not-callable.js new file mode 100644 index 00000000000..b6c1c36ca5d --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/capability-executor-not-callable.js @@ -0,0 +1,107 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allsettledkeyed +description: > + Throws a TypeError if either resolve or reject capability is not callable. +info: | + Promise.allSettledKeyed ( promises ) + + ... + 2. Let promiseCapability be ? NewPromiseCapability(C). + + NewPromiseCapability ( C ) + + ... + 4. Let executor be a new built-in function object as defined in + GetCapabilitiesExecutor Functions. + 5. Set the [[Capability]] internal slot of executor to promiseCapability. + 6. Let promise be ? Construct(C, « executor »). + ... + 8. If IsCallable(promiseCapability.[[Resolve]]) is false, throw a TypeError exception. + 9. If IsCallable(promiseCapability.[[Reject]]) is false, throw a TypeError exception. +features: [await-dictionary] +---*/ + +var checkPoint = ""; +function fn1(executor) { + checkPoint += "a"; +} +Object.defineProperty(fn1, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allSettledKeyed.call(fn1, {}); +}, "executor not called at all"); +assert.sameValue(checkPoint, "a", "executor not called at all"); + +checkPoint = ""; +function fn2(executor) { + checkPoint += "a"; + executor(); + checkPoint += "b"; +} +Object.defineProperty(fn2, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allSettledKeyed.call(fn2, {}); +}, "executor called with no arguments"); +assert.sameValue(checkPoint, "ab", "executor called with no arguments"); + +checkPoint = ""; +function fn3(executor) { + checkPoint += "a"; + executor(undefined, undefined); + checkPoint += "b"; +} +Object.defineProperty(fn3, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allSettledKeyed.call(fn3, {}); +}, "executor called with (undefined, undefined)"); +assert.sameValue(checkPoint, "ab", "executor called with (undefined, undefined)"); + +checkPoint = ""; +function fn4(executor) { + checkPoint += "a"; + executor(undefined, function() {}); + checkPoint += "b"; +} +Object.defineProperty(fn4, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allSettledKeyed.call(fn4, {}); +}, "executor called with (undefined, function)"); +assert.sameValue(checkPoint, "ab", "executor called with (undefined, function)"); + +checkPoint = ""; +function fn5(executor) { + checkPoint += "a"; + executor(function() {}, undefined); + checkPoint += "b"; +} +Object.defineProperty(fn5, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allSettledKeyed.call(fn5, {}); +}, "executor called with (function, undefined)"); +assert.sameValue(checkPoint, "ab", "executor called with (function, undefined)"); + +checkPoint = ""; +function fn6(executor) { + checkPoint += "a"; + executor(123, "invalid value"); + checkPoint += "b"; +} +Object.defineProperty(fn6, 'resolve', { + get() { throw new Test262Error(); } +}); +assert.throws(TypeError, function() { + Promise.allSettledKeyed.call(fn6, {}); +}, "executor called with (Number, String)"); +assert.sameValue(checkPoint, "ab", "executor called with (Number, String)"); diff --git a/test/built-ins/Promise/allSettledKeyed/ctx-ctor-constructed.js b/test/built-ins/Promise/allSettledKeyed/ctx-ctor-constructed.js new file mode 100644 index 00000000000..1b960733b7b --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/ctx-ctor-constructed.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allsettledkeyed +description: > + Promise.allSettledKeyed invokes the constructor via Construct (not Call), + passing a single executor argument that is a function with length 2. +info: | + Promise.allSettledKeyed ( promises ) + + 1. Let C be the this value. + 2. Let promiseCapability be ? NewPromiseCapability(C). + + NewPromiseCapability ( C ) + + ... + 6. Let promise be Construct(C, « executor »). +features: [await-dictionary] +---*/ + +var error = new Test262Error(); + +function Constructor(executor) { + if (new.target !== Constructor) { + throw error; + } + assert.sameValue(arguments.length, 1, "expected exactly one argument"); + assert.sameValue(typeof executor, "function", "executor is a function"); + assert.sameValue(executor.length, 2, "executor.length === 2"); + executor(function() {}, function() {}); +} +Constructor.resolve = function(v) { return v; }; + +Promise.allSettledKeyed.call(Constructor, { a: 1 }); diff --git a/test/built-ins/Promise/allSettledKeyed/ctx-ctor-throws.js b/test/built-ins/Promise/allSettledKeyed/ctx-ctor-throws.js new file mode 100644 index 00000000000..e099c9a3783 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/ctx-ctor-throws.js @@ -0,0 +1,28 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allsettledkeyed +description: > + Promise.allSettledKeyed invoked on a constructor value that throws an error +info: | + Promise.allSettledKeyed ( promises ) + + 1. Let C be the this value. + 2. Let promiseCapability be ? NewPromiseCapability(C). + + NewPromiseCapability ( C ) + + ... + 6. Let promise be Construct(C, « executor »). + 7. ReturnIfAbrupt(promise). +features: [await-dictionary] +---*/ + +var CustomPromise = function() { + throw new Test262Error(); +}; + +assert.throws(Test262Error, function() { + Promise.allSettledKeyed.call(CustomPromise, {}); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/getownproperty-not-enumerable.js b/test/built-ins/Promise/allSettledKeyed/getownproperty-not-enumerable.js new file mode 100644 index 00000000000..1c8018ddbd7 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/getownproperty-not-enumerable.js @@ -0,0 +1,50 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Keys whose property descriptor has [[Enumerable]] false are skipped +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + a. Let desc be ? promises.[[GetOwnProperty]](key). + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var input = Object.create(null); +Object.defineProperty(input, "a", { + value: Promise.resolve(1), + enumerable: true, + configurable: true +}); +Object.defineProperty(input, "b", { + value: Promise.resolve(2), + enumerable: false, + configurable: true +}); +Object.defineProperty(input, "c", { + value: Promise.resolve(3), + enumerable: true, + configurable: true +}); + +asyncTest(function() { + return Promise.allSettledKeyed(input).then(function(result) { + assert.sameValue(Object.getPrototypeOf(result), null, "result is null-prototype"); + var keys = Reflect.ownKeys(result); + assert.sameValue(keys.length, 2, "only enumerable keys are present"); + assert.sameValue(keys[0], "a", "first key"); + assert.sameValue(keys[1], "c", "second key"); + assert.sameValue(result.a.status, "fulfilled", "result.a.status"); + assert.sameValue(result.a.value, 1, "result.a.value"); + assert.sameValue(result.c.status, "fulfilled", "result.c.status"); + assert.sameValue(result.c.value, 3, "result.c.value"); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/getownproperty-returns-undefined.js b/test/built-ins/Promise/allSettledKeyed/getownproperty-returns-undefined.js new file mode 100644 index 00000000000..90268e92b83 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/getownproperty-returns-undefined.js @@ -0,0 +1,35 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Keys where [[GetOwnProperty]] returns undefined are skipped +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + a. Let desc be ? promises.[[GetOwnProperty]](key). + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary, Proxy] +---*/ + +var input = new Proxy({ key: Promise.resolve(1) }, { + ownKeys: function() { + return ['key']; + }, + getOwnPropertyDescriptor: function() { + return undefined; + } +}); + +asyncTest(function() { + return Promise.allSettledKeyed(input).then(function(result) { + assert.sameValue(Object.getPrototypeOf(result), null, "result is null-prototype"); + assert.sameValue(Reflect.ownKeys(result).length, 0); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/getownproperty-throws.js b/test/built-ins/Promise/allSettledKeyed/getownproperty-throws.js new file mode 100644 index 00000000000..b174d16c8ae --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/getownproperty-throws.js @@ -0,0 +1,42 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Rejects when [[GetOwnProperty]] on the promises object throws +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + a. Let desc be ? promises.[[GetOwnProperty]](key). + ... + + Promise.allSettledKeyed ( promises ) + + ... + 6. Let result be Completion(PerformPromiseAllKeyed(...)). + 7. IfAbruptRejectPromise(result, promiseCapability). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary, Proxy] +---*/ + +var error = new Test262Error(); +var input = new Proxy({ key: 1 }, { + ownKeys: function() { + return ['key']; + }, + getOwnPropertyDescriptor: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allSettledKeyed(input).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/invoke-resolve-error-reject.js b/test/built-ins/Promise/allSettledKeyed/invoke-resolve-error-reject.js new file mode 100644 index 00000000000..d7d53ffbd98 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/invoke-resolve-error-reject.js @@ -0,0 +1,39 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Calling promiseResolve throws, resulting in promise rejection +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + iv. Let nextPromise be ? Call(promiseResolve, constructor, « value »). + + Promise.allSettledKeyed ( promises ) + + ... + 6. Let result be Completion(PerformPromiseAllKeyed(...)). + 7. IfAbruptRejectPromise(result, promiseCapability). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +Promise.resolve = function() { + throw error; +}; + +asyncTest(function() { + return Promise.allSettledKeyed({ key: 1 }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/invoke-resolve-get-error-reject.js b/test/built-ins/Promise/allSettledKeyed/invoke-resolve-get-error-reject.js new file mode 100644 index 00000000000..4fc59bd6ae1 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/invoke-resolve-get-error-reject.js @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.allsettledkeyed +description: > + Error retrieving the constructor's `resolve` method rejects the promise +info: | + Promise.allSettledKeyed ( promises ) + + ... + 3. Let promiseResolve be Completion(GetPromiseResolve(C)). + 4. IfAbruptRejectPromise(promiseResolve, promiseCapability). + + GetPromiseResolve ( promiseConstructor ) + + 1. Let promiseResolve be ? Get(promiseConstructor, "resolve"). + ... +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +Object.defineProperty(Promise, 'resolve', { + get: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allSettledKeyed({ key: 1 }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/invoke-then-error-reject.js b/test/built-ins/Promise/allSettledKeyed/invoke-then-error-reject.js new file mode 100644 index 00000000000..7f5f89a72e9 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/invoke-then-error-reject.js @@ -0,0 +1,39 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Error thrown when invoking the instance's `then` method (rejecting Promise) +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + iv. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). + ... + 11. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +var promise = new Promise(function() {}); + +Object.defineProperty(promise, "then", { + value: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allSettledKeyed({ key: promise }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/invoke-then-get-error-reject.js b/test/built-ins/Promise/allSettledKeyed/invoke-then-get-error-reject.js new file mode 100644 index 00000000000..fdefaa625f3 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/invoke-then-get-error-reject.js @@ -0,0 +1,39 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Error thrown when accessing the instance's `then` method (rejecting Promise) +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + iv. Let nextPromise be ? Call(promiseResolve, constructor, « nextValue »). + ... + 11. Perform ? Invoke(nextPromise, "then", « onFulfilled, onRejected »). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary] +---*/ + +var error = new Test262Error(); +var promise = new Promise(function() {}); + +Object.defineProperty(promise, "then", { + get: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allSettledKeyed({ key: promise }).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/ownkeys-throws.js b/test/built-ins/Promise/allSettledKeyed/ownkeys-throws.js new file mode 100644 index 00000000000..b9bd51a41c3 --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/ownkeys-throws.js @@ -0,0 +1,37 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Rejects when [[OwnPropertyKeys]] on the promises object throws +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + 1. Let allKeys be ? promises.[[OwnPropertyKeys]](). + ... + + Promise.allSettledKeyed ( promises ) + + ... + 6. Let result be Completion(PerformPromiseAllKeyed(...)). + 7. IfAbruptRejectPromise(result, promiseCapability). +includes: [asyncHelpers.js] +flags: [async] +features: [await-dictionary, Proxy] +---*/ + +var error = new Test262Error(); +var input = new Proxy({}, { + ownKeys: function() { + throw error; + } +}); + +asyncTest(function() { + return Promise.allSettledKeyed(input).then(function() { + throw new Test262Error('The promise should be rejected.'); + }, function(reason) { + assert.sameValue(reason, error); + }); +}); diff --git a/test/built-ins/Promise/allSettledKeyed/resolve-before-loop-exit.js b/test/built-ins/Promise/allSettledKeyed/resolve-before-loop-exit.js new file mode 100644 index 00000000000..bc4e4b88d1e --- /dev/null +++ b/test/built-ins/Promise/allSettledKeyed/resolve-before-loop-exit.js @@ -0,0 +1,82 @@ +// Copyright (C) 2026 Danial Asaria (Bloomberg LP). All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-performpromiseallkeyed +description: > + Cannot tamper remainingElementsCount when resolve element functions are called + synchronously from thenables during the loop. +info: | + PerformPromiseAllKeyed ( variant, promises, constructor, resultCapability, promiseResolve ) + + ... + 4. Let remainingElementsCount be the Record { [[Value]]: 1 }. + ... + 6. For each element key of allKeys, do + ... + b. If desc is not undefined and desc.[[Enumerable]] is true, then + ... + vi. Let onFulfilled be a new Abstract Closure ... + ... + 5. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + 6. If remainingElementsCount.[[Value]] = 0, then + ... + ... + 7. Set remainingElementsCount.[[Value]] to remainingElementsCount.[[Value]] - 1. + 8. If remainingElementsCount.[[Value]] = 0, then + ... + NOTE: This can happen even if keys was non-empty if an ill-behaved thenable + synchronously invoked the callback passed to its "then" method. +features: [await-dictionary] +---*/ + +var resolveStartCount = 0; +var resolveEndCount = 0; + +function Constructor(executor) { + function resolve(result) { + resolveStartCount += 1; + assert.sameValue(Object.getPrototypeOf(result), null, "result is null-prototype"); + var keys = Reflect.ownKeys(result); + assert.sameValue(keys.length, 3, "result has 3 keys"); + assert.sameValue(result.a.status, "fulfilled", "result.a.status"); + assert.sameValue(result.a.value, "a-fulfill", "result.a.value"); + assert.sameValue(result.b.status, "fulfilled", "result.b.status"); + assert.sameValue(result.b.value, "b-fulfill", "result.b.value"); + assert.sameValue(result.c.status, "fulfilled", "result.c.status"); + assert.sameValue(result.c.value, "c-fulfill", "result.c.value"); + resolveEndCount += 1; + } + executor(resolve, Test262Error.thrower); +} +Constructor.resolve = function(v) { + return v; +}; + +var aOnFulfilled; + +var input = { + a: { + then: function(onFulfilled, onRejected) { + aOnFulfilled = onFulfilled; + } + }, + b: { + then: function(onFulfilled, onRejected) { + aOnFulfilled("a-fulfill"); + onFulfilled("b-fulfill"); + } + }, + c: { + then: function(onFulfilled, onRejected) { + onFulfilled("c-fulfill"); + } + } +}; + +assert.sameValue(resolveStartCount, 0, "resolveStartCount before call to allSettledKeyed()"); + +Promise.allSettledKeyed.call(Constructor, input); + +assert.sameValue(resolveStartCount, 1, "resolve callback entered once"); +assert.sameValue(resolveEndCount, 1, "resolve callback completed once");