diff --git a/test/built-ins/Promise/resolve-prms-poisoned-species-deferred.js b/test/built-ins/Promise/resolve-prms-poisoned-species-deferred.js new file mode 100644 index 00000000000..8bcfb62bbaf --- /dev/null +++ b/test/built-ins/Promise/resolve-prms-poisoned-species-deferred.js @@ -0,0 +1,56 @@ +// Copyright (C) 2026 Alistair Smith. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Resolving with a native Promise whose `constructor[Symbol.species]` + getter throws, after invocation of the executor function +esid: sec-newpromiseresolvethenablejob +info: | + NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) + + 1. Let job be a new Job Abstract Closure with no parameters that captures + promiseToResolve, thenable, and then and performs the following steps + when called: + a. Let resolvingFuncs be CreateResolvingFunctions(promiseToResolve). + b. Let thenCallResult be Completion(HostCallJobCallback(then, thenable, + « resolvingFuncs.[[Resolve]], resolvingFuncs.[[Reject]] »)). + c. If thenCallResult is an abrupt completion, then + i. Return ? Call(resolvingFuncs.[[Reject]], undefined, + « thenCallResult.[[Value]] »). + d. Return ! thenCallResult. + + Promise.prototype.then ( onFulfilled, onRejected ) + + 1. Let promise be the this value. + 2. If IsPromise(promise) is false, throw a TypeError exception. + 3. Let ctor be ? SpeciesConstructor(promise, %Promise%). +flags: [async] +includes: [asyncHelpers.js] +features: [Symbol.species] +---*/ + +var returnValue = null; +var value = {}; +var resolve; +var thenable = Promise.resolve(); +thenable.constructor = {}; +Object.defineProperty(thenable.constructor, Symbol.species, { + get: function() { + throw value; + } +}); +var promise = new Promise(function(_resolve) { + resolve = _resolve; +}); + +asyncTest(function() { + return promise.then(function() { + throw new Test262Error('The promise should not be fulfilled.'); + }, function(val) { + assert.sameValue(val, value, 'The promise should be rejected with the thrown value.'); + }); +}); + +returnValue = resolve(thenable); + +assert.sameValue(returnValue, undefined, '"resolve" return value'); diff --git a/test/built-ins/Promise/resolve-prms-poisoned-species-immed.js b/test/built-ins/Promise/resolve-prms-poisoned-species-immed.js new file mode 100644 index 00000000000..0ee9f8a70e5 --- /dev/null +++ b/test/built-ins/Promise/resolve-prms-poisoned-species-immed.js @@ -0,0 +1,54 @@ +// Copyright (C) 2026 Alistair Smith. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Resolving with a native Promise whose `constructor[Symbol.species]` + getter throws, from within the executor function +esid: sec-newpromiseresolvethenablejob +info: | + NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) + + 1. Let job be a new Job Abstract Closure with no parameters that captures + promiseToResolve, thenable, and then and performs the following steps + when called: + a. Let resolvingFuncs be CreateResolvingFunctions(promiseToResolve). + b. Let thenCallResult be Completion(HostCallJobCallback(then, thenable, + « resolvingFuncs.[[Resolve]], resolvingFuncs.[[Reject]] »)). + c. If thenCallResult is an abrupt completion, then + i. Return ? Call(resolvingFuncs.[[Reject]], undefined, + « thenCallResult.[[Value]] »). + d. Return ! thenCallResult. + + Promise.prototype.then ( onFulfilled, onRejected ) + + 1. Let promise be the this value. + 2. If IsPromise(promise) is false, throw a TypeError exception. + 3. Let ctor be ? SpeciesConstructor(promise, %Promise%). +flags: [async] +includes: [asyncHelpers.js] +features: [Symbol.species] +---*/ + +var returnValue = null; +var value = {}; +var thenable = Promise.resolve(); +thenable.constructor = {}; +Object.defineProperty(thenable.constructor, Symbol.species, { + get: function() { + throw value; + } +}); + +var promise = new Promise(function(resolve) { + returnValue = resolve(thenable); +}); + +assert.sameValue(returnValue, undefined, '"resolve" return value'); + +asyncTest(function() { + return promise.then(function() { + throw new Test262Error('The promise should not be fulfilled.'); + }, function(val) { + assert.sameValue(val, value, 'The promise should be rejected with the thrown value.'); + }); +}); diff --git a/test/built-ins/Promise/resolve-thenable-then-throws-deferred.js b/test/built-ins/Promise/resolve-thenable-then-throws-deferred.js new file mode 100644 index 00000000000..42fa56c06f1 --- /dev/null +++ b/test/built-ins/Promise/resolve-thenable-then-throws-deferred.js @@ -0,0 +1,47 @@ +// Copyright (C) 2026 Alistair Smith. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Resolving with a thenable whose `then` method throws before settling the + promise, after invocation of the executor function +esid: sec-newpromiseresolvethenablejob +info: | + NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) + + 1. Let job be a new Job Abstract Closure with no parameters that captures + promiseToResolve, thenable, and then and performs the following steps + when called: + a. Let resolvingFuncs be CreateResolvingFunctions(promiseToResolve). + b. Let thenCallResult be Completion(HostCallJobCallback(then, thenable, + « resolvingFuncs.[[Resolve]], resolvingFuncs.[[Reject]] »)). + c. If thenCallResult is an abrupt completion, then + i. Return ? Call(resolvingFuncs.[[Reject]], undefined, + « thenCallResult.[[Value]] »). + d. Return ! thenCallResult. +flags: [async] +includes: [asyncHelpers.js] +---*/ + +var returnValue = null; +var value = {}; +var resolve; +var thenable = { + then: function() { + throw value; + } +}; +var promise = new Promise(function(_resolve) { + resolve = _resolve; +}); + +asyncTest(function() { + return promise.then(function() { + throw new Test262Error('The promise should not be fulfilled.'); + }, function(val) { + assert.sameValue(val, value, 'The promise should be rejected with the thrown value.'); + }); +}); + +returnValue = resolve(thenable); + +assert.sameValue(returnValue, undefined, '"resolve" return value'); diff --git a/test/built-ins/Promise/resolve-thenable-then-throws-immed.js b/test/built-ins/Promise/resolve-thenable-then-throws-immed.js new file mode 100644 index 00000000000..d57f76c70fc --- /dev/null +++ b/test/built-ins/Promise/resolve-thenable-then-throws-immed.js @@ -0,0 +1,45 @@ +// Copyright (C) 2026 Alistair Smith. All rights reserved. +// This code is governed by the BSD license found in the LICENSE file. +/*--- +description: > + Resolving with a thenable whose `then` method throws before settling the + promise, from within the executor function +esid: sec-newpromiseresolvethenablejob +info: | + NewPromiseResolveThenableJob ( promiseToResolve, thenable, then ) + + 1. Let job be a new Job Abstract Closure with no parameters that captures + promiseToResolve, thenable, and then and performs the following steps + when called: + a. Let resolvingFuncs be CreateResolvingFunctions(promiseToResolve). + b. Let thenCallResult be Completion(HostCallJobCallback(then, thenable, + « resolvingFuncs.[[Resolve]], resolvingFuncs.[[Reject]] »)). + c. If thenCallResult is an abrupt completion, then + i. Return ? Call(resolvingFuncs.[[Reject]], undefined, + « thenCallResult.[[Value]] »). + d. Return ! thenCallResult. +flags: [async] +includes: [asyncHelpers.js] +---*/ + +var returnValue = null; +var value = {}; +var thenable = { + then: function() { + throw value; + } +}; + +var promise = new Promise(function(resolve) { + returnValue = resolve(thenable); +}); + +assert.sameValue(returnValue, undefined, '"resolve" return value'); + +asyncTest(function() { + return promise.then(function() { + throw new Test262Error('The promise should not be fulfilled.'); + }, function(val) { + assert.sameValue(val, value, 'The promise should be rejected with the thrown value.'); + }); +});