Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Handle asynchronous modules (top-level await). #1444

Merged
merged 3 commits into from
Jun 11, 2021
Merged

Conversation

Ms2ger
Copy link
Contributor

@Ms2ger Ms2ger commented Jun 25, 2019

Fixes #1407.


Preview | Diff

@jakearchibald
Copy link
Contributor

Is top-level await only allowed in module scripts?

@Ms2ger
Copy link
Contributor Author

Ms2ger commented Jun 25, 2019

Yes, that's correct.

Copy link
Contributor

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. @annevk are you happy with this as the final approach? Top level await is allowed, but any listeners added after an await will throw.

Copy link

@littledan littledan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This patch has the semantics I was imagining. It's probably worth including a note about the unfortunate compatibility situation leading to the synchronous check of the promise (which is generally a design anti-pattern).

Or, to the folks here more familiar with ServiceWorker usage: would it be OK to skip the synchronous error check, and just rely on the error being reported a bit later, and letting the rest of the initialization algorithm complete as if the module loaded successfully?

docs/index.bs Outdated Show resolved Hide resolved
@jakearchibald
Copy link
Contributor

would it be OK to skip the synchronous error check, and just rely on the error being reported a bit later,

I don't think so. When we first install a service worker, if it throws an error during initial execution, we discard it.

@annevk
Copy link
Member

annevk commented Jun 27, 2019

I'm probably missing something, but how could the timing be observed? (In particular for the error case. I don't quite understand why it matters that the check there is synchronous or how that is observable.)

Can you add listeners before an await? At what point are they invoked?

@jakearchibald
Copy link
Contributor

jakearchibald commented Jun 28, 2019

Here's how I think this works: (@littledan: correct me if I'm wrong)

When we run a service worker, we consider it as 'evaluated' and ready to receive events after synchronous execution + microtasks.

Errors & evaluation

This error will prevent a service worker from installing:

throw Error('…');

As will this:

await undefined;

throw Error('…');

As will this:

import './foo.js';

// If `./foo.js` is:
throw Error('…');

However, this error will not prevent a service worker from installing:

await new Promise(r => setTimeout(r));

throw Error('…');

…as the error has happened after 'evaluation'.

Adding listeners

// This is fine
addEventListener('fetch', func);

// This is fine
await undefined;
addEventListener('fetch', func);

// This shows a warning, and the fetch listener isn't added.
await new Promise(r => setTimeout(r));
addEventListener('fetch', func);

However, the throw is asynchronous, so it wouldn't prevent install.

Dispatching events

We consider the service worker ready to receive events once it's evaluated:

addEventListener('fetch', () => {
  console.log('A');
});

await new Promise(r => setTimeout(r, 5000));
console.log('B');

The console may log A before B.

Concerns

import './third-party-script-v1.js';

addEventListener('install', event => );
addEventListener('fetch', event => );

The above works as the developer expects. However, later, they update to './third-party-script-v2.js'. This include a top-level await that waits beyond a microtask.

Now, their service worker updates successfully, but their install and fetch listeners are absent. They'll show as an error in the console, but the developer may miss them.

@annevk
Copy link
Member

annevk commented Jun 28, 2019

Thank you! "Concerns" seems pretty bad from a debugging perspective, i.e., action at a distance.

To address @littledan's concern, we could perhaps race the evaluationPromise with another promise, but maybe that introduces issues for the remainder of the algorithm. (I was also thinking of this as a way to allow top-level await, but only for a limited number of ticks.)

This whole setup still seems rather suboptimal for the design of service workers.

@jakearchibald
Copy link
Contributor

I was also thinking of this as a way to allow top-level await, but only for a limited number of ticks

What would we do if it exceeded that number of ticks?

This whole setup still seems rather suboptimal for the design of service workers.

Yeah, it still feels like we'd have to tell developers "you can do this but oh god please don't do this".

Looking at the PR, we could fail the install of a service worker if its |evaluationPromise|.\[[PromiseState]] is "pending". This would effectively prevent top-level await in service workers at install time. A top-level await could be introduced later, using something like if (Date.now() > …), in which case we'd behave as above.

@annevk
Copy link
Member

annevk commented Jun 28, 2019

I was thinking that if you exceed the ticks it's equivalent to an evaluation error. So you're allowed to do some minor queueMicrotask()-type stuff, but nothing else.

(At some point we need to have a conversation with TC39 about language extensions and the different types of host environments. Not everything that is great for documents/dedicated workers/servers necessarily makes sense for workets/service workers.)

@jakearchibald
Copy link
Contributor

I was thinking that if you exceed the ticks it's equivalent to an evaluation error. So you're allowed to do some minor queueMicrotask()-type stuff, but nothing else.

I thought we already got microtasks for free since they're handled just after "executing as script". The bit I'd be worried about is:

addEventListener('fetch', );
await usuallyFast();

…which means you have a service worker that usually works, but sometimes doesn't.

@littledan
Copy link

Yeah, these are the semantics I was suggesting. Another possibility (based on a suggestion from @annevk) would be to have a per-agent variable which sets whether TLA will currently be allowed. We could make the module link algorithm check this flag, and make the flag disable TLA just while initializing the module, and reenable it later. How does this sound?

Copy link
Contributor

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Going to unapprove this until we figure out the behaviour we want at TPAC next week.

@Ms2ger
Copy link
Contributor Author

Ms2ger commented Sep 20, 2019

With the changes in tc39/proposal-top-level-await#128 and whatwg/html#4352, I believe this implements the new semantics.

@annevk
Copy link
Member

annevk commented Sep 23, 2019

Is there no way to avoid looking at the internals of a promise? If we really need to do that perhaps provide an abstraction in IDL? At the moment TC39 will only alert IDL and HTML of changes to such things (if it happens at all).

@littledan
Copy link

In previous drafts, the result of evaluating a module was either a Promise or a synchronous reaction. Following feedback from @domenic, this was made to always return a single Promise. We could go back to the old way, to avoid looking at Promise internals here, though it would add a couple lines of spec algorithm to HTML.

Copy link
Contributor

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks good. Just one question (see review).

: Output
:: a boolean

1. If |record| is not a [=Cyclic Module Record=], then:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm struggling to figure out what a "Cyclic Module Record" is. What's the difference between a Cyclic Module Record and a non-Cyclic Module Record? How does each relate to a module?

Copy link

@littledan littledan Jan 22, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

JavaScript modules (Source Text Module Records) had a bunch of their functionality for traversing the module graph refactored up into a spec-internal superclass called Cyclic Module Records, so that WebAssembly modules could share the logic. Not all modules use this infrastructure, for example, JSON modules are leaves so they don't bother.

Copy link
Contributor

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

Are there any tests for this? If not I can write some.

@littledan
Copy link

@jakearchibald I think we're missing tests for this as well as whatwg/html#4352 , making it a rather big project. I'd welcome help here!

docs/index.bs Outdated
1. Let |evaluationPromise| be the result of <a lt="run a module script">running the module script</a> |script|, with report errors set to false.
1. Assert: |evaluationPromise|.\[[PromiseState]] is not "pending".
1. If |evaluationPromise|.\[[PromiseState]] is "rejected":
1. Set |startFailed| to true and abort these steps.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should set |evaluationStatus| to an abrupt completion here, i.e. shouldn't set |startFailed| to true, because in classic scripts (and module scripts before this PR) don't set |startFailed| on abrupt completion.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @hiroshige-g. This is subtle, but throwing an error during service worker script evaluation doesn't count as a startup failure in the current spec. The service worker thread is running and will get events dispatched to it. Throwing an error only affects the Install algorithm; the service worker will subsequently be terminated and will not be installed. If we want to change this behavior, we should do so for classic scripts as well as module scripts.

1. Invoke [=Reject Job Promise=] with |job| and `TypeError`.

Note: This will do nothing if [=Reject Job Promise=] was previously invoked with "{{SecurityError}}" {{DOMException}}.
1. If |script| is null or [=Is Async Module=] with |script|'s [=script/record=], |script|'s [=script/base URL=], and « » is true, then:

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A random idea: How about checking whether |evaluationPromise|.\[[PromiseState]] is "pending" instead of [=Is Async Module=] ... is true?

This makes the logic a little simpler (using only |evaluationPromise|.\[[PromiseState]], removing module graph traversal and module inspection), but this is not equivalent from the current condition (e.g. for modules with await undefined).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A goal of the design here was to not be "racy" in the sense that, both asynchronous behavior and errors are based on the module syntax, not whether it has already run or not, or whether it dynamically reached an await. This would make behavior more stable.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, it makes sense > based on syntax.

pull bot pushed a commit to ashu8912/v8 that referenced this pull request Oct 7, 2020
This is a predicate checking if any module in a module graph is [[Async]], i.e.
contains a top-level await. It is needed for ServiceWorker integration, as
ServiceWorkers disallows top-level await in its modules to prevent stalling
during registration.

w3c/ServiceWorker#1444

Bug: v8:9344
Change-Id: Id84489bc73717b4c9950059c8ff6def9297499d0
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2451212
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Reviewed-by: Ulan Degenbaev <ulan@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70390}
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this pull request Oct 9, 2020
Top-level await is prohibited in service workers and causes the
Update algorithm to reject with a TypeError according to the
spec PR [1].

This CL also fixes error handling in SWs after top-level await
integration. Since this rejection happens before evaluation,
all Promises returned by module evaluation are synchronously
settled. (Module evaluation always returns Promises after
top-level await integration). [1] treats synchronously rejected
Promises the same as errors thrown from sync module scripts without
top-level await. This CL implements the new behavior.

[1] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this pull request Oct 9, 2020
Top-level await is prohibited in service workers and causes the
Update algorithm to reject with a TypeError according to the
spec PR [1].

This CL also fixes error handling in SWs after top-level await
integration. Since this rejection happens before evaluation,
all Promises returned by module evaluation are synchronously
settled. (Module evaluation always returns Promises after
top-level await integration). [1] treats synchronously rejected
Promises the same as errors thrown from sync module scripts without
top-level await. This CL implements the new behavior.

[1] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
chromium-wpt-export-bot pushed a commit to web-platform-tests/wpt that referenced this pull request Dec 2, 2020
This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832849}
blueboxd pushed a commit to blueboxd/chromium-legacy that referenced this pull request Dec 2, 2020
This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832849}
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Dec 5, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019
moz-v2v-gh pushed a commit to mozilla/gecko-dev that referenced this pull request Dec 7, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019
sidvishnoi pushed a commit to sidvishnoi/gecko-webmonetization that referenced this pull request Dec 10, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019
sidvishnoi pushed a commit to sidvishnoi/gecko-webmonetization that referenced this pull request Dec 10, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <syg@chromium.org>
Reviewed-by: Dominic Farolino <dom@chromium.org>
Reviewed-by: Makoto Shimazu <shimazu@chromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshige@chromium.org>
Cr-Commit-Position: refs/heads/master@{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified-and-comments-removed that referenced this pull request Dec 14, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <sygchromium.org>
Reviewed-by: Dominic Farolino <domchromium.org>
Reviewed-by: Makoto Shimazu <shimazuchromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshigechromium.org>
Cr-Commit-Position: refs/heads/master{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019

UltraBlame original commit: 259caf0f5dd8d0d96bc5604e0b5f0ad11f16faf1
gecko-dev-updater pushed a commit to marco-c/gecko-dev-comments-removed that referenced this pull request Dec 14, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <sygchromium.org>
Reviewed-by: Dominic Farolino <domchromium.org>
Reviewed-by: Makoto Shimazu <shimazuchromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshigechromium.org>
Cr-Commit-Position: refs/heads/master{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019

UltraBlame original commit: 259caf0f5dd8d0d96bc5604e0b5f0ad11f16faf1
gecko-dev-updater pushed a commit to marco-c/gecko-dev-wordified that referenced this pull request Dec 14, 2020
…it in service workers, a=testonly

Automatic update from web-platform-tests
[top-level await] Prohibit top-level await in service workers

This CL disallows top-level await for service workers by
terminating the worker.

The error that's thrown when Run Service Worker fails is changed
to a TypeError per the spec draft for top-level await integration [0].

Note that this also changes the sudden termination errors that were
previously AbortErrors to TypeErrors.

[0] w3c/ServiceWorker#1444

Bug: 1129795
Bug: 1022182
Bug: v8:9344
Change-Id: Ia764ab553b88e52d48125f23632dc77f4c256009
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2454306
Commit-Queue: Shu-yu Guo <sygchromium.org>
Reviewed-by: Dominic Farolino <domchromium.org>
Reviewed-by: Makoto Shimazu <shimazuchromium.org>
Reviewed-by: Hiroshige Hayashizaki <hiroshigechromium.org>
Cr-Commit-Position: refs/heads/master{#832849}

--

wpt-commits: aa77795ebe17dc0d7c3259a3fe92023b9da62d3e
wpt-pr: 26019

UltraBlame original commit: 259caf0f5dd8d0d96bc5604e0b5f0ad11f16faf1
@Ms2ger Ms2ger requested a review from jakearchibald January 11, 2021 16:52
@jakearchibald
Copy link
Contributor

@mfalken could you take a look at this too?

Copy link
Contributor

@jakearchibald jakearchibald left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good aside from the undefined. Also, are there any tests?

docs/index.bs Outdated Show resolved Hide resolved
docs/index.bs Outdated Show resolved Hide resolved
docs/index.bs Outdated Show resolved Hide resolved
docs/index.bs Outdated Show resolved Hide resolved
docs/index.bs Show resolved Hide resolved
Copy link
Contributor Author

@Ms2ger Ms2ger left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know off-hand what the testing situation is. Can check later.

docs/index.bs Show resolved Hide resolved
@Ms2ger
Copy link
Contributor Author

Ms2ger commented Jan 20, 2021

A test was added at web-platform-tests/wpt#26019; thanks @codehag for pointing it out to me. Let me know if you want me to look into adding more tests.

Copy link
Member

@mfalken mfalken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry for the delay! I needed some time to understand this.

Can the commit description elaborate more on what the change is (the bug thread is very long). It sounds like this is implementing #1407 (comment), is that right?

docs/index.bs Outdated
1. Let |evaluationPromise| be the result of <a lt="run a module script">running the module script</a> |script|, with report errors set to false.
1. Assert: |evaluationPromise|.\[[PromiseState]] is not "pending".
1. If |evaluationPromise|.\[[PromiseState]] is "rejected":
1. Set |startFailed| to true and abort these steps.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree with @hiroshige-g. This is subtle, but throwing an error during service worker script evaluation doesn't count as a startup failure in the current spec. The service worker thread is running and will get events dispatched to it. Throwing an error only affects the Install algorithm; the service worker will subsequently be terminated and will not be installed. If we want to change this behavior, we should do so for classic scripts as well as module scripts.

docs/index.bs Outdated
1. If |evaluationPromise|.\[[PromiseState]] is "rejected":
1. Set |startFailed| to true and abort these steps.

Note: This is intended to maintain compatibilty with failing synchronous modules before the introduction of asynchronous modules.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand this point, and it seems to be opposite to what @hiroshige-g is saying. Does throwing an error in the synchronous module case cause |startFailed| to be true?

(If we keep the note, there's a typo "compatibilty".)

docs/index.bs Outdated
1. Return false.
1. If |record|.\[[Async]] is true, then:
1. Return true.
1. [=list/For each=] string |requested| of |record|.\[[RequestedModules]],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: use a colon instead of a comma at the end for consistency

docs/index.bs Outdated
1. Assert: |url| is never failure, because [=resolve a module specifier|resolving a module specifier=] must have been previously successful with these same two arguments.
1. If |seen| does not [=set/contain=] |url|, then:
1. [=set/Append=] |url| to |seen|.
1. If [=Is Async Module=] for |moduleMap|[|url|]'s [=script/record=], |moduleMap|, |base|, and |seen| is true, then
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: colon at end

Base automatically changed from master to main February 4, 2021 19:56
1. Return true.
1. [=list/For each=] string |requested| of |record|.\[[RequestedModules]],
1. Let |url| be the result of [=resolve a module specifier|resolving a module specifier=] given |base| and |requested|.
1. Assert: |url| is never failure, because [=resolve a module specifier|resolving a module specifier=] must have been previously successful with these same two arguments.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

During implementation in Chrome we realized that linking failures means this assert can trip. If the module graph couldn't be successfully linked, we can't assume the entire module graph is accessible this way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not really familiar with the module loading stuff. Any idea what spec change is needed here?

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't grok the module loading stuff enough myself to be confident with any suggested fix. I don't think resolving a module specifier itself can fail, but that a module graph could have failed to Link(). In which case a requested module's url may not map to any module record in the module map.

Is there an observable error timing currently for a "SW has a TLA" error vs a linking error? My in-flight CL in Chrome chooses to only check if a module graph has any TLAs in it if it successfully linked. If it failed to link, it doesn't check if it's async at all and let the linking error propagate.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reviewing this I was not sure if this comment was resolved. If I understand correctly, per the next comment thread, the assert failure was fixed in a later commit to this PR that added the following:

If |moduleMap|[|url|] does not have a [=script/record=], then return false.

Fixes w3c#1407.

Co-authored-by: Jake Archibald <jaffathecake@gmail.com>
Co-authored-by: yulia <ystartsev@mozilla.com>
@codehag
Copy link

codehag commented May 21, 2021

I think we addressed the open issues, should we rebase?

docs/index.bs Outdated
1. Return true.
1. [=list/For each=] string |requested| of |record|.\[[RequestedModules]]:
1. Let |url| be the result of [=resolve a module specifier|resolving a module specifier=] given |base| and |requested|.
1. If |url| is *failure*:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know if this can actually fail (I'm inclined to believe no, it can't).

1. Return false.
1. If |seen| does not [=set/contain=] |url|, then:
1. [=set/Append=] |url| to |seen|.
1. If [=Is Async Module=] for |moduleMap|[|url|]'s [=script/record=], |moduleMap|, |base|, and |seen| is true, then:
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The spec bug I was pointing out in #1444 (comment) is here, moduleMap[url] might not have a module record in case of link failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Tried to fix this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we also want to bring the assertion back: Ms2ger@6290a2c

Copy link
Member

@mfalken mfalken left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm very sorry for the long delay. I am not an expert here, but this looks good to me. In particular, my main concern from the previous review about startFailure has been addressed. Thank you.

1. Let |evaluationPromise| be the result of [=run a module script|running the module script=] |script|, with report errors set to false.
1. Assert: |evaluationPromise|.\[[PromiseState]] is not "pending".
1. If |evaluationPromise|.\[[PromiseState]] is "rejected":
1. Set |evaluationStatus| to [$ThrowCompletion$](|evaluationPromise|.\[[PromiseResult]]).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ThrowCompletion() does not become a link in the Preview. Is it possible to fix it?

1. Return true.
1. [=list/For each=] string |requested| of |record|.\[[RequestedModules]],
1. Let |url| be the result of [=resolve a module specifier|resolving a module specifier=] given |base| and |requested|.
1. Assert: |url| is never failure, because [=resolve a module specifier|resolving a module specifier=] must have been previously successful with these same two arguments.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While reviewing this I was not sure if this comment was resolved. If I understand correctly, per the next comment thread, the assert failure was fixed in a later commit to this PR that added the following:

If |moduleMap|[|url|] does not have a [=script/record=], then return false.

@mfalken
Copy link
Member

mfalken commented Jun 11, 2021

Actually I'll just merge this and we can fix the link later.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Top-level await integration for ServiceWorkers running modules
8 participants