diff --git a/test/built-ins/Promise/any/call-reject-element-after-return.js b/test/built-ins/Promise/any/call-reject-element-after-return.js index 822df3fdc7..03b09e0e2e 100644 --- a/test/built-ins/Promise/any/call-reject-element-after-return.js +++ b/test/built-ins/Promise/any/call-reject-element-after-return.js @@ -19,16 +19,10 @@ let callCount = 0; let errorArray; function Constructor(executor) { - function reject(error) { - callCount += 1; + executor($ERROR, (error) => { + callCount++; errorArray = error.errors; - - assert(Array.isArray(error.errors), "error is array"); - assert.sameValue(error.errors.length, 1, "error.length"); - assert.sameValue(error.errors[0], "onRejectedValue", "error[0]"); - assert(error instanceof AggregateError, "error instanceof AggregateError"); - } - executor($ERROR, reject); + }); } Constructor.resolve = function(v) { return v; @@ -52,5 +46,4 @@ assert.sameValue(errorArray[0], "onRejectedValue", "errorArray after call to any p1OnRejected("unexpectedonRejectedValue"); -assert.sameValue(callCount, 1, "callCount after call to onRejected()"); -assert.sameValue(errorArray[0], "onRejectedValue", "errorArray after call to onRejected()"); +assert.sameValue(errorArray[0], "onRejectedValue", "errorArray[0] === 'onRejectedValue', after call to p1OnRejected()"); diff --git a/test/built-ins/Promise/any/iter-arg-is-error-object-reject.js b/test/built-ins/Promise/any/iter-arg-is-error-object-reject.js new file mode 100644 index 0000000000..492e9cf40c --- /dev/null +++ b/test/built-ins/Promise/any/iter-arg-is-error-object-reject.js @@ -0,0 +1,44 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Promise.any(new Test262Error()) rejects with TypeError. +info: | + Promise.any ( iterable ) + + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... + + GetMethod + + 2. Let func be ? GetV(V, P). + 3. If func is either undefined or null, return undefined. + 4. If IsCallable(func) is false, throw a TypeError exception. + + Call ( F, V [ , argumentsList ] ) + + 2. If IsCallable(F) is false, throw a TypeError exception. +features: [Promise.any] +flags: [async] +---*/ + +try { + Promise.any(new Test262Error()).then(() => { + $DONE('The promise should be rejected, but was resolved'); + }, (error) => { + assert(error instanceof TypeError); + }).then($DONE, $DONE); +} catch (error) { + $DONE(`The promise should be rejected, but threw an exception: ${error.message}`); +} diff --git a/test/built-ins/Promise/any/iter-next-val-err-no-close.js b/test/built-ins/Promise/any/iter-next-val-err-no-close.js new file mode 100644 index 0000000000..8304ba5eef --- /dev/null +++ b/test/built-ins/Promise/any/iter-next-val-err-no-close.js @@ -0,0 +1,63 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Error when accessing an iterator result's `value` property (not closing + iterator) +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + If result is an abrupt completion, then + If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result). + IfAbruptRejectPromise(result, promiseCapability). + + ... + + Runtime Semantics: PerformPromiseAny + + ... + Repeat + Let nextValue be IteratorValue(next). + If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. + ReturnIfAbrupt(nextValue). + +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ +let callCount = 0; +let returnCount = 0; +let error = new Test262Error(); +let poisoned = { + done: false +}; +Object.defineProperty(poisoned, 'value', { + get() { + callCount++; + throw error; + } +}); +let iterNextValThrows = { + [Symbol.iterator]() { + callCount++; + return { + next() { + callCount++; + return poisoned; + }, + return() { + returnCount++; + return {}; + } + }; + } +}; + +Promise.any(iterNextValThrows).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (reason) => { + assert(error instanceof Test262Error); + assert.sameValue(reason, error); + assert.sameValue(callCount, 3, 'callCount === 3'); + assert.sameValue(returnCount, 0); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-next-val-err-reject.js b/test/built-ins/Promise/any/iter-next-val-err-reject.js new file mode 100644 index 0000000000..cf434ba6ac --- /dev/null +++ b/test/built-ins/Promise/any/iter-next-val-err-reject.js @@ -0,0 +1,57 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Error when accessing an iterator result's `value` property (rejecting + promise) +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + If result is an abrupt completion, then + If iteratorRecord.[[Done]] is false, set result to IteratorClose(iteratorRecord, result). + IfAbruptRejectPromise(result, promiseCapability). + + ... + + Runtime Semantics: PerformPromiseAny + + ... + Repeat + Let nextValue be IteratorValue(next). + If nextValue is an abrupt completion, set iteratorRecord.[[Done]] to true. + ReturnIfAbrupt(nextValue). + +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ +let callCount = 0; +let error = new Test262Error(); +let poisoned = { + done: false +}; +Object.defineProperty(poisoned, 'value', { + get() { + callCount++; + throw error; + } +}); +let iterNextValThrows = { + [Symbol.iterator]() { + callCount++; + return { + next() { + callCount++; + return poisoned; + } + }; + } +}; + +Promise.any(iterNextValThrows).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (reason) => { + assert(error instanceof Test262Error); + assert.sameValue(reason, error); + assert.sameValue(callCount, 3, 'callCount === 3'); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-false-reject.js b/test/built-ins/Promise/any/iter-returns-false-reject.js new file mode 100644 index 0000000000..cc9a5ab218 --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-false-reject.js @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns false +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... + +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return false; + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-null-reject.js b/test/built-ins/Promise/any/iter-returns-null-reject.js new file mode 100644 index 0000000000..db4b288d4d --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-null-reject.js @@ -0,0 +1,36 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns null +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... + +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return null; + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-number-reject.js b/test/built-ins/Promise/any/iter-returns-number-reject.js new file mode 100644 index 0000000000..e78689d9f1 --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-number-reject.js @@ -0,0 +1,35 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns number +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return 1; + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-string-reject.js b/test/built-ins/Promise/any/iter-returns-string-reject.js new file mode 100644 index 0000000000..c16c863507 --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-string-reject.js @@ -0,0 +1,35 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns string +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return ''; + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-symbol-reject.js b/test/built-ins/Promise/any/iter-returns-symbol-reject.js new file mode 100644 index 0000000000..0f96fd980d --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-symbol-reject.js @@ -0,0 +1,35 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns symbol +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... +features: [Promise.any, Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return Symbol(); + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-true-reject.js b/test/built-ins/Promise/any/iter-returns-true-reject.js new file mode 100644 index 0000000000..1488a7239a --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-true-reject.js @@ -0,0 +1,35 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns true +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... +features: [Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return true; + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/iter-returns-undefined-reject.js b/test/built-ins/Promise/any/iter-returns-undefined-reject.js new file mode 100644 index 0000000000..3845e04969 --- /dev/null +++ b/test/built-ins/Promise/any/iter-returns-undefined-reject.js @@ -0,0 +1,35 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Reject when argument's Symbol.iterator returns undefined +info: | + ... + Let iteratorRecord be GetIterator(iterable). + IfAbruptRejectPromise(iteratorRecord, promiseCapability). + ... + + GetIterator ( obj [ , hint [ , method ] ] ) + + ... + Let iterator be ? Call(method, obj). + If Type(iterator) is not Object, throw a TypeError exception. + ... +features: [Symbol.iterator] +flags: [async] +---*/ + +let callCount = 0; +Promise.any({ + [Symbol.iterator]() { + callCount++; + return undefined; + } +}).then(() => { + $DONE('The promise should be rejected, but was resolved'); +}, (error) => { + assert.sameValue(callCount, 1, 'callCount === 1'); + assert(error instanceof TypeError); +}).then($DONE, $DONE); diff --git a/test/built-ins/Promise/any/reject-ignored-deferred.js b/test/built-ins/Promise/any/reject-ignored-deferred.js new file mode 100644 index 0000000000..e13ade1109 --- /dev/null +++ b/test/built-ins/Promise/any/reject-ignored-deferred.js @@ -0,0 +1,68 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-promise.any +description: > + Resolved promises ignore rejections through deferred invocation of the + provided resolving function +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + + Runtime Semantics: PerformPromiseAny + + ... + Let remainingElementsCount be a new Record { [[value]]: 1 }. + ... + 8.d ... + ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1. + iii. If remainingElementsCount.[[value]] is 0, + 1. Let error be a newly created AggregateError object. + 2. Perform ! DefinePropertyOrThrow(error, "errors", Property Descriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: errors }). + 3. Return ThrowCompletion(error). + ... + + Promise.any Reject Element Functions + ... + Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot. + If alreadyCalled.[[value]] is true, return undefined. + Set alreadyCalled.[[value]] to true. + ... + +flags: [async] +features: [Promise.any, arrow-function] +---*/ + +let callCount = 0; +let fulfiller = { + then(resolve) { + new Promise((resolve) => { + callCount++; + resolve(); + }) + .then(() => { + callCount++; + resolve(); + }); + } +}; +let rejector = { + then(resolve, reject) { + new Promise((resolve) => { + callCount++; + resolve(); + }) + .then(() => { + callCount++; + resolve(); + reject(); + }); + } +}; + +Promise.all([fulfiller, rejector]) + .then(() => { + assert.sameValue(callCount, 4, "callCount === 4"); + $DONE(); + }, () => { + $DONE("The promise should not be rejected."); + }); diff --git a/test/built-ins/Promise/any/reject-ignored-immed.js b/test/built-ins/Promise/any/reject-ignored-immed.js new file mode 100644 index 0000000000..9756cf17e3 --- /dev/null +++ b/test/built-ins/Promise/any/reject-ignored-immed.js @@ -0,0 +1,52 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-promise.any +description: > + Resolved promises ignore rejections through immediate invocation of the + provided resolving function +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + + Runtime Semantics: PerformPromiseAny + + ... + Let remainingElementsCount be a new Record { [[value]]: 1 }. + ... + 8.d ... + ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1. + iii. If remainingElementsCount.[[value]] is 0, + 1. Let error be a newly created AggregateError object. + 2. Perform ! DefinePropertyOrThrow(error, "errors", Property Descriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: errors }). + 3. Return ThrowCompletion(error). + ... + + Promise.any Reject Element Functions + ... + Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot. + If alreadyCalled.[[value]] is true, return undefined. + Set alreadyCalled.[[value]] to true. + ... + +flags: [async] +features: [Promise.any, arrow-function] +---*/ + +let fulfiller = { + then(resolve) { + resolve(); + } +}; +let lateRejector = { + then(resolve, reject) { + resolve(); + reject(); + } +}; + +Promise.any([fulfiller, lateRejector]) + .then(() => { + $DONE(); + }, () => { + $DONE('The promise should not be rejected.'); + }); diff --git a/test/built-ins/Promise/any/reject-immed.js b/test/built-ins/Promise/any/reject-immed.js new file mode 100644 index 0000000000..7015f6d14c --- /dev/null +++ b/test/built-ins/Promise/any/reject-immed.js @@ -0,0 +1,44 @@ +// Copyright (C) 2016 the V8 project authors. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +esid: sec-promise.any +description: Rejecting through immediate invocation of the provided resolving function +info: | + ... + Let promiseCapability be NewPromiseCapability(C). + ... + Let result be PerformPromiseAny(iteratorRecord, promiseCapability, C). + ... + + Runtime Semantics: PerformPromiseAny + ... + 8. Repeat + ... + r. Perform ? Invoke(nextPromise, "then", + « resultCapability.[[Resolve]], rejectElement »). + + + Promise.any Reject Element Functions + ... + 6. Return RejectPromise(promise, reason). +flags: [async] +features: [Promise.any, arrow-function] +---*/ + +let callCount = 0; +let thenable = { + then(_, reject) { + callCount++; + reject('reason'); + } +}; + +Promise.any([thenable]) + .then(() => { + $DONE('The promise should not be fulfilled.'); + }, (error) => { + assert.sameValue(callCount, 1, "callCount === 1"); + assert(error instanceof AggregateError, "error instanceof AggregateError"); + assert.sameValue(error.errors[0], "reason", "error.errors[0] === 'reason'"); + $DONE(); + }); diff --git a/test/built-ins/Promise/any/resolve-before-loop-exit-from-same.js b/test/built-ins/Promise/any/resolve-before-loop-exit-from-same.js new file mode 100644 index 0000000000..f9af8b9ed7 --- /dev/null +++ b/test/built-ins/Promise/any/resolve-before-loop-exit-from-same.js @@ -0,0 +1,99 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Cannot tamper remainingElementsCount when Promise.all resolve element function is called twice in a row. +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + + Runtime Semantics: PerformPromiseAny + + ... + Let remainingElementsCount be a new Record { [[value]]: 1 }. + ... + 8.d ... + ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1. + iii. If remainingElementsCount.[[value]] is 0, + 1. Let error be a newly created AggregateError object. + 2. Perform ! DefinePropertyOrThrow(error, "errors", Property Descriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: errors }). + 3. Return ThrowCompletion(error). + ... + + Promise.any Reject Element Functions + ... + Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot. + If alreadyCalled.[[value]] is true, return undefined. + Set alreadyCalled.[[value]] to true. + ... + +features: [Promise.any, arrow-function] +---*/ + +let callCount = 0; +let errorArray; + +function Constructor(executor) { + function reject(error) { + callCount += 1; + errorArray = error.errors; + + assert(Array.isArray(error.errors), "error is array"); + assert.sameValue(error.errors.length, 3, "error.length"); + assert.sameValue(error.errors[0], "p1-rejection", "error.errors[0] === 'p1-rejection'"); + assert.sameValue(error.errors[1], "p2-rejection", "error.errors[1] === 'p2-rejection'"); + assert.sameValue(error.errors[2], "p3-rejection", "error.errors[2] === 'p3-rejection'"); + assert(error instanceof AggregateError, "error instanceof AggregateError"); + } + executor($ERROR, reject); +} +Constructor.resolve = function(v) { + return v; +}; + +let p1OnRejected; + +let p1 = { + then(onResolved, onRejected) { + p1OnRejected = onRejected; + } +}; +let p2 = { + then(onResolved, onRejected) { + onRejected("p2-rejection"); + onRejected("unexpectedonRejectedValue"); + } +}; +let p3 = { + then(onResolved, onRejected) { + onRejected("p3-rejection"); + } +}; + +assert.sameValue(callCount, 0, "callCount before call to any()"); + +Promise.any.call(Constructor, [p1, p2, p3]); + +assert.sameValue(callCount, 0, "callCount after call to any()"); + +p1OnRejected("p1-rejection"); + +assert.sameValue(callCount, 1, "callCount after call to p1OnRejected()"); +assert.sameValue( + errorArray[0], + "p1-rejection", + "errorArray[0] === 'p1-rejection', after call to p1OnRejected(...)" +); +assert.sameValue( + errorArray[1], + "p2-rejection", + "errorArray[1] === 'p2-rejection', after call to p1OnRejected(...)" +); +assert.sameValue( + errorArray[2], + "p3-rejection", + "errorArray[2] === 'p3-rejection', after call to p1OnRejected(...)" +); + + diff --git a/test/built-ins/Promise/any/resolve-before-loop-exit.js b/test/built-ins/Promise/any/resolve-before-loop-exit.js new file mode 100644 index 0000000000..00542d2880 --- /dev/null +++ b/test/built-ins/Promise/any/resolve-before-loop-exit.js @@ -0,0 +1,100 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + Cannot tamper remainingElementsCount when two Promise.any Reject Element Function are called in succession. +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + + Runtime Semantics: PerformPromiseAny + + ... + Let remainingElementsCount be a new Record { [[value]]: 1 }. + ... + 8.d ... + ii. Set remainingElementsCount.[[value]] to remainingElementsCount.[[value]] − 1. + iii. If remainingElementsCount.[[value]] is 0, + 1. Let error be a newly created AggregateError object. + 2. Perform ! DefinePropertyOrThrow(error, "errors", Property Descriptor { [[Configurable]]: true, [[Enumerable]]: false, [[Writable]]: true, [[Value]]: errors }). + 3. Return ThrowCompletion(error). + ... + + Promise.any Reject Element Functions + ... + Let alreadyCalled be the value of F's [[AlreadyCalled]] internal slot. + If alreadyCalled.[[value]] is true, return undefined. + Set alreadyCalled.[[value]] to true. + ... + +features: [Promise.any, arrow-function] +---*/ + +let callCount = 0; +let errorArray; + +function Constructor(executor) { + function reject(error) { + callCount += 1; + errorArray = error.errors; + + assert(Array.isArray(error.errors), "error is array"); + assert.sameValue(error.errors.length, 3, "error.length"); + assert.sameValue(error.errors[0], "p1-rejection", "error.errors[0] === 'p1-rejection'"); + assert.sameValue(error.errors[1], "p2-rejection", "error.errors[1] === 'p2-rejection'"); + assert.sameValue(error.errors[2], "p3-rejection", "error.errors[2] === 'p3-rejection'"); + assert(error instanceof AggregateError, "error instanceof AggregateError"); + } + executor($ERROR, reject); +} +Constructor.resolve = function(v) { + return v; +}; + +let p1OnRejected; + +let p1 = { + then(onResolved, onRejected) { + p1OnRejected = onRejected; + } +}; +let p2 = { + then(onResolved, onRejected) { + p1OnRejected("p1-rejection"); + onRejected("p2-rejection"); + } +}; +let p3 = { + then(onResolved, onRejected) { + onRejected("p3-rejection"); + } +}; + +assert.sameValue(callCount, 0, "callCount before call to any()"); + +Promise.any.call(Constructor, [p1, p2, p3]); + +assert.sameValue(callCount, 1, "callCount after call to any()"); +assert.sameValue(errorArray[0], "p1-rejection", "errorArray[0] === 'p1-rejection'"); +assert.sameValue(errorArray[1], "p2-rejection", "errorArray[1] === 'p2-rejection'"); +assert.sameValue(errorArray[2], "p3-rejection", "errorArray[2] === 'p3-rejection'"); + +p1OnRejected("unexpectedonRejectedValue"); + +assert.sameValue(callCount, 1, "callCount after call to p1OnRejected()"); +assert.sameValue( + errorArray[0], + "p1-rejection", + "errorArray[0] === 'p1-rejection', after call to p1OnRejected(...)" +); +assert.sameValue( + errorArray[1], + "p2-rejection", + "errorArray[1] === 'p2-rejection', after call to p1OnRejected(...)" +); +assert.sameValue( + errorArray[2], + "p3-rejection", + "errorArray[2] === 'p3-rejection', after call to p1OnRejected(...)" +); diff --git a/test/built-ins/Promise/any/resolve-not-callable-reject-with-typeerror.js b/test/built-ins/Promise/any/resolve-not-callable-reject-with-typeerror.js new file mode 100644 index 0000000000..b7e4ca8abe --- /dev/null +++ b/test/built-ins/Promise/any/resolve-not-callable-reject-with-typeerror.js @@ -0,0 +1,28 @@ +// Copyright (C) 2020 Rick Waldron. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. + +/*--- +esid: sec-promise.any +description: > + If the constructor's `resolve` method is not callable, reject with a TypeError. +info: | + Let result be PerformPromiseAny(iteratorRecord, C, promiseCapability). + + Runtime Semantics: PerformPromiseAny + + Let promiseResolve be ? Get(constructor, "resolve"). + If ! IsCallable(promiseResolve) is false, throw a TypeError exception. + +flags: [async] +features: [Promise.any, arrow-function] +---*/ + +Promise.resolve = null; + +Promise.any([1]) + .then( + () => $DONE('The promise should not be resolved.'), + error => { + assert(error instanceof TypeError); + } + ).then($DONE, $DONE);