Skip to content

Commit 4069869

Browse files
sygljharb
authored andcommitted
Normative: Add machinery to track the incumbent settings object in HTML for Job callbacks (#2086)
In HTML, JS functions that are called asynchronously as part of jobs (like Promise microtasks) are generally associated with a piece of state called an incumbent settings object. The incumbent is stored at the time when the callback is first passed to the API that is responsible for scheduling the job (e.g. Promise.then), and restored when the callback is called. For symmetry, all WebIDL callbacks do this: https://heycam.github.io/webidl/#idl-callback-interface This PR adds the necessary host hook machinery to let HTML store and restore the incumbent settings object. The default behavior is nop.
1 parent 0fa70bc commit 4069869

File tree

1 file changed

+110
-17
lines changed

1 file changed

+110
-17
lines changed

spec.html

Lines changed: 110 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7793,6 +7793,91 @@ <h1>Jobs and Host Operations to Enqueue Jobs</h1>
77937793

77947794
<p>Particular kinds of Jobs have additional conformance requirements.</p>
77957795

7796+
<emu-clause id="sec-jobcallback-records">
7797+
<h1>JobCallback Records</h1>
7798+
<p>A <dfn>JobCallback Record</dfn> is a Record value used to store a function object and a host-defined value. Function objects that are invoked via a Job enqueued by the host may have additional host-defined context. To propagate the state, Job Abstract Closures should not capture and call function objects directly. Instead, use HostMakeJobCallback and HostCallJobCallback.</p>
7799+
<emu-note>
7800+
<p>The WHATWG HTML specification (<a href="https://html.spec.whatwg.org/">https://html.spec.whatwg.org/</a>), for example, uses the host-defined value to propagate the incumbent settings object for Promise callbacks.</p>
7801+
</emu-note>
7802+
<p>JobCallback Records have the fields listed in <emu-xref href="#table-jobcallback-records"></emu-xref>.</p>
7803+
<emu-table id="table-jobcallback-records" caption="JobCallback Record Fields">
7804+
<table>
7805+
<tbody>
7806+
<tr>
7807+
<th>
7808+
Field Name
7809+
</th>
7810+
<th>
7811+
Value
7812+
</th>
7813+
<th>
7814+
Meaning
7815+
</th>
7816+
</tr>
7817+
<tr>
7818+
<td>
7819+
[[Callback]]
7820+
</td>
7821+
<td>
7822+
A function object
7823+
</td>
7824+
<td>
7825+
The function to invoke when the Job is invoked.
7826+
</td>
7827+
</tr>
7828+
<tr>
7829+
<td>
7830+
[[HostDefined]]
7831+
</td>
7832+
<td>
7833+
Any, default value is ~empty~.
7834+
</td>
7835+
<td>
7836+
Field reserved for use by hosts.
7837+
</td>
7838+
</tr>
7839+
</tbody>
7840+
</table>
7841+
</emu-table>
7842+
</emu-clause>
7843+
7844+
<emu-clause id="sec-hostmakejobcallback" aoid="HostMakeJobCallback">
7845+
<h1>HostMakeJobCallback ( _callback_ )</h1>
7846+
<p>HostMakeJobCallback is a host-defined abstract operation that takes argument _callback_ (a function object).</p>
7847+
<p>The implementation of HostMakeJobCallback must conform to the following requirements:</p>
7848+
<ul>
7849+
<li>It must always complete normally (i.e., not return an abrupt completion).</li>
7850+
<li>It must always return a JobCallback Record whose [[Callback]] field is _callback_.</li>
7851+
</ul>
7852+
<p>The default implementation of HostMakeJobCallback performs the following steps when called:</p>
7853+
<emu-alg>
7854+
1. Assert: IsCallable(_callback_) is *true*.
7855+
1. Return the JobCallback Record { [[Callback]]: _callback_, [[HostDefined]]: ~empty~ }.
7856+
</emu-alg>
7857+
<p>ECMAScript hosts that are not web browsers must use the default implementation of HostMakeJobCallback.</p>
7858+
<emu-note>
7859+
<p>This is called at the time that the callback is passed to the function that is responsible for its being eventually scheduled and run. For example, `promise.then(thenAction)` calls MakeJobCallback on `thenAction` at the time of invoking `Promise.prototype.then`, not at the time of scheduling the reaction Job.</p>
7860+
</emu-note>
7861+
</emu-clause>
7862+
7863+
<emu-clause id="sec-hostcalljobcallback" aoid="HostCallJobCallback">
7864+
<h1>HostCallJobCallback ( _jobCallback_, _V_, _argumentsList_ )</h1>
7865+
<p>HostCallJobCallback is a host-defined abstract operation that takes arguments _jobCallback_ (a JobCallback Record), _V_ (an ECMAScript language value), and _argumentsList_ (a List of ECMAScript language values).</p>
7866+
<p>The implementation of HostCallJobCallback must conform to the following requirements:</p>
7867+
<ul>
7868+
<li>It must always perform and return the result of Call(_jobCallback_.[[Callback]], _V_, _argumentsList_).</li>
7869+
</ul>
7870+
<emu-note>
7871+
<p>This requirement means that hosts cannot change the [[Call]] behaviour of function objects defined in this specification.</p>
7872+
</emu-note>
7873+
<p>The default implementation of HostCallJobCallback performs the following steps when called:</p>
7874+
<emu-alg>
7875+
1. Assert: IsCallable(_jobCallback_.[[Callback]]) is *true*.
7876+
1. Return ? Call(_jobCallback_.[[Callback]], _V_, _argumentsList_).
7877+
</emu-alg>
7878+
<p>ECMAScript hosts that are not web browsers must use the default implementation of HostCallJobCallback.</p>
7879+
</emu-clause>
7880+
77967881
<emu-clause id="sec-hostenqueuepromisejob" aoid="HostEnqueuePromiseJob">
77977882
<h1>HostEnqueuePromiseJob ( _job_, _realm_ )</h1>
77987883
<p>HostEnqueuePromiseJob is a host-defined abstract operation that schedules the Job Abstract Closure _job_ to be performed, at some future time. The Abstract Closures used with this algorithm are intended to be related to the handling of Promises, or otherwise, to be scheduled with equal priority to Promise handling operations.</p>
@@ -22797,7 +22882,7 @@ <h1>Script Records</h1>
2279722882
[[HostDefined]]
2279822883
</td>
2279922884
<td>
22800-
Any, default value is *undefined*.
22885+
Any, default value is ~empty~.
2280122886
</td>
2280222887
<td>
2280322888
Field reserved for use by host environments that need to associate additional information with a script.
@@ -40578,18 +40663,18 @@ <h1>PromiseReaction Records</h1>
4057840663
~Fulfill~ | ~Reject~
4057940664
</td>
4058040665
<td>
40581-
The [[Type]] is used when [[Handler]] is *undefined* to allow for behaviour specific to the settlement type.
40666+
The [[Type]] is used when [[Handler]] is ~empty~ to allow for behaviour specific to the settlement type.
4058240667
</td>
4058340668
</tr>
4058440669
<tr>
4058540670
<td>
4058640671
[[Handler]]
4058740672
</td>
4058840673
<td>
40589-
A function object or *undefined*.
40674+
A JobCallback Record | ~empty~.
4059040675
</td>
4059140676
<td>
40592-
The function that should be applied to the incoming value, and whose return value will govern what happens to the derived promise. If [[Handler]] is *undefined*, a function that depends on the value of [[Type]] will be used instead.
40677+
The function that should be applied to the incoming value, and whose return value will govern what happens to the derived promise. If [[Handler]] is ~empty~, a function that depends on the value of [[Type]] will be used instead.
4059340678
</td>
4059440679
</tr>
4059540680
</tbody>
@@ -40651,7 +40736,8 @@ <h1>Promise Resolve Functions</h1>
4065140736
1. Let _thenAction_ be _then_.[[Value]].
4065240737
1. If IsCallable(_thenAction_) is *false*, then
4065340738
1. Return FulfillPromise(_promise_, _resolution_).
40654-
1. Let _job_ be NewPromiseResolveThenableJob(_promise_, _resolution_, _thenAction_).
40739+
1. Let _thenJobCallback_ be HostMakeJobCallback(_thenAction_).
40740+
1. Let _job_ be NewPromiseResolveThenableJob(_promise_, _resolution_, _thenJobCallback_).
4065540741
1. Perform HostEnqueuePromiseJob(_job_.[[Job]], _job_.[[Realm]]).
4065640742
1. Return *undefined*.
4065740743
</emu-alg>
@@ -40738,7 +40824,7 @@ <h1>RejectPromise ( _promise_, _reason_ )</h1>
4073840824

4073940825
<emu-clause id="sec-triggerpromisereactions" aoid="TriggerPromiseReactions">
4074040826
<h1>TriggerPromiseReactions ( _reactions_, _argument_ )</h1>
40741-
<p>The abstract operation TriggerPromiseReactions takes arguments _reactions_ (a collection of PromiseReaction Records) and _argument_. It enqueues a new Job for each record in _reactions_. Each such Job processes the [[Type]] and [[Handler]] of the PromiseReaction Record, and if the [[Handler]] is a function, calls it passing the given argument. If the [[Handler]] is *undefined*, the behaviour is determined by the [[Type]]. It performs the following steps when called:</p>
40827+
<p>The abstract operation TriggerPromiseReactions takes arguments _reactions_ (a collection of PromiseReaction Records) and _argument_. It enqueues a new Job for each record in _reactions_. Each such Job processes the [[Type]] and [[Handler]] of the PromiseReaction Record, and if the [[Handler]] is not ~empty~, calls it passing the given argument. If the [[Handler]] is ~empty~, the behaviour is determined by the [[Type]]. It performs the following steps when called:</p>
4074240828
<emu-alg>
4074340829
1. For each _reaction_ in _reactions_, in original insertion order, do
4074440830
1. Let _job_ be NewPromiseReactionJob(_reaction_, _argument_).
@@ -40782,12 +40868,12 @@ <h1>NewPromiseReactionJob ( _reaction_, _argument_ )</h1>
4078240868
1. Let _promiseCapability_ be _reaction_.[[Capability]].
4078340869
1. Let _type_ be _reaction_.[[Type]].
4078440870
1. Let _handler_ be _reaction_.[[Handler]].
40785-
1. If _handler_ is *undefined*, then
40871+
1. If _handler_ is ~empty~, then
4078640872
1. If _type_ is ~Fulfill~, let _handlerResult_ be NormalCompletion(_argument_).
4078740873
1. Else,
4078840874
1. Assert: _type_ is ~Reject~.
4078940875
1. Let _handlerResult_ be ThrowCompletion(_argument_).
40790-
1. Else, let _handlerResult_ be Call(_handler_, *undefined*, &laquo; _argument_ &raquo;).
40876+
1. Else, let _handlerResult_ be HostCallJobCallback(_handler_, *undefined*, &laquo; _argument_ &raquo;).
4079140877
1. If _promiseCapability_ is *undefined*, then
4079240878
1. Assert: _handlerResult_ is not an abrupt completion.
4079340879
1. Return NormalCompletion(~empty~).
@@ -40798,8 +40884,8 @@ <h1>NewPromiseReactionJob ( _reaction_, _argument_ )</h1>
4079840884
1. Let _status_ be Call(_promiseCapability_.[[Resolve]], *undefined*, &laquo; _handlerResult_.[[Value]] &raquo;).
4079940885
1. Return Completion(_status_).
4080040886
1. Let _handlerRealm_ be *null*.
40801-
1. If _reaction_.[[Handler]] is not *undefined*, then
40802-
1. Let _getHandlerRealmResult_ be GetFunctionRealm(_reaction_.[[Handler]]).
40887+
1. If _reaction_.[[Handler]] is not ~empty~, then
40888+
1. Let _getHandlerRealmResult_ be GetFunctionRealm(_reaction_.[[Handler]].[[Callback]]).
4080340889
1. If _getHandlerRealmResult_ is a normal completion, then set _handlerRealm_ to _getHandlerRealmResult_.[[Value]].
4080440890
1. Else, set _handlerRealm_ to the current Realm Record.
4080540891
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.
@@ -40813,15 +40899,15 @@ <h1>NewPromiseResolveThenableJob ( _promiseToResolve_, _thenable_, _then_ )</h1>
4081340899
<emu-alg>
4081440900
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:
4081540901
1. Let _resolvingFunctions_ be CreateResolvingFunctions(_promiseToResolve_).
40816-
1. Let _thenCallResult_ be Call(_then_, _thenable_, &laquo; _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] &raquo;).
40902+
1. Let _thenCallResult_ be HostCallJobCallback(_then_, _thenable_, &laquo; _resolvingFunctions_.[[Resolve]], _resolvingFunctions_.[[Reject]] &raquo;).
4081740903
1. If _thenCallResult_ is an abrupt completion, then
4081840904
1. Let _status_ be Call(_resolvingFunctions_.[[Reject]], *undefined*, &laquo; _thenCallResult_.[[Value]] &raquo;).
4081940905
1. Return Completion(_status_).
4082040906
1. Return Completion(_thenCallResult_).
40821-
1. Let _getThenRealmResult_ be GetFunctionRealm(_then_).
40907+
1. Let _getThenRealmResult_ be GetFunctionRealm(_then_.[[Callback]]).
4082240908
1. If _getThenRealmResult_ is a normal completion, then let _thenRealm_ be _getThenRealmResult_.[[Value]].
4082340909
1. Else, let _thenRealm_ be the current Realm Record.
40824-
1. NOTE: _thenRealm_ is never *null*. When _then_ is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects.
40910+
1. NOTE: _thenRealm_ is never *null*. When _then_.[[Callback]] is a revoked Proxy and no code runs, _thenRealm_ is used to create error objects.
4082540911
1. Return the Record { [[Job]]: _job_, [[Realm]]: _thenRealm_ }.
4082640912
</emu-alg>
4082740913
<emu-note>
@@ -41383,11 +41469,15 @@ <h1>PerformPromiseThen ( _promise_, _onFulfilled_, _onRejected_ [ , _resultCapab
4138341469
1. If _resultCapability_ is not present, then
4138441470
1. Set _resultCapability_ to *undefined*.
4138541471
1. If IsCallable(_onFulfilled_) is *false*, then
41386-
1. Set _onFulfilled_ to *undefined*.
41472+
1. Let _onFulfilledJobCallback_ be ~empty~.
41473+
1. Else,
41474+
1. Let _onFulfilledJobCallback_ be HostMakeJobCallback(_onFulfilled_).
4138741475
1. If IsCallable(_onRejected_) is *false*, then
41388-
1. Set _onRejected_ to *undefined*.
41389-
1. Let _fulfillReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Fulfill~, [[Handler]]: _onFulfilled_ }.
41390-
1. Let _rejectReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Reject~, [[Handler]]: _onRejected_ }.
41476+
1. Let _onRejectedJobCallback_ be ~empty~.
41477+
1. Else,
41478+
1. Let _onRejectedJobCallback_ be HostMakeJobCallback(_onRejected_).
41479+
1. Let _fulfillReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Fulfill~, [[Handler]]: _onFulfilledJobCallback_ }.
41480+
1. Let _rejectReaction_ be the PromiseReaction { [[Capability]]: _resultCapability_, [[Type]]: ~Reject~, [[Handler]]: _onRejectedJobCallback_ }.
4139141481
1. If _promise_.[[PromiseState]] is ~pending~, then
4139241482
1. Append _fulfillReaction_ as the last element of the List that is _promise_.[[PromiseFulfillReactions]].
4139341483
1. Append _rejectReaction_ as the last element of the List that is _promise_.[[PromiseRejectReactions]].
@@ -44262,13 +44352,15 @@ <h1>Host Layering Points</h1>
4426244352
<p>See <emu-xref href="#sec-hosts-and-implementations"></emu-xref> for the definition of host.</p>
4426344353
<emu-annex id="sec-host-hooks-summary">
4426444354
<h1>Host Hooks</h1>
44355+
<p><b>HostCallJobCallback(...)</b></p>
4426544356
<p><b>HostEnqueueFinalizationRegistryCleanupJob(...)</b></p>
4426644357
<p><b>HostEnqueuePromiseJob(...)</b></p>
4426744358
<p><b>HostEnsureCanCompileStrings(...)</b></p>
4426844359
<p><b>HostFinalizeImportMeta(...)</b></p>
4426944360
<p><b>HostGetImportMetaProperties(...)</b></p>
4427044361
<p><b>HostHasSourceTextAvailable(...)</b></p>
4427144362
<p><b>HostImportModuleDynamically(...)</b></p>
44363+
<p><b>HostMakeJobCallback(...)</b></p>
4427244364
<p><b>HostPromiseRejectionTracker(...)</b></p>
4427344365
<p><b>HostResolveImportedModule(...)</b></p>
4427444366
<p><b>InitializeHostDefinedRealm(...)</b></p>
@@ -44278,6 +44370,7 @@ <h1>Host-defined Fields</h1>
4427844370
<p>[[HostDefined]] on Realm Records: See <emu-xref href="#table-21"></emu-xref>.</p>
4427944371
<p>[[HostDefined]] on Script Records: See <emu-xref href="#table-script-records"></emu-xref>.</p>
4428044372
<p>[[HostDefined]] on Module Records: See <emu-xref href="#table-36"></emu-xref>.</p>
44373+
<p>[[HostDefined]] on JobCallback Records: See <emu-xref href="#table-jobcallback-records"></emu-xref>.</p>
4428144374
<p>[[HostSynchronizesWith]] on Candidate Executions: See <emu-xref href="#table-candidate-execution-records"></emu-xref>.</p>
4428244375
<p>[[IsHTMLDDA]]: See <emu-xref href="#sec-IsHTMLDDA-internal-slot"></emu-xref>.</p>
4428344376
</emu-annex>

0 commit comments

Comments
 (0)