Skip to content

Commit

Permalink
Layering: Implement HostEnqueuePromiseJob
Browse files Browse the repository at this point in the history
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 <littledan@chromium.org>
  • Loading branch information
syg and littledan authored Mar 18, 2020
1 parent 99503f7 commit 203842a
Showing 1 changed file with 37 additions and 61 deletions.
98 changes: 37 additions & 61 deletions source
Original file line number Diff line number Diff line change
Expand Up @@ -2933,6 +2933,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li><dfn data-x-href="https://tc39.es/ecma262/#sec-built-in-function-objects">NewTarget</dfn></li>
<li><dfn data-x-href="https://tc39.es/ecma262/#running-execution-context">running JavaScript execution context</dfn></li>
<li><dfn data-x-href="https://tc39.es/ecma262/#surrounding-agent">surrounding agent</dfn></li>
<li><dfn data-x-href="https://tc39.es/ecma262/#sec-abstract-closure">abstract closure</dfn></li>
<li><dfn data-x-href="https://tc39.es/ecma262/#sec-well-known-symbols">Well-Known Symbols</dfn>, including
<dfn>@@hasInstance</dfn>,
<dfn>@@isConcatSpreadable</dfn>,
Expand Down Expand Up @@ -2974,14 +2975,14 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-createbytedatablock">CreateByteDataBlock</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-createdataproperty">CreateDataProperty</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-detacharraybuffer">DetachArrayBuffer</dfn> abstract operation</li>
<li>The <dfn data-x="js-EnqueueJob" data-x-href="https://tc39.es/ecma262/#sec-enqueuejob">EnqueueJob</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-enumerableownproperties">EnumerableOwnProperties</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-finishdynamicimport">FinishDynamicImport</dfn> abstract operation</li>
<li>The <dfn data-x="js-OrdinaryFunctionCreate" data-x-href="https://tc39.es/ecma262/#sec-ordinaryfunctioncreate">OrdinaryFunctionCreate</dfn> abstract operation</li>
<li>The <dfn data-x="js-Get" data-x-href="https://tc39.es/ecma262/#sec-get-o-p">Get</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-getactivescriptormodule">GetActiveScriptOrModule</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-getfunctionrealm">GetFunctionRealm</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-hasownproperty">HasOwnProperty</dfn> abstract operation</li>
<li>The <dfn data-x="js-HostEnqueuePromiseJob" data-x-href="https://tc39.es/ecma262/#sec-hostenqueuepromisejob">HostEnqueuePromiseJob</dfn> abstract operation</li>
<li>The <dfn data-x="js-HostEnsureCanCompileStrings" data-x-href="https://tc39.es/ecma262/#sec-hostensurecancompilestrings">HostEnsureCanCompileStrings</dfn> abstract operation</li>
<li>The <dfn data-x="js-HostImportModuleDynamically" data-x-href="https://tc39.es/proposal-dynamic-import/#sec-hostimportmoduledynamically">HostImportModuleDynamically</dfn> abstract operation</li>
<li>The <dfn data-x="js-HostPromiseRejectionTracker" data-x-href="https://tc39.es/ecma262/#sec-host-promise-rejection-tracker">HostPromiseRejectionTracker</dfn> abstract operation</li>
Expand Down Expand Up @@ -3009,8 +3010,8 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-objectcreate">ObjectCreate</dfn> abstract operation</li>
<li>The <dfn data-x="js-ParseModule" data-x-href="https://tc39.es/ecma262/#sec-parsemodule">ParseModule</dfn> abstract operation</li>
<li>The <dfn data-x="js-ParseScript" data-x-href="https://tc39.es/ecma262/#sec-parse-script">ParseScript</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-promisereactionjob">PromiseReactionJob</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-promiseresolvethenablejob">PromiseResolveThenableJob</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-newpromisereactionjob">NewPromiseReactionJob</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-newpromiseresolvethenablejob">NewPromiseResolveThenableJob</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-regexpbuiltinexec">RegExpBuiltinExec</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-regexpcreate">RegExpCreate</dfn> abstract operation</li>
<li>The <dfn data-x-href="https://tc39.es/ecma262/#sec-runjobs">RunJobs</dfn> abstract operation</li>
Expand Down Expand Up @@ -90832,8 +90833,8 @@ interface <dfn>ApplicationCache</dfn> : <span>EventTarget</span> {
spec="WEBIDL"></p>

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

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

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

<p>The JavaScript specification defines the JavaScript job and job queue abstractions in order to
specify certain invariants about how promise operations execute with a clean <span>JavaScript
execution context stack</span> and in a certain order. However, as of the time of this writing
the definition of <span data-x="js-EnqueueJob">EnqueueJob</span> in that specification is not
sufficiently flexible to integrate with HTML as a host environment. <ref spec=JAVASCRIPT></p>

<p class="note">This is not strictly true. It is in fact possible, by taking liberal advantage of
the many "implementation defined" sections of the algorithm, to contort it to our purposes.
However, the end result is a mass of messy indirection and workarounds that essentially bypasses
the job queue infrastructure entirely, albeit in a way that is technically sanctioned within the
bounds of implementation-defined behavior. We do not take this path, and instead introduce the
following <span>willful violation</span>.</p>

<p>As such, user agents must instead use the following definition in place of that in the
JavaScript specification. These ensure that the promise jobs enqueued by the JavaScript
specification are properly integrated into the user agent's <span data-x="event loop">event
loops</span>.</p>

<p>The <span>RunJobs</span> abstract operation from the JavaScript specification must
not be used by user agents.</p>

<h6><dfn>EnqueueJob</dfn>(<var>queueName</var>, <var>job</var>, <var>arguments</var>)</h6>

<p>When the JavaScript specification says to call the EnqueueJob abstract operation, the
following algorithm must be used in place of JavaScript's <span
data-x="js-EnqueueJob">EnqueueJob</span>:</p>
<p>JavaScript contains an implementation-defined <span
data-x="js-HostEnqueuePromiseJob">HostEnqueuePromiseJob</span>(<var>job</var>, <var>realm</var>)
abstract operation to schedule Promise-related operations. HTML schedules these operations in the
microtask queue. User agents must use the following implementation: <ref spec=JAVASCRIPT></p>

<ol>
<li><p>Assert: <var>queueName</var> is <code data-x="">"PromiseJobs"</code>. (<code
data-x="">"ScriptJobs"</code> must not be used by user agents.)</p></li>

<li><p>Assert: <var>job</var> is either <span>PromiseResolveThenableJob</span> or
<span>PromiseReactionJob</span>. (The following steps would need to be updated if another type of
promise job were introduced.)</p></li>

<li>
<p>Let <var>job settings</var> be determined by switching on <var>job</var>:</p>
<p>If <var>realm</var> is not null, then let <var>job settings</var> be the <span
data-x="concept-realm-settings-object">settings object</span> for <var>realm</var>. Otherwise,
let <var>job settings</var> be null.</p>

<dl class="switch">
<dt><span>PromiseResolveThenableJob</span></dt>
<dd>
<p>The <span data-x="concept-realm-settings-object">settings object</span> for
<var>arguments</var>[2].[[Realm]]. (I.e., the <span data-x="JavaScript realm">Realm</span> of
the <code data-x="">then</code> function.)</p>
</dd>

<dt><span>PromiseReactionJob</span></dt>
<dd>
<p>If <var>arguments</var>[0].[[Handler]] is not undefined, then the <span
data-x="concept-realm-settings-object">settings object</span> of
<var>arguments</var>[0].[[Handler]].[[Realm]]; otherwise, null.</p>
<div class="note">
<p>If <var>realm</var> is not null, it is the <span data-x="JavaScript realm">Realm</span> of
the author code that will run. When <var>job</var> is returned by
<span>NewPromiseReactionJob</span>, it is the realm of the promise's handler function. When
<var>job</var> is returned by <span>NewPromiseResolveThenableJob</span>, it is the realm of
the <code data-x="">then</code> function.

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

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

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

<li><p>Let <var>result</var> be the result of performing the abstract operation specified by
<var>job</var>, using the elements of <var>arguments</var> as its arguments.</p></li>
<li>
<p>Let <var>result</var> be <var>job</var>().</p>

<p class="note"><var>job</var> is an <span>abstract closure</span> returned by
<span>NewPromiseReactionJob</span> or <span>NewPromiseResolveThenableJob</span>.</p>
</li>

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

0 comments on commit 203842a

Please sign in to comment.