From 5450b14b5989b0d6cd0bf686616f3dd335c691cd Mon Sep 17 00:00:00 2001 From: Daniel Ehrenberg Date: Sat, 7 Nov 2020 16:53:29 +0100 Subject: [PATCH] [WIP] JavaScript Realm proposal integration This patch gives the TC39 JavaScript Realm proposal semantics in HTML. Realms are given an environment settings object, which is especially meaningful for module loading. Realms created with the JavaScript Realm API are identified as "synthetic" realms, whereas Realms which implement the Window or WorkerGlobalObject interfaces are identified as "principal" realms. All synthetic realms have an associated principal realm. HTML/Web state and behavior which was previously keyed by realm is instead generally keyed by principal realm, including the association with an environment settings object, with the exception of the module map. Each realm, both synthetic and principal realms, contain their own module map. This is because JavaScript modules close over a global object, and it may be useful to run a module in the context of a synthetic realm. The Realm proposal is currently at Stage 2, and this patch should not land until, at the earliest, the proposal is at Stage 3. --- source | 381 ++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 294 insertions(+), 87 deletions(-) diff --git a/source b/source index 5130a613861..0b01262c112 100644 --- a/source +++ b/source @@ -2743,7 +2743,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute agent cluster
  • automatic semicolon insertion
  • candidate execution
  • -
  • The current Realm Record
  • +
  • The current Realm Record
  • early error
  • forward progress
  • invariants of the essential internal methods
  • @@ -2846,6 +2846,7 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute
  • The RunJobs abstract operation
  • The SameValue abstract operation
  • The ScriptEvaluation abstract operation
  • +
  • The SetDefaultGlobalBindings abstract operation
  • The SetImmutablePrototype abstract operation
  • The ToBoolean abstract operation
  • The ToString abstract operation
  • @@ -2878,6 +2879,16 @@ a.setAttribute('href', 'https://example.com/'); // change the content attribute

    User agents that support JavaScript must also implement the Top-Level Await proposal.

    + +

    User agents that support JavaScript must also implement the Realm + proposal. The following term is defined there, and used in this specification:

    + + @@ -10380,8 +10391,8 @@ document.createElement("bad-1"); // (2)
    1. Let element be the result of internally creating a new object implementing the interface - to which the active function object corresponds, given the current Realm - Record and NewTarget.

    2. + to which the active function object corresponds, given the current principal + realm and NewTarget.

    3. Set element's node document to the current global object's associated @@ -79937,7 +79948,7 @@ console.assert(iframeWindow.frameElement === null);

    4. Let value be originalDesc.[[Value]].

    5. If ! IsCallable(value) is true, then set value to - an anonymous built-in function, created in the current Realm Record, that + an anonymous built-in function, created in the current principal Realm, that performs the same steps as the IDL operation P on object O.

    6. Set crossOriginDesc to PropertyDescriptor{ @@ -79955,14 +79966,14 @@ console.assert(iframeWindow.frameElement === null);

    7. Let crossOriginGet be undefined.

    8. If e.[[NeedsGet]] is true, then set crossOriginGet to an - anonymous built-in function, created in the current Realm Record, that + anonymous built-in function, created in the current principal Realm, that performs the same steps as the getter of the IDL attribute P on object O.

    9. Let crossOriginSet be undefined.

    10. If e.[[NeedsSet]] is true, then set crossOriginSet to an - anonymous built-in function, created in the current Realm Record, that + anonymous built-in function, created in the current principal Realm, that performs the same steps as the setter of the IDL attribute P on object O.

    11. @@ -87663,7 +87674,7 @@ new PaymentRequest(…); // Allowed to use
    12. Set the current entry to entry.

    13. -
    14. Let targetRealm be the current Realm Record.

    15. +
    16. Let targetRealm be the current principal Realm.

    17. Let state be null.

    18. @@ -88559,7 +88570,7 @@ interface BeforeUnloadEvent : Event { data-x="">This pointer is not yet defined in the JavaScript specification; see tc39/ecma262#1357.

      -

      The agent equivalent of the current Realm Record is the +

      The agent equivalent of the current principal Realm is the surrounding agent.

      @@ -88781,16 +88792,29 @@ interface BeforeUnloadEvent : Event {

      Realms and their counterparts

      The JavaScript specification introduces the realm - concept, representing a global environment in which script is run. Each realm comes with an - implementation-defined global object; much of this specification is - devoted to defining that global object and its properties.

      + concept, representing a global environment in which script is run.

      + +

      In the context of the JavaScript Realms API proposal, there are two kinds of realms:

      + +

      For web specifications, it is often useful to associate values or algorithms with a - realm/global object pair. When the values are specific to a particular type of realm, they are - associated directly with the global object in question, e.g., in the definition of the + principal Realm/global object pair. When the values are specific to a particular type of realm, + they are associated directly with the global object in question, e.g., in the definition of the Window or WorkerGlobalScope interfaces. When the values have utility across multiple realms, we use the environment settings object concept.

      +

      Each synthetic Realm has an associated principal Realm from which it + originated. In contexts where the associated values or algorithms of a synthetic Realm are needed, + refer instead to the associated principal Realm.

      +

      Finally, in some cases it is necessary to track associated values before a realm/global object/environment settings object even comes into existence (for example, during navigation). These values are tracked in the @@ -88964,32 +88988,40 @@ interface BeforeUnloadEvent : Event {

      A global object is a JavaScript object that is the [[GlobalObject]] field of a JavaScript realm.

      -

      In this specification, all JavaScript - realms are created with global objects that are either Window or - WorkerGlobalScope objects.

      +

      A JavaScript realm's global object is found in its [[GlobalObject]] field. A global + object's Realm is the unique + JavaScript realm whose global object is that object.

      + +

      A principal global object is a global object of a principal + Realm. Similarly, a synthetic global object is a global object of a + synthetic Realm.

      + +

      In this specification, all principal JavaScript + Realms are created with global objects that are either Window, + WorkerGlobalScope or WorkletGlobalScope objects.

      -

      There is always a 1-to-1-to-1 mapping between JavaScript - realms, global objects, and environment settings objects:

      +

      There is always a 1-to-1-to-1 mapping between principal + Realms, principal global objects, and environment settings objects:

    +
    Synthetic realm settings objects
    + +

    Each synthetic Realm has an associated synthetic realm settings + object with the following fields:

    + +
    +

    A principal Realm

    + +

    The principal Realm which this synthetic realm exists within.

    + +

    An underlying Realm

    + +

    The synthetic realm which this settings object represents.

    + +

    A module map

    + +

    A module map that is used when importing JavaScript modules.

    +
    + +

    Synthetic Realms are created with global objects which are initialized by the JavaScript specification's + SetDefaultGlobalBindings algorithm. Synthetic + global objects do not implement any WebIDL interface.

    + +

    Analogously, there is always a 1-to-1-to-1 mapping between synthetic Realms, synthetic global + objects, and synthetic realm settings + objects:

    + + + +

    The principal Realm of any JavaScript + Realm realm is defined by the following algorithm:

    + +
      +
    1. If realm.[[HostDefined]] is a synthetic realm settings object, then + realm is a synthetic Realm; set realm to the principal Realm of + realm.[[HostDefined]].

    2. + +
    3. Assert: realm.[[HostDefined]] is an environment settings object, + and realm is a principal Realm.

    4. + +
    5. Return realm.

    6. +
    + +

    The module map of a JavaScript Realm + realm is defined by the following algorithm:

    + +
      +
    1. If realm is a principal Realm, return the module map of the environment settings object of + realm.

    2. + +
    3. Assert: realm is a synthetic Realm.

    4. + +
    5. Return the module map of the + synthetic realm settings object of realm.

    6. +
    +

    When defining algorithm steps throughout this specification, it is often important to indicate - what JavaScript realm is to be used—or, equivalently, what global object - or environment settings object is to be used. In general, there are at least four - possibilities:

    + what principal Realm is to be used—or, equivalently, what principal global + object or environment settings object is to be used. In general, there are at + least four possibilities:

    Entry
    @@ -89069,8 +89189,8 @@ interface BeforeUnloadEvent : Event {
    Current
    This corresponds to the currently-running function object, including built-in user-agent - functions which might not be implemented as JavaScript. (It is derived from the current JavaScript realm.)
    + functions which might not be implemented as JavaScript. (It is derived from the current + underlying Realm.)
    Relevant
    Every platform object has a relevant @@ -89149,7 +89269,7 @@ interface BeforeUnloadEvent : Event { data-x="">a.html.

  • The incumbent Realm is that of b.html.

  • -
  • The current Realm is that of

    The current principal Realm is that of c.html (since it is the print() method from c.html whose code is running).

  • The relevant Realm of the object on which @@ -89197,7 +89317,7 @@ interface BeforeUnloadEvent : Event { </script>

    If the algorithm for the getBattery() method - had instead used the current Realm, all the results + had instead used the current principal Realm, all the results would be reversed. That is, after the first call to getBattery() in outer.html, the Navigator object in inner.html would be permanently storing @@ -89226,7 +89346,8 @@ interface BeforeUnloadEvent : Event {

    With this in hand, we define the entry execution context to be the most recently pushed item in the JavaScript execution context stack that is a realm execution context. The entry - Realm is the entry execution context's Realm component.

    + Realm is the principal Realm of the + entry execution context's Realm component.

    Then, the entry settings object is the environment settings object of the BeforeUnloadEvent : Event {

  • Return context's Realm component's principal realm's settings object.

  • @@ -89505,15 +89627,29 @@ document.querySelector("button").addEventListener("click", bound);
    Current
    -

    The JavaScript specification defines the current Realm Record, sometimes - abbreviated to the "current Realm".

    +

    The JavaScript specification defines the current underlying Realm algorithm, + referred to in that specification as the "current Realm Record".

    + +

    The current principal Realm is the principal Realm of the current underlying + Realm.

    + +

    This specification and other web specifications avoid the term "current + Realm", as it is ambiguous whether the intention is to refer to the current principal + realm or the current underlying Realm. By contrast, the entry, incumbent and + relevant concepts always refer to a principal Realm. Almost all uses of the "current" + concept need to refer to the current principal Realm as well, with the exception of + JavaScript module processing, as synthetic Realms have an + independent module map. If any reference to the "current Realm" exists in another Web + specification, the intention is likely to be to refer to the current principal + Realm.

    Then, the current settings object is the environment settings object of the current - Realm Record.

    + principal Realm
    .

    Similarly, the current global object is the global object of the current Realm Record.

    + data-x="concept-realm-global">global object of the current principal Realm.

    Relevant
    @@ -89634,10 +89770,12 @@ document.querySelector("button").addEventListener("click", bound); data-x="struct">structs. All scripts have:

    -
    A settings object
    +
    A Realm
    -

    An environment settings object, containing various settings that are shared - with other scripts in the same context.

    +

    A JavaScript Realm where the script is evaluated, which is shared + with other scripts in the same context. Note that, + in the case of module scripts (but not classic scripts) this realm may be a synthetic Realm.

    A record
    @@ -89690,6 +89828,10 @@ document.querySelector("button").addEventListener("click", bound); scripts.

    +

    The settings object of a script is the + settings object of the principal realm of the script's + realm

    A classic script is a type of script that has the following additional item:

    @@ -90063,11 +90205,11 @@ document.querySelector("button").addEventListener("click", bound);
    1. Fetch a single module script given url, settings - object, "script", options, settings object, - "client", and with the top-level module fetch flag set. If the - caller of this algorithm specified custom perform - the fetch steps, pass those along as well. Wait until the algorithm asynchronously - completes with result.

    2. + object, "script", options, settings object's + realm, "client", + and with the top-level module fetch flag set. If the caller of this algorithm specified + custom perform the fetch steps, pass those + along as well. Wait until the algorithm asynchronously completes with result.

    3. If result is null, asynchronously complete this algorithm with null, and return.

    4. @@ -90082,9 +90224,9 @@ document.querySelector("button").addEventListener("click", bound);

    To fetch an import() module script graph given a specifier, a base - URL, a settings object, and some options, run these steps. The - algorithm will asynchronously complete with either null (on failure) or a module - script (on success).

    + URL, a settings object, some fetch options options and a module + map realm, run these steps. The algorithm will asynchronously complete with either null (on + failure) or a module script (on success).

    1. Let url be the result of resolving a @@ -90094,7 +90236,7 @@ document.querySelector("button").addEventListener("click", bound); return.

    2. Fetch a single module script given url, settings - object, "script", options, settings object, + object, "script", options, module map realm, "client", and with the top-level module fetch flag set. If the caller of this algorithm specified custom perform the fetch steps, pass those along as well. Wait until the algorithm asynchronously @@ -90119,7 +90261,8 @@ document.querySelector("button").addEventListener("click", bound);

      1. Fetch a single module script given url, settings - object, destination, options, settings object, ", destination, options, settings object's + realm, "client", and with the top-level module fetch flag set. Wait until algorithm asynchronously completes with result.

      2. @@ -90152,8 +90295,8 @@ document.querySelector("button").addEventListener("click", bound);
        1. Let script be the result of creating a module script using - source text, settings object, base URL, and - options.

        2. + source text, settings object's realm, base URL, and options.

        3. If script is null, asynchronously complete this algorithm with null, and return.

        4. @@ -90349,11 +90492,11 @@ document.querySelector("button").addEventListener("click", bound); policy is the empty string.

        5. Fetch a single module script given url, fetch client settings - object, destination, options, module map settings object, - "client", and with the top-level module fetch flag set. If the - caller of this algorithm specified custom perform - the fetch steps, pass those along as well. Wait until the algorithm asynchronously - completes with result.

        6. + object, destination, options, module map settings object's + realm, "client", + and with the top-level module fetch flag set. If the caller of this algorithm specified + custom perform the fetch steps, pass those + along as well. Wait until the algorithm asynchronously completes with result.

        7. If result is null, asynchronously complete this algorithm with null, and return.

        8. @@ -90472,9 +90615,9 @@ document.querySelector("button").addEventListener("click", bound);

          For each url in urls, perform the internal module script graph fetching procedure given url, fetch client settings object, destination, options, module - script's settings object, visited set, and module - script's base URL. If the caller of this - algorithm specified custom perform the + script's realm, visited set, and + module script's base URL. If the caller + of this algorithm specified custom perform the fetch steps, pass those along while performing the internal module script graph fetching procedure.

          @@ -90493,7 +90636,7 @@ document.querySelector("button").addEventListener("click", bound);

          To perform the internal module script graph fetching procedure given a url, a fetch client settings object, a destination, some - options, a module map settings object, a visited set, and a + options, a module map realm, a visited set, and a referrer, perform these steps. The algorithm will asynchronously complete with either null (on failure) or a module script (on success).

          @@ -90502,7 +90645,7 @@ document.querySelector("button").addEventListener("click", bound); url.

        9. Fetch a single module script given url, fetch client settings - object, destination, options, module map settings object, + object, destination, options, module map realm, referrer, and with the top-level module fetch flag unset. If the caller of this algorithm specified custom perform the fetch steps, pass those along while fetching a @@ -90525,13 +90668,13 @@ document.querySelector("button").addEventListener("click", bound);

          To fetch a single module script, given a url, a fetch client settings object, a destination, some options, a module map - settings object, a referrer, and a top-level module fetch flag, run - these steps. The algorithm will asynchronously complete with either null (on failure) or a - module script (on success).

          + realm, a referrer, and a top-level module fetch flag, run these steps. + The algorithm will asynchronously complete with either null (on failure) or a module + script (on success).

            -
          1. Let moduleMap be module map settings object's module map.

          2. +
          3. Let moduleMap be module map realm's module map.

          4. If moduleMap[url] is "fetching", wait in parallel until that entry's value changes, then queue a task on @@ -90604,7 +90747,7 @@ document.querySelector("button").addEventListener("click", bound); decoding response's body.

          5. Let module script be the result of creating a module script given - source text, module map settings object, response's source text, module map realm, response's url, and options.

          6. @@ -90623,8 +90766,9 @@ document.querySelector("button").addEventListener("click", bound); moduleScript and an optional discoveredSet:

              -
            1. Let moduleMap be moduleScript's settings object's - module map.

            2. +
            3. Let moduleMap be moduleScript's realm's module + map.

            4. If discoveredSet was not given, let it be an empty set.

            5. @@ -90697,7 +90841,8 @@ document.querySelector("button").addEventListener("click", bound);
            6. Let script be a new classic script that this algorithm will subsequently initialize.

            7. -
            8. Set script's settings object to settings.

            9. +
            10. Set script's Realm to + settings's Realm.

            11. Set script's base URL to baseURL.

            12. @@ -90738,9 +90883,9 @@ document.querySelector("button").addEventListener("click", bound);

            To create a module script, given a - string source, an environment settings object - settings, a URL baseURL, and some script fetch - options options:

            + string source, a JavaScript Realm realm, a + URL baseURL, and some script fetch options + options:

            1. If scripting is disabled for @@ -90749,7 +90894,8 @@ document.querySelector("button").addEventListener("click", bound);

            2. Let script be a new module script that this algorithm will subsequently initialize.

            3. -
            4. Set script's settings object to settings.

            5. +
            6. Set script's Realm to + realm.

            7. Set script's base URL to baseURL.

            8. @@ -91466,7 +91612,8 @@ dictionary PromiseRejectionEventInit : EventInit
            9. If realm is not null, then let job settings be the settings object for realm. Otherwise, + data-x="concept-realm-settings-object">settings object for the principal Realm of realm. Otherwise, let job settings be null.

              @@ -91800,6 +91947,13 @@ import "https://example.com/foo/../module2.mjs";
            10. Let fetch options be the default classic script fetch options.

            11. +
            12. +

              Let module map realm be the current underlying Realm.

              + +

              In the case of the Realm.prototype.import API the current + underlying Realm is set to the appropriate synthetic realm.

              +
            13. +
            14. If referencingScriptOrModule is not null, then:

              @@ -91816,6 +91970,15 @@ import "https://example.com/foo/../module2.mjs";
            15. Set fetch options to the descendant script fetch options for referencing script's fetch options.

            16. + +
            17. +

              Set module map realm to referencing script's Realm

              + +

              In the case of a dynamic import nested within a module loaded through the + Realm.prototype.import API, the realm of the script is set to the appropriate + synthetic realm.

              +

            As explained above for HostResolveImportedModule, in the common @@ -91823,8 +91986,8 @@ import "https://example.com/foo/../module2.mjs";

          7. Fetch an import() module script graph given specifier, base - URL, settings object, and fetch options. Wait until the algorithm - asynchronously completes with result.

          8. + URL, settings object, fetch options and module map realm. + Wait until the algorithm asynchronously completes with result.

          9. Let promise be null. @@ -91853,6 +92016,14 @@ import "https://example.com/foo/../module2.mjs";

          10. Let base URL be settings object's API base URL.

          11. +
          12. +

            Let module map realm be the current underlying Realm.

            + +

            This statement will never be the way that a synthetic realm + is used because in the Realm.prototype.import case, the following conditional + will be taken.

            +
          13. +
          14. If referencingScriptOrModule is not null, then:

            @@ -91865,6 +92036,15 @@ import "https://example.com/foo/../module2.mjs";
          15. Set base URL to referencing script's base URL.

          16. + +
          17. +

            Set module map realm to referencing script's Realm.

            + +

            In the case of a module loaded from an import statement nested + within a call to Realm.prototype.import, module map realm might be a + synthetic Realm.

            +
          @@ -91882,8 +92062,8 @@ import "https://example.com/foo/../module2.mjs";
        10. -
        11. Let moduleMap be settings object's module map.

        12. +
        13. Let moduleMap be module map realm's module map.

        14. Let url be the result of resolving a module specifier given base URL and specifier.

        15. @@ -91906,6 +92086,30 @@ import "https://example.com/foo/../module2.mjs"; data-x="concept-script-record">record.

        +
        HostInitializeSyntheticRealm(realm)
        + +

        JavaScript contains an implementation-defined HostInitializeSyntheticRealm(realm) + abstract operation. User agents must use the following implementation:

        + +
          +
        1. Let settings be a new synthetic realm settings object that this + algorithm will subsequently initialize.

        2. + +
        3. Set settings's principal realm to the + current principal Realm.

        4. + +
        5. Set settings's underlying realm to + realm.

        6. + +
        7. Set settings's module + map to a new module map, initially empty.

        8. + +
        9. Set realm's [[HostDefined]] field to settings.

        10. +
        +

        Event loops

        @@ -92859,7 +93063,7 @@ import "https://example.com/foo/../module2.mjs";
    3. -
    4. Let realm be the current Realm Record.

    5. +
    6. Let realm be the current principal Realm.

    7. Let p be a new promise.

    8. @@ -94861,7 +95065,7 @@ enum DOMParserSupportedType {
    9. If previous handle was not provided, add an entry to the list of active timers for handle.

    10. -
    11. Let callerRealm be the current Realm Record, and +

    12. Let callerRealm be the current principal Realm, and calleeRealm be method context's JavaScript realm.

    13. Let initiating script be the active script.

    14. @@ -124363,6 +124567,9 @@ INSERT INTERFACES HERE
      [JSON]
      The JavaScript Object Notation (JSON) Data Interchange Format, T. Bray. IETF.
      +
      [JSREALMS]
      +
      Realms. Ecma International.
      +
      [LONGTASKS]
      Long Tasks, D. Denicola, I. Grigorik, S. Panicker. W3C.