Skip to content

Layering: Add HostLoadImportedModule hook#2905

Merged
ljharb merged 1 commit intotc39:mainfrom
nicolo-ribaudo:refactor-import-host-hooks
Dec 10, 2022
Merged

Layering: Add HostLoadImportedModule hook#2905
ljharb merged 1 commit intotc39:mainfrom
nicolo-ribaudo:refactor-import-host-hooks

Conversation

@nicolo-ribaudo
Copy link
Copy Markdown
Member

@nicolo-ribaudo nicolo-ribaudo commented Sep 15, 2022

This PR also removes the HostResolveImportedModule and HostImportModuleDynamically hooks.

A few comments/questions:

  1. ContinueDynamicImport is kinda messy, with all the closures and PerformPromiseThen. Is it ok to extend AsyncBlockStart like in https://tc39.es/proposal-array-from-async/#sec-asyncblockstart so that I can use the Await macro?
  2. ResolveExport is described as returning null or something, but it can also return a throw completion (if it's called on a module that re-exports from a dependency that hasn't been already loaded - NOTE: this can only happen if a host calls ResolveExport() even if LoadRequestedModules() doesn't succeed, and they don't have any good reason to do so). This was the behavior even before this PR.
    • Is it ok?
    • Should we make it return null?
    • Should we require that it's called only after having loaded the dependencies?
  3. I named the new Module Records method LoadRequestedModules() to match the name of the [[RequestedModules]] slot, but maybe LoadImportedModules() or LoadDependencies() are better?
  4. I find the hostDefined parameter of LoadRequestedModules() to be incredibly ugly, but it's needed by HTML to pass some context only to modules imported in "the current loading process" and not in "future loading processes" (the .destination flag it sets on the Request object it creates to fetch modules). It's possible that one day HTML will align requests of static and dynamic imports, but today is not that day yet.
  5. I tracked the loading state across the ecma262-host-ecma262 round trip using a ModuleLoadState record, which is overloaded for two usages that are distinguished using an [[Action]] enum field. Is it ok, or should I split it into two different record types (GraphLoadingState and DynamicImportState)? (related: Layering: Add HostLoadImportedModule hook #2905 (comment))

The HTML PR is whatwg/html#8253. Today's consensus was based on the fact that this refactor is purely editorial for the ECMA-262+(insert known host here) pair, so I would like to get a full review on the HTML side to check if we need any tweaks before moving on with this PR. EDIT: Got a 👍 from the HTML side.

@nicolo-ribaudo nicolo-ribaudo added the layering affects the public spec interface and may require updates to integrating specs label Sep 15, 2022
@nicolo-ribaudo nicolo-ribaudo force-pushed the refactor-import-host-hooks branch 2 times, most recently from bac620f to 36635e0 Compare September 15, 2022 14:41
@nicolo-ribaudo
Copy link
Copy Markdown
Member Author

The HTML side of this refactor is starting to get reviewed, I would love to receive a review for this PR too!

Copy link
Copy Markdown

@guybedford guybedford left a comment

Choose a reason for hiding this comment

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

It's possible that one day HTML will align requests of static and dynamic imports, but today is not that day yet.

Would be interested to hear more!

ResolveExport is described as returning null or something, but it can also return a throw completion (if it's called on a module that re-exports from a dependency that hasn't been already loaded - NOTE: this can only happen if a host calls ResolveExport() even if LoadRequestedModules() doesn't succeed, and they don't have any good reason to do so):

I don't think there is any real-world expectation that has ever relied upon ResolveExport throwing, so my preference would be turning this into an internal assertion and enforcing the behaviour that it is only ever called on loaded modules. Is that possible to do?

<p>An implementation of HostLoadImportedModule must conform to the following requirements:</p>
<ul>
<li>
The host environment must perform FinishLoadingImportedModule(_referrer_, _specifier_, _payload_, _result_), where _result_ is either a normal completion containing the loaded Module Record or a throw completion, either synchronously or asynchronously.
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

In the case where a module has already been loaded, is it up to the host whether it resolves synchronously or asynchronously? I wonder if we should mandate already-loaded modules return synchronously to avoid unnecessary divergence?

Copy link
Copy Markdown
Member Author

@nicolo-ribaudo nicolo-ribaudo Oct 5, 2022

Choose a reason for hiding this comment

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

Until last week the HTML spec was ambiguous and allowed loading of already-loaded modules to be asynchronous. I updated that spec to make it synchronous, but without first discussing it with implementers, and browsers don't currently agree.

I'm working on it in web-platform-tests/wpt#35949, but I prefer to keep it out of this PR (since the consensus was based on the fact that this was not observably normative in common hosts).

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Glad to hear you're working on it!

</td>
<td>
This field is only used if [[Action]] is ~graph-loading~. It is true if the loading process has not finished yet, neither successfully nor with an error.
</td>
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Do we really need a separate [[IsLoading]] state if it is equivalent to checking [[PendingModules]] > 0?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

It's not equivalent if the host calls FinishLoadingImportedModule with a throw completion: there might still be pending modules, but we stop the loading process.

@nicolo-ribaudo nicolo-ribaudo force-pushed the refactor-import-host-hooks branch from 3cc0cce to e5360da Compare October 5, 2022 18:17
@jmdyck
Copy link
Copy Markdown
Collaborator

jmdyck commented Oct 7, 2022

Oh, I forgot to say: This PR undefines the ids:

  • sec-hostresolveimportedmodule
  • sec-hostimportmoduledynamically
  • sec-finishdynamicimport

which should maybe become oldids.

@nicolo-ribaudo nicolo-ribaudo force-pushed the refactor-import-host-hooks branch from eac7558 to 389bf4f Compare October 7, 2022 09:03
@nicolo-ribaudo
Copy link
Copy Markdown
Member Author

Thanks for the review @jmdyck! I added sec-hostresolveimportedmodule/sec-hostimportmoduledynamically as oldids of HostLoadImportedModule, and sec-finishdynamicimport as oldids fo FinishLoadingImportedModule. However, I'm not sure if it's correct because those aren't just renames but completely new AOs.

@nicolo-ribaudo
Copy link
Copy Markdown
Member Author

@tc39/ecma262-editors is there anything I could do to help moving this PR forward? There are a bunch of proposals that needs to be updated on top of this, so having it reviewed&merged would be nice.

There are some editorial questions I have (in the issue description), and you can probably give your opinion on them just reading the new spec text without first doing a deep review, so that I can start resolving them :)

@michaelficarra michaelficarra added the editor call to be discussed in the next editor call label Oct 19, 2022
@syg
Copy link
Copy Markdown
Contributor

syg commented Oct 19, 2022

@nicolo-ribaudo We discussed some of your questions on the editor call, our preferences are:

  1. We have plans eventually to basically have asynchronous Abstract Closures. For now, since you already wrote the manual promise-handler version, just keep it as is, we'll simplify this when we introduce the ability to have async ACs.

  2. We don't quite grok the scenario you're describing. Are you saying the only reason this method would throw today is if a host calls it in a weird way that no host actually does? If so, please help us confirm if that's the case, and once confirmed, we should add assertions and make the method infallible.

  3. Name sounds good.

  4. More of a comment than a question? Let's keep the host-defined.

  5. Please name and split them into two separate Record types. Get rid of [[Action]] and just query for if the record is one of the specific record types.

@michaelficarra
Copy link
Copy Markdown
Member

@nicolo-ribaudo Can you try rebasing on main?

@nicolo-ribaudo nicolo-ribaudo force-pushed the refactor-import-host-hooks branch from 6704c5e to f2ea1ad Compare November 29, 2022 09:56
@nicolo-ribaudo
Copy link
Copy Markdown
Member Author

Still failing, maybe because Tobie left google (or at least, it's not linked anymore in his GitHub profile)?

@ljharb
Copy link
Copy Markdown
Member

ljharb commented Nov 29, 2022

@nicolo-ribaudo ill take a look at it

@ljharb
Copy link
Copy Markdown
Member

ljharb commented Nov 29, 2022

It's very strange; the passing tests found 146 authors, in the last day or two it's found 147 including tobie. I'll fix it by adding them to the Emeriti team, since they were employed at Google at the time.

jmdyck
jmdyck previously requested changes Nov 29, 2022
@nicolo-ribaudo nicolo-ribaudo force-pushed the refactor-import-host-hooks branch from f2ea1ad to 0413272 Compare November 29, 2022 21:45
@nicolo-ribaudo
Copy link
Copy Markdown
Member Author

Thanks @ljharb :)

@jmdyck done!

@syg syg added the editor call to be discussed in the next editor call label Dec 7, 2022
@syg syg removed the editor call to be discussed in the next editor call label Dec 7, 2022
@bakkot bakkot added the ready to merge Editors believe this PR needs no further reviews, and is ready to land. label Dec 7, 2022
This commit also removed the HostResolveImportedModule and
HostImportModuleDynamically hooks

* Layering: Add HostLoadImportedModule hook
* fix(JWK review): some wrong variable names
* html review: FinishLoadImportedModule -> FinishLoadingImportedModule
* guybedford review
* jmdyck review
* Add missing _state_.[[IsLoading]] check
* syg review + add missing "Return ~unused~" steps
* Require LoadRequestedModules before ResolveExport and GetExportedNames
@ljharb ljharb force-pushed the refactor-import-host-hooks branch from 0413272 to 18d41aa Compare December 10, 2022 05:49
@ljharb ljharb merged commit 18d41aa into tc39:main Dec 10, 2022
domenic pushed a commit to whatwg/html that referenced this pull request Dec 26, 2022
Follows tc39/ecma262#2905. See https://docs.google.com/presentation/d/1RVUE-MENQT8dj2wxvMLMDxg_VoMOwiwNQQged39QIEU/edit for an overview of the changes here.

This is not a normative change, and in fact hosts can continue to have the same capabilities as before. But now, much of the graph-crawling work can be offloaded to the JavaScript specification.
noamr pushed a commit to noamr/html that referenced this pull request Jan 17, 2023
Follows tc39/ecma262#2905. See https://docs.google.com/presentation/d/1RVUE-MENQT8dj2wxvMLMDxg_VoMOwiwNQQged39QIEU/edit for an overview of the changes here.

This is not a normative change, and in fact hosts can continue to have the same capabilities as before. But now, much of the graph-crawling work can be offloaded to the JavaScript specification.
@nicolo-ribaudo nicolo-ribaudo deleted the refactor-import-host-hooks branch May 8, 2023 12:59
lando-worker bot pushed a commit to mozilla-firefox/firefox that referenced this pull request Jul 21, 2025
…mport. r=jonco

From the specification, ContinueDynamicImport is done inside the engine.
https://tc39.es/ecma262/#sec-ContinueDynamicImport

However, our implementation for EvaluateModuleInContext() has bytecode
encoding related stuff, so for now I implement the ContinueDynamicImport
in the host layer.

Also, the updated spec has a slightly different error handling behavior when
module.Link() fails. I list them below:

Before HTML PR 8253 [whatwg/html#8253]

fetch the descendants of and link a module script
https://web.archive.org/web/20221130023614/https://html.spec.whatwg.org/#fetch-the-descendants-of-and-link-a-module-script

onFetchDescendantsComplete
Step 3.2. Perform record.Link().
          If this throws an exception, set result's error to rethrow to that exception.

HostImportModuleDynamically
https://web.archive.org/web/20221130023614/https://html.spec.whatwg.org/#hostimportmoduledynamically(referencingscriptormodule,-modulerequest,-promisecapability)
Step 6.3 Otherwise, set promise to the result of running a module script given result and true.

run a module script
https://web.archive.org/web/20221130023614/https://html.spec.whatwg.org/#run-a-module-script
Step 5. If script's error to rethrow is not null, then set evaluationPromise to a promise rejected with script's error to rethrow.

--------------------------------------------------------
After ECMA262 PR 2905 [tc39/ecma262#2905]
ContinueDynamicImport
Step 6.a Let link be Completion(module.Link()).
Step 6.b If link is an abrupt completion, then
  Step 6.b.i. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).
  Step 6.b.ii. Return unused.

In short,
  * The old behavior:
    It catched the exception thrown during module.Link(), and set
    the module script's error to rethrow to the exception. Later in
    Evaluate(), if the module script's error to rethrow is not null,
    reject the evaluation promise with the error to rethrow.

  * The new behavior:
    It simply rejects the evaluation promise with the exception thrown
    during module.Link().

Differential Revision: https://phabricator.services.mozilla.com/D214577
lando-worker bot pushed a commit to mozilla-firefox/firefox that referenced this pull request Jul 22, 2025
…mport. r=jonco

From the specification, ContinueDynamicImport is done inside the engine.
https://tc39.es/ecma262/#sec-ContinueDynamicImport

However, our implementation for EvaluateModuleInContext() has bytecode
encoding related stuff, so for now I implement the ContinueDynamicImport
in the host layer.

Also, the updated spec has a slightly different error handling behavior when
module.Link() fails. I list them below:

Before HTML PR 8253 [whatwg/html#8253]

fetch the descendants of and link a module script
https://web.archive.org/web/20221130023614/https://html.spec.whatwg.org/#fetch-the-descendants-of-and-link-a-module-script

onFetchDescendantsComplete
Step 3.2. Perform record.Link().
          If this throws an exception, set result's error to rethrow to that exception.

HostImportModuleDynamically
https://web.archive.org/web/20221130023614/https://html.spec.whatwg.org/#hostimportmoduledynamically(referencingscriptormodule,-modulerequest,-promisecapability)
Step 6.3 Otherwise, set promise to the result of running a module script given result and true.

run a module script
https://web.archive.org/web/20221130023614/https://html.spec.whatwg.org/#run-a-module-script
Step 5. If script's error to rethrow is not null, then set evaluationPromise to a promise rejected with script's error to rethrow.

--------------------------------------------------------
After ECMA262 PR 2905 [tc39/ecma262#2905]
ContinueDynamicImport
Step 6.a Let link be Completion(module.Link()).
Step 6.b If link is an abrupt completion, then
  Step 6.b.i. Perform ! Call(promiseCapability.[[Reject]], undefined, « link.[[Value]] »).
  Step 6.b.ii. Return unused.

In short,
  * The old behavior:
    It catched the exception thrown during module.Link(), and set
    the module script's error to rethrow to the exception. Later in
    Evaluate(), if the module script's error to rethrow is not null,
    reject the evaluation promise with the error to rethrow.

  * The new behavior:
    It simply rejects the evaluation promise with the exception thrown
    during module.Link().

Differential Revision: https://phabricator.services.mozilla.com/D214577
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

layering affects the public spec interface and may require updates to integrating specs ready to merge Editors believe this PR needs no further reviews, and is ready to land.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants