Skip to content

Commit 203842a

Browse files
syglittledan
andauthored
Layering: Implement HostEnqueuePromiseJob
The JavaScript specification now uses the HostEnqueuePromiseJob hook, to be implemented by embedders, for enqueuing promises with the host. This allows HTML to avoid its willful violation, and delete an explanation of the motivation for this violation. Closes #4722 by superseding it. Co-authored-by: Daniel Ehrenberg <[email protected]>
1 parent 99503f7 commit 203842a

File tree

1 file changed

+37
-61
lines changed

1 file changed

+37
-61
lines changed

source

Lines changed: 37 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -2933,6 +2933,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
29332933
<li><dfn data-x-href="https://tc39.es/ecma262/#sec-built-in-function-objects">NewTarget</dfn></li>
29342934
<li><dfn data-x-href="https://tc39.es/ecma262/#running-execution-context">running JavaScript execution context</dfn></li>
29352935
<li><dfn data-x-href="https://tc39.es/ecma262/#surrounding-agent">surrounding agent</dfn></li>
2936+
<li><dfn data-x-href="https://tc39.es/ecma262/#sec-abstract-closure">abstract closure</dfn></li>
29362937
<li><dfn data-x-href="https://tc39.es/ecma262/#sec-well-known-symbols">Well-Known Symbols</dfn>, including
29372938
<dfn>@@hasInstance</dfn>,
29382939
<dfn>@@isConcatSpreadable</dfn>,
@@ -2974,14 +2975,14 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
29742975
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-createbytedatablock">CreateByteDataBlock</dfn> abstract operation</li>
29752976
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-createdataproperty">CreateDataProperty</dfn> abstract operation</li>
29762977
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-detacharraybuffer">DetachArrayBuffer</dfn> abstract operation</li>
2977-
<li>The <dfn data-x="js-EnqueueJob" data-x-href="https://tc39.es/ecma262/#sec-enqueuejob">EnqueueJob</dfn> abstract operation</li>
29782978
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-enumerableownproperties">EnumerableOwnProperties</dfn> abstract operation</li>
29792979
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-finishdynamicimport">FinishDynamicImport</dfn> abstract operation</li>
29802980
<li>The <dfn data-x="js-OrdinaryFunctionCreate" data-x-href="https://tc39.es/ecma262/#sec-ordinaryfunctioncreate">OrdinaryFunctionCreate</dfn> abstract operation</li>
29812981
<li>The <dfn data-x="js-Get" data-x-href="https://tc39.es/ecma262/#sec-get-o-p">Get</dfn> abstract operation</li>
29822982
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-getactivescriptormodule">GetActiveScriptOrModule</dfn> abstract operation</li>
29832983
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-getfunctionrealm">GetFunctionRealm</dfn> abstract operation</li>
29842984
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-hasownproperty">HasOwnProperty</dfn> abstract operation</li>
2985+
<li>The <dfn data-x="js-HostEnqueuePromiseJob" data-x-href="https://tc39.es/ecma262/#sec-hostenqueuepromisejob">HostEnqueuePromiseJob</dfn> abstract operation</li>
29852986
<li>The <dfn data-x="js-HostEnsureCanCompileStrings" data-x-href="https://tc39.es/ecma262/#sec-hostensurecancompilestrings">HostEnsureCanCompileStrings</dfn> abstract operation</li>
29862987
<li>The <dfn data-x="js-HostImportModuleDynamically" data-x-href="https://tc39.es/proposal-dynamic-import/#sec-hostimportmoduledynamically">HostImportModuleDynamically</dfn> abstract operation</li>
29872988
<li>The <dfn data-x="js-HostPromiseRejectionTracker" data-x-href="https://tc39.es/ecma262/#sec-host-promise-rejection-tracker">HostPromiseRejectionTracker</dfn> abstract operation</li>
@@ -3009,8 +3010,8 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
30093010
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-objectcreate">ObjectCreate</dfn> abstract operation</li>
30103011
<li>The <dfn data-x="js-ParseModule" data-x-href="https://tc39.es/ecma262/#sec-parsemodule">ParseModule</dfn> abstract operation</li>
30113012
<li>The <dfn data-x="js-ParseScript" data-x-href="https://tc39.es/ecma262/#sec-parse-script">ParseScript</dfn> abstract operation</li>
3012-
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-promisereactionjob">PromiseReactionJob</dfn> abstract operation</li>
3013-
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-promiseresolvethenablejob">PromiseResolveThenableJob</dfn> abstract operation</li>
3013+
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-newpromisereactionjob">NewPromiseReactionJob</dfn> abstract operation</li>
3014+
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob">NewPromiseResolveThenableJob</dfn> abstract operation</li>
30143015
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-regexpbuiltinexec">RegExpBuiltinExec</dfn> abstract operation</li>
30153016
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-regexpcreate">RegExpCreate</dfn> abstract operation</li>
30163017
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-runjobs">RunJobs</dfn> abstract operation</li>
@@ -90832,8 +90833,8 @@ interface <dfn>ApplicationCache</dfn> : <span>EventTarget</span> {
9083290833
spec="WEBIDL"></p>
9083390834

9083490835
<p>When Web IDL is used to <span data-x="es-invoking-callback-functions">invoke</span> author
90835-
code, or when <span>EnqueueJob</span> invokes a promise job, they use the following algorithms to
90836-
track relevant data for determining the <span>incumbent settings object</span>:</p>
90836+
code, or when <span>HostEnqueuePromiseJob</span> invokes a promise job, they use the following
90837+
algorithms to track relevant data for determining the <span>incumbent settings object</span>:</p>
9083790838

9083890839
<p>To <dfn data-export="">prepare to run a callback</dfn> with an <span>environment settings
9083990840
object</span> <var>settings</var>:</p>
@@ -91142,65 +91143,35 @@ document.querySelector("button").addEventListener("click", bound);
9114291143
user with a mechanism to just close the page entirely, without running any <code
9114391144
data-x="event-unload">unload</code> event handlers.</p>
9114491145

91145-
<h5>Integration with the JavaScript job queue</h5>
91146+
<span id="integration-with-the-javascript-job-queue"></span>
91147+
<span id="enqueuejob(queuename,-job,-arguments)"></span>
91148+
<h5 id="hostenqueuepromisejob"><dfn>HostEnqueuePromiseJob</dfn>(<var>job</var>,
91149+
<var>realm</var>)</h5>
9114691150

91147-
<p>The JavaScript specification defines the JavaScript job and job queue abstractions in order to
91148-
specify certain invariants about how promise operations execute with a clean <span>JavaScript
91149-
execution context stack</span> and in a certain order. However, as of the time of this writing
91150-
the definition of <span data-x="js-EnqueueJob">EnqueueJob</span> in that specification is not
91151-
sufficiently flexible to integrate with HTML as a host environment. <ref spec=JAVASCRIPT></p>
91152-
91153-
<p class="note">This is not strictly true. It is in fact possible, by taking liberal advantage of
91154-
the many "implementation defined" sections of the algorithm, to contort it to our purposes.
91155-
However, the end result is a mass of messy indirection and workarounds that essentially bypasses
91156-
the job queue infrastructure entirely, albeit in a way that is technically sanctioned within the
91157-
bounds of implementation-defined behavior. We do not take this path, and instead introduce the
91158-
following <span>willful violation</span>.</p>
91159-
91160-
<p>As such, user agents must instead use the following definition in place of that in the
91161-
JavaScript specification. These ensure that the promise jobs enqueued by the JavaScript
91162-
specification are properly integrated into the user agent's <span data-x="event loop">event
91163-
loops</span>.</p>
91164-
91165-
<p>The <span>RunJobs</span> abstract operation from the JavaScript specification must
91166-
not be used by user agents.</p>
91167-
91168-
<h6><dfn>EnqueueJob</dfn>(<var>queueName</var>, <var>job</var>, <var>arguments</var>)</h6>
91169-
91170-
<p>When the JavaScript specification says to call the EnqueueJob abstract operation, the
91171-
following algorithm must be used in place of JavaScript's <span
91172-
data-x="js-EnqueueJob">EnqueueJob</span>:</p>
91151+
<p>JavaScript contains an implementation-defined <span
91152+
data-x="js-HostEnqueuePromiseJob">HostEnqueuePromiseJob</span>(<var>job</var>, <var>realm</var>)
91153+
abstract operation to schedule Promise-related operations. HTML schedules these operations in the
91154+
microtask queue. User agents must use the following implementation: <ref spec=JAVASCRIPT></p>
9117391155

9117491156
<ol>
91175-
<li><p>Assert: <var>queueName</var> is <code data-x="">"PromiseJobs"</code>. (<code
91176-
data-x="">"ScriptJobs"</code> must not be used by user agents.)</p></li>
91177-
91178-
<li><p>Assert: <var>job</var> is either <span>PromiseResolveThenableJob</span> or
91179-
<span>PromiseReactionJob</span>. (The following steps would need to be updated if another type of
91180-
promise job were introduced.)</p></li>
91181-
9118291157
<li>
91183-
<p>Let <var>job settings</var> be determined by switching on <var>job</var>:</p>
91158+
<p>If <var>realm</var> is not null, then let <var>job settings</var> be the <span
91159+
data-x="concept-realm-settings-object">settings object</span> for <var>realm</var>. Otherwise,
91160+
let <var>job settings</var> be null.</p>
9118491161

91185-
<dl class="switch">
91186-
<dt><span>PromiseResolveThenableJob</span></dt>
91187-
<dd>
91188-
<p>The <span data-x="concept-realm-settings-object">settings object</span> for
91189-
<var>arguments</var>[2].[[Realm]]. (I.e., the <span data-x="JavaScript realm">Realm</span> of
91190-
the <code data-x="">then</code> function.)</p>
91191-
</dd>
91192-
91193-
<dt><span>PromiseReactionJob</span></dt>
91194-
<dd>
91195-
<p>If <var>arguments</var>[0].[[Handler]] is not undefined, then the <span
91196-
data-x="concept-realm-settings-object">settings object</span> of
91197-
<var>arguments</var>[0].[[Handler]].[[Realm]]; otherwise, null.</p>
91162+
<div class="note">
91163+
<p>If <var>realm</var> is not null, it is the <span data-x="JavaScript realm">Realm</span> of
91164+
the author code that will run. When <var>job</var> is returned by
91165+
<span>NewPromiseReactionJob</span>, it is the realm of the promise's handler function. When
91166+
<var>job</var> is returned by <span>NewPromiseResolveThenableJob</span>, it is the realm of
91167+
the <code data-x="">then</code> function.
9119891168

91199-
<p class="note">If the handler is undefined, then we are in a case like <code
91200-
data-x="">promise.then(null, null)</code>. In this case, no author code will run, so all of
91201-
the steps below that would otherwise use <var>job settings</var> get skipped.</p>
91202-
</dd>
91203-
</dl>
91169+
<p>If <var>realm</var> is null, either no author code will run or author code is guaranteed to
91170+
throw. For the former, the author may not have passed in code to run, such as in <code
91171+
data-x="">promise.then(null, null)</code>. For the latter, it is because a revoked Proxy was
91172+
passed. In both cases, all the steps below that would otherwise use <var>job settings</var>
91173+
get skipped.</p>
91174+
</div>
9120491175
</li>
9120591176

9120691177
<li><p>Let <var>incumbent settings</var> be the <span>incumbent settings object</span>.</p></li>
@@ -91244,7 +91215,8 @@ document.querySelector("button").addEventListener("click", bound);
9124491215
handler</span> will be created by the <span data-x="getting the current value of the event
9124591216
handler">get the current value of the event handler</span> algorithm, which creates a function
9124691217
with null [[ScriptOrModule]] value. Thus, when the promise machinery calls
91247-
<span>EnqueueJob</span>, there will be no <span>active script</span> to pass along.</p>
91218+
<span>HostEnqueuePromiseJob</span>, there will be no <span>active script</span> to pass
91219+
along.</p>
9124891220

9124991221
<p>As a consequence, this means that when the <code>import()</code> expression is evaluated,
9125091222
there will still be no <span>active script</span>. Fortunately that is handled by our
@@ -91286,8 +91258,12 @@ document.querySelector("button").addEventListener("click", bound);
9128691258
runs.</p>
9128791259
</li>
9128891260

91289-
<li><p>Let <var>result</var> be the result of performing the abstract operation specified by
91290-
<var>job</var>, using the elements of <var>arguments</var> as its arguments.</p></li>
91261+
<li>
91262+
<p>Let <var>result</var> be <var>job</var>().</p>
91263+
91264+
<p class="note"><var>job</var> is an <span>abstract closure</span> returned by
91265+
<span>NewPromiseReactionJob</span> or <span>NewPromiseResolveThenableJob</span>.</p>
91266+
</li>
9129191267

9129291268
<li><p>If <var>script execution context</var> is not null, then <span data-x="stack
9129391269
pop">pop</span> <var>script execution context</var> from the <span>JavaScript execution context

0 commit comments

Comments
 (0)