Skip to content

Commit 7b3bfb4

Browse files
Make sure to propagate a promise job's snapshot when handling abrupt completions (tc39#41)
* Make sure to propagate a promise job's snapshot when handling abrupt completions Closes tc39#35. * Wrap resolve call in `AsyncContextSwap` as well * Update spec.html * Remove extra AsyncContextSwap --------- Co-authored-by: Justin Ridgewell <[email protected]>
1 parent ad3f0de commit 7b3bfb4

File tree

1 file changed

+83
-32
lines changed

1 file changed

+83
-32
lines changed

spec.html

Lines changed: 83 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -206,38 +206,7 @@ <h1>
206206
</emu-alg>
207207
<p>ECMAScript hosts that are not web browsers must use the default implementation of HostMakeJobCallback.</p>
208208
<emu-note>
209-
<p>HostMakeJobCallback snapshots the current AsyncContext global state. HostCallJobCallback restores the snapshot.</p>
210-
</emu-note>
211-
</emu-clause>
212-
213-
<emu-clause id="sec-hostcalljobcallback" type="host-defined abstract operation">
214-
<h1>
215-
HostCallJobCallback (
216-
_jobCallback_: a JobCallback Record,
217-
_V_: an ECMAScript language value,
218-
_argumentsList_: a List of ECMAScript language values,
219-
): either a normal completion containing an ECMAScript language value or a throw completion
220-
</h1>
221-
<dl class="header">
222-
</dl>
223-
<p>An implementation of HostCallJobCallback must conform to the following requirements:</p>
224-
<ul>
225-
<li><ins>It must perform AsyncContextSwap(_jobCallback_.[[AsyncContextSnapshot]]) before the call,</ins></li>
226-
<li>It must perform and return the result of Call(_jobCallback_.[[Callback]], _V_, _argumentsList_),</li>
227-
<li><ins>It must perform AsyncContextSwap after the call, with the result of the earlier AsyncContextSwap operation.</ins></li>
228-
</ul>
229-
<p>The default implementation of HostCallJobCallback performs the following steps when called:</p>
230-
<emu-alg>
231-
1. Assert: IsCallable(_jobCallback_.[[Callback]]) is *true*.
232-
1. <del>Return ? Call(_jobCallback_.[[Callback]], _V_, _argumentsList_).</del>
233-
1. <ins>Let _previousContextMapping_ be AsyncContextSwap(_jobCallback_.[[AsyncContextSnapshot]]).</ins>
234-
1. <ins>Let _result_ be Completion(Call(_jobCallback_.[[Callback]], _V_, _argumentsList_)).</ins>
235-
1. <ins>AsyncContextSwap(_previousContextMapping_).</ins>
236-
1. <ins>Return _result_.</ins>
237-
</emu-alg>
238-
<p>ECMAScript hosts that are not web browsers must use the default implementation of HostCallJobCallback.</p>
239-
<emu-note>
240-
<p>HostCallJobCallback restores the snapshot saved in HostMakeJobCallback when calling the _jobCallback_.[[Callback]].</p>
209+
<p>HostMakeJobCallback snapshots the current AsyncContext global state. The snapshot is restored before calling HostCallJobCallback in NewPromiseReactionJob and NewPromiseResolveThenableJob.</p>
241210
</emu-note>
242211
</emu-clause>
243212
</emu-clause>
@@ -333,6 +302,88 @@ <h1>
333302
</ins>
334303
</emu-clause>
335304
</emu-clause>
305+
306+
<emu-clause id="sec-promise-jobs">
307+
<h1>Promise Jobs</h1>
308+
<emu-clause id="sec-newpromisereactionjob" type="abstract operation" oldids="sec-promisereactionjob">
309+
<h1>
310+
NewPromiseReactionJob (
311+
_reaction_: a PromiseReaction Record,
312+
_argument_: an ECMAScript language value,
313+
): a Record with fields [[Job]] (a Job Abstract Closure) and [[Realm]] (a Realm Record or *null*)
314+
</h1>
315+
<dl class="header">
316+
<dt>description</dt>
317+
<dd>It returns a new Job Abstract Closure that applies the appropriate handler to the incoming value, and uses the handler's return value to resolve or reject the derived promise associated with that handler.</dd>
318+
</dl>
319+
<emu-alg>
320+
1. Let _job_ be a new Job Abstract Closure with no parameters that captures _reaction_ and _argument_ and performs the following steps when called:
321+
1. Let _promiseCapability_ be _reaction_.[[Capability]].
322+
1. Let _type_ be _reaction_.[[Type]].
323+
1. Let _handler_ be _reaction_.[[Handler]].
324+
1. <ins>Let _previousContextMapping_ be AsyncContextSwap(_handler_.[[AsyncContextSnapshot]]).</ins>
325+
1. If _handler_ is ~empty~, then
326+
1. If _type_ is ~Fulfill~, let _handlerResult_ be NormalCompletion(_argument_).
327+
1. Else,
328+
1. Assert: _type_ is ~Reject~.
329+
1. Let _handlerResult_ be ThrowCompletion(_argument_).
330+
1. Else, let _handlerResult_ be Completion(HostCallJobCallback(_handler_, *undefined*, « _argument_ »)).
331+
1. If _promiseCapability_ is *undefined*, then
332+
1. Assert: _handlerResult_ is not an abrupt completion.
333+
1. <ins>AsyncContextSwap(_previousContextMapping_).</ins>
334+
1. Return ~empty~.
335+
1. Assert: _promiseCapability_ is a PromiseCapability Record.
336+
1. If _handlerResult_ is an abrupt completion, then
337+
1. <del>Return ? Call(_promiseCapability_.[[Reject]], *undefined*, « _handlerResult_.[[Value]] »).</del>
338+
1. <ins>Let _resolvingFunctionResult_ be Completion(Call(_promiseCapability_.[[Reject]], *undefined*, « _handlerResult_.[[Value]] »)).</ins>
339+
1. Else,
340+
1. <del>Return ? Call(_promiseCapability_.[[Resolve]], *undefined*, « _handlerResult_.[[Value]] »).</del>
341+
1. <ins>Let _resolvingFunctionResult_ be Completion(Call(_promiseCapability_.[[Resolve]], *undefined*, « _handlerResult_.[[Value]] »)).</ins>
342+
1. <ins>AsyncContextSwap(_previousContextMapping_).</ins>
343+
1. <ins>Return _resolvingFunctionResult_.</ins>
344+
1. Let _handlerRealm_ be *null*.
345+
1. If _reaction_.[[Handler]] is not ~empty~, then
346+
1. Let _getHandlerRealmResult_ be Completion(GetFunctionRealm(_reaction_.[[Handler]].[[Callback]])).
347+
1. If _getHandlerRealmResult_ is a normal completion, set _handlerRealm_ to _getHandlerRealmResult_.[[Value]].
348+
1. Else, set _handlerRealm_ to the current Realm Record.
349+
1. NOTE: _handlerRealm_ is never *null* unless the handler is *undefined*. When the handler is a revoked Proxy and no ECMAScript code runs, _handlerRealm_ is used to create error objects.
350+
1. Return the Record { [[Job]]: _job_, [[Realm]]: _handlerRealm_ }.
351+
</emu-alg>
352+
</emu-clause>
353+
354+
<emu-clause id="sec-newpromiseresolvethenablejob" type="abstract operation" oldids="sec-promiseresolvethenablejob">
355+
<h1>
356+
NewPromiseResolveThenableJob (
357+
_promiseToResolve_: a Promise,
358+
_thenable_: an Object,
359+
_then_: a JobCallback Record,
360+
): a Record with fields [[Job]] (a Job Abstract Closure) and [[Realm]] (a Realm Record)
361+
</h1>
362+
<dl class="header">
363+
</dl>
364+
<emu-alg>
365+
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:
366+
1. Let _resolvingFunctions_ be CreateResolvingFunctions(_promiseToResolve_).
367+
1. <ins>Let _previousContextMapping_ be AsyncContextSwap(_then_.[[AsyncContextSnapshot]]).</ins>
368+
1. Let _thenCallResult_ be Completion(HostCallJobCallback(_then_, _thenable_, « _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] »)).
369+
1. If _thenCallResult_ is an abrupt completion, then
370+
1. <del>Return ? Call(_resolvingFunctions_.[[Reject]], *undefined*, « _thenCallResult_.[[Value]] »).</del>
371+
1. <ins>Let _rejectResult_ be Completion(Call(_resolvingFunctions_.[[Reject]], *undefined*, « _thenCallResult_.[[Value]] »)).</ins>
372+
1. <ins>AsyncContextSwap(_previousContextMapping_).</ins>
373+
1. <ins>Return _rejectResult_.</ins>
374+
1. <ins>AsyncContextSwap(_previousContextMapping_).</ins>
375+
1. Return ? _thenCallResult_.
376+
1. Let _getThenRealmResult_ be Completion(GetFunctionRealm(_then_.[[Callback]])).
377+
1. If _getThenRealmResult_ is a normal completion, let _thenRealm_ be _getThenRealmResult_.[[Value]].
378+
1. Else, let _thenRealm_ be the current Realm Record.
379+
1. NOTE: _thenRealm_ is never *null*. When _then_.[[Callback]] is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects.
380+
1. Return the Record { [[Job]]: _job_, [[Realm]]: _thenRealm_ }.
381+
</emu-alg>
382+
<emu-note>
383+
<p>This Job uses the supplied thenable and its `then` method to resolve the given promise. This process must take place as a Job to ensure that the evaluation of the `then` method occurs after evaluation of any surrounding code has completed.</p>
384+
</emu-note>
385+
</emu-clause>
386+
</emu-clause>
336387
</emu-clause>
337388

338389
<emu-clause id="sec-generator-objects">

0 commit comments

Comments
 (0)