Skip to content
52 changes: 42 additions & 10 deletions spec.html
Original file line number Diff line number Diff line change
Expand Up @@ -3890,6 +3890,16 @@ <h1>Well-Known Intrinsic Objects</h1>
The Promise constructor (<emu-xref href="#sec-promise-constructor"></emu-xref>)
</td>
</tr>
<tr>
<td>
%PromiseThenAction%
</td>
<td>
</td>
<td>
An internal function object for PerformPromiseThen
</td>
</tr>
<tr>
<td>
%Proxy%
Expand Down Expand Up @@ -48472,21 +48482,42 @@ <h1>Promise Resolve Functions</h1>
1. If _resolution_ is not an Object, then
1. Perform FulfillPromise(_promise_, _resolution_).
1. Return *undefined*.
1. Let _then_ be Completion(Get(_resolution_, *"then"*)).
1. If _then_ is an abrupt completion, then
1. Perform RejectPromise(_promise_, _then_.[[Value]]).
1. Return *undefined*.
1. Let _thenAction_ be _then_.[[Value]].
1. If IsCallable(_thenAction_) is *false*, then
1. Perform FulfillPromise(_promise_, _resolution_).
1. Return *undefined*.
1. Let _thenAction_ be *null*.
1. If IsPromise(_resolution_) is *true*, then
1. Let _proto_ be ! _resolution_.[[GetPrototypeOf]]().
1. If SameValue(_proto_, %Promise.prototype%) is *true*, set _thenAction_ to %PromiseThenAction%.
1. If _thenAction_ is *null*, then
1. Let _then_ be Completion(Get(_resolution_, *"then"*)).
1. If _then_ is an abrupt completion, then
1. Perform RejectPromise(_promise_, _then_.[[Value]]).
1. Return *undefined*.
1. Set _thenAction_ to _then_.[[Value]].
1. If IsCallable(_thenAction_) is *false*, then
1. Perform FulfillPromise(_promise_, _resolution_).
1. Return *undefined*.
1. Let _thenJobCallback_ be HostMakeJobCallback(_thenAction_).
1. Let _job_ be NewPromiseResolveThenableJob(_promise_, _resolution_, _thenJobCallback_).
1. Perform HostEnqueuePromiseJob(_job_.[[Job]], _job_.[[Realm]]).
1. Return *undefined*.
</emu-alg>
<p>The *"length"* property of a promise resolve function is *1*<sub>𝔽</sub>.</p>
</emu-clause>

<emu-clause id="sec-%promisethenaction%">
<h1>%PromiseThenAction% ( _onFulfilled_, _onRejected_ )</h1>
<p>The <dfn>%PromiseThenAction%</dfn> intrinsic function:</p>
<ul>
<li>is an anonymous built-in function object that is defined once for each realm.</li>
<li>is never directly accessible to ECMAScript code.</li>
<li>is used by the promise resolve functions to adopt the state of the promise passed as the receiver.</li>
<li>performs the following steps when called:</li>
</ul>
<emu-alg>
1. Let _promise_ be the *this* value.
1. Assert: IsPromise(_promise_) is *true*.
1. Return PerformPromiseThen(_promise_, _onFulfilled_, _onRejected_).
</emu-alg>
</emu-clause>
</emu-clause>

<emu-clause id="sec-fulfillpromise" type="abstract operation">
Expand Down Expand Up @@ -49141,8 +49172,9 @@ <h1>
</dl>
<emu-alg>
1. If IsPromise(_x_) is *true*, then
1. Let _xConstructor_ be ? Get(_x_, *"constructor"*).
1. If SameValue(_xConstructor_, _C_) is *true*, return _x_.
1. Let _xProto_ be ! _x_.[[GetPrototypeOf]]().

Check warning on line 49175 in spec.html

View workflow job for this annotation

GitHub Actions / check for newly-introduced spelling errors

Potential Typo

"xProto" is not a previously used word or composed of previously used words. Perhaps it is a typo?
1. Let _CPrototype_ be ? Get(_C_, *"prototype"*).

Check warning on line 49176 in spec.html

View workflow job for this annotation

GitHub Actions / check for newly-introduced spelling errors

Potential Typo

"CPrototype" is not a previously used word or composed of previously used words. Perhaps it is a typo?
1. If SameValue(_xProto_, _CPrototype_) is *true*, return _x_.

Check warning on line 49177 in spec.html

View workflow job for this annotation

GitHub Actions / check for newly-introduced spelling errors

Potential Typo

"CPrototype" is not a previously used word or composed of previously used words. Perhaps it is a typo?

Check warning on line 49177 in spec.html

View workflow job for this annotation

GitHub Actions / check for newly-introduced spelling errors

Potential Typo

"xProto" is not a previously used word or composed of previously used words. Perhaps it is a typo?
Comment on lines 49549 to 49553
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, consider (realPromise => Reflect.apply(Promise.resolve, Object.defineProperty(function(){ throw Error("invoked"); }, "prototype", { value: Promise.prototype }), [realPromise]) === realPromise)(new Promise(() => {})) in an unmodified environment. With the current definition of PromiseResolve, it will invoke the supplied constructor (effectively attempting to return an instance from it), but with the proposed new definition, it will instead return realPromise and the entire expression will be true.

This is basically inverting the loophole that already exists, where instead of a real promise being able to fake association with an arbitrary constructor (and getting the chance to run code in the process), an arbitrary constructor can fake association with any real promise. But since resolve is a method of the constructor anyway, that power totally makes sense. 👍

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Correct, .resolve trusts and works in the context of its "constructor" receiver.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we wanted to be really prescriptive about this, we could check if _C_ is a constructor/function. I don't believe the .prototype of a function can ever be an accessor? So we could turn this into ! instead of ?.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some functions don't have a .prototype, which could be installed manually. Also C could be a proxy for a constructor / function.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it’s not a constructor, then we can’t new C, so NewPromiseCapability(C) would just throw. I don’t think I we need to consider those.

I’m kinda ambivalent on whether we need to support proxies of the promise constructor. Up to you.

1. Let _promiseCapability_ be ? NewPromiseCapability(_C_).
1. Perform ? Call(_promiseCapability_.[[Resolve]], *undefined*, « _x_ »).
1. Return _promiseCapability_.[[Promise]].
Expand Down