From e1af86747f591e33fd0f3f21f301dc866868c9d4 Mon Sep 17 00:00:00 2001 From: Domenic Denicola Date: Tue, 9 Aug 2016 12:06:32 +0900 Subject: [PATCH] Handle error cases during module script tree fetching/running better As discussed in #1545 and in https://esdiscuss.org/topic/moduledeclarationinstantiation-behaviour-after-failure, the spec would potentially call ModuleDeclarationInstantiation on a module that previously failed to instantiate, with cascading bad consequences for the rest of the spec's algorithms. The JavaScript spec expects us to track these failures and avoid calling ModuleDeclarationInstantiation a second time. This commit adds such state tracking, partially by moving the ModuleDeclarationInstantiation calls to fetch time (with any errors stored for later rethrowing during evaluation time). Fixes #1545. --- source | 170 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 144 insertions(+), 26 deletions(-) diff --git a/source b/source index ebd2e9de86b..ab7fe6a890d 100644 --- a/source +++ b/source @@ -85956,6 +85956,27 @@ interface NavigatorOnLine { +
An instantiation state
+ +
+ +

One of "uninstantiated", "errored", or "instantiated", used to prevent reinvocation of ModuleDeclarationInstantiation on modules that + failed to instantiate previously.

+ +
+ +
An instantiation error
+ +
+ +

A JavaScript value, which has meaning only if the instantiation state is "errored".

+ +
+
A credentials mode
@@ -86385,13 +86406,103 @@ interface NavigatorOnLine {
  • Otherwise, result is a module script. Fetch the descendants of result given destination and an ancestor list obtained by appending url to ancestor - list.

  • + list. Wait for fetching the + descendants of a module script to asynchronously complete with descendants + result before proceeding to the next step.

    + +
  • Let record be result's module record.

    + +
  • +

    Let instantiationStatus be record.ModuleDeclarationInstantiation().

    -
  • When fetching the descendants of - a module script asynchronously completes with descendants result, - asynchronously complete this algorithm with descendants result.

  • +

    This step will recursively call ModuleDeclarationInstantiation all of the + module's uninstantiated dependencies.

    + + +
  • +

    For each module script script in result's + uninstantiated inclusive descendant module scripts, perform the following + steps:

    + +
      +
    1. If instantiationStatus is an abrupt completion, then set script's + instantiation state to "errored", its instantiation error to + instantiationStatus.[[Value]], and its module record to null.

    2. + +
    3. Otherwise, set script's instantiation state to "instantiated".

    4. +
    +
  • + +
  • +

    Asynchronously complete this algorithm with descendants result.

    + +

    It is intentional that we complete with descendants result here, and + not result, as this allows us to propagate error signals to the caller.

    +
  • +

    In the above algorithm, a module script script's uninstantiated + inclusive descendant module scripts is a set of module + scripts determined as follows:

    + +
      +
    1. Let module map be script's settings object's + module map.

    2. + +
    3. Let stack be a list containing script.

    4. + +
    5. Let inclusive descendants be an empty set.

    6. + +
    7. +

      While stack is not empty:

      + +
        +
      1. Let current be the last element of stack. Remove it from + stack.

      2. + +
      3. +

        If current is not in inclusive descendants and is not in + stack, then:

        + +
          +
        1. Add current to inclusive descendants.

        2. + +
        3. Let child specifiers be the value of current's module record's [[RequestedModules]] + internal slot.

        4. + +
        5. Let child URLs be the set obtained by calling resolve a module + specifier once for each element of child specifiers, given + current and that element. Omit any failures.

        6. + +
        7. Let child modules be the set obtained by looking up each value in + module map whose key is given by an element of child URLs.

        8. + +
        9. For each s in child modules that is not in inclusive + descendants, add s to stack.

        10. +
        +
      4. +
      +
    8. + +
    9. Return a list containing all elements of inclusive descendants whose instantiation state is "uninstantiated".

    10. +
    + +

    The above algorithm gives a depth-first search of the module dependency graph. The + main interesting part is in how the "edges" of this graph are determined. The actual search + implementation is not important; any other technique for exploring the graph will suffice, given + that the output is an unordered set.

    +

    To fetch a single module script, given a url, a fetch client settings object, a destination, a cryptographic nonce, a parser state, a credentials mode, a module map settings object, a @@ -86539,13 +86650,9 @@ interface NavigatorOnLine { perform the fetch steps, pass those along while performing the internal module script tree fetching procedure.

    -

    If any of the internal module script tree fetching procedure invocations - asynchronously complete with null, the user agent may terminate any or all of the other fetches, and must then - asynchronously complete this algorithm with null.

    - -

    Once all of the internal module script tree fetching procedure invocations - asynchronously complete with a module script, asynchronously complete this +

    Wait for all of the internal module script tree fetching procedure invocations + to asynchronously complete. If any of them asynchronously complete with null, then + asynchronously complete this algorithm with null. Otherwise, asynchronously complete this algorithm with module script.

    @@ -86696,19 +86803,19 @@ interface NavigatorOnLine {
  • Check if we can run script with settings. If this returns "do not run" then abort these steps.

  • -
  • Let record be s's module record.

    - -
  • -

    Let instantiationStatus be record.ModuleDeclarationInstantiation().

    +
  • If s's instantiation + state is "errored", then report the + exception given by s's instantiation error for s + and abort these steps.

  • -

    This step will recursively instantiate all of the module's dependencies.

    - +
  • Assert: s's instantiation state is "instantiated" (and thus its module record is not null).

  • -
  • If instantiationStatus is an abrupt completion, report the - exception given by instantiationStatus.[[Value]] for s and abort - these steps.

  • +
  • Let record be s's module record.

  • Prepare to run script given settings.

  • @@ -86719,10 +86826,11 @@ interface NavigatorOnLine {

    This step will recursively evaluate all of the module's dependencies.

    -
  • If evaluationStatus is an abrupt completion, report the exception - given by evaluationStatus.[[Value]] for s. (Do not perform this step if - ModuleEvaluation fails to complete as a result of the - user agent aborting the running script.)

  • +
  • If evaluationStatus is an abrupt completion, then report the + exception given by evaluationStatus.[[Value]] for s. (Do not perform + this step if ModuleEvaluation fails to complete as a + result of the user agent aborting the running + script.)

  • Clean up after running script with settings.

  • @@ -87506,6 +87614,16 @@ document.querySelector("button").addEventListener("click", bound); data-x="">fetching", throw a TypeError exception and abort these steps.

    +
  • If resolved module script's instantiation state is "errored", then throw resolved module script's instantiation error.

  • + +
  • Assert: resolved module script's instantiation state is "instantiated" (and thus its module record is not null).

  • +
  • Return resolved module script's module record.