Skip to content

Conversation

@littledan
Copy link
Collaborator

@littledan littledan commented Oct 19, 2018

linclark and others added 4 commits August 12, 2018 18:50
- Initialize Wasm exports in TDZ; closes WebAssembly#18
- Document motivation for no circular modules and circular imports
  of functions as a follow-on; closes WebAssembly#17
- Avoid use of the term "live binding"; closes WebAssembly#19
- Permit imports of Numbers as constant globals; closes WebAssembly#16
@littledan
Copy link
Collaborator Author

littledan commented Oct 19, 2018

Note, this specification draft makes wasm<->wasm cycles throw an error during the ESM evaluation phase, not the ESM instantiation phase. It sort of falls out naturally, since the imports won't have been initialized yet. It's hard to see how to throw the error earlier without requiring another layering change.

@rossberg
Copy link
Member

I'm confused, are you referring to Wasm instantiation or the ES2015 notion of instantiation? Wasm instantiation cannot possibly deal with cycles.

@littledan
Copy link
Collaborator Author

littledan commented Oct 19, 2018

ESM instantation. This PR doesn't create actual wasm<->wasm cycles. Edited the earlier comment for clarity.

@tschneidereit
Copy link
Member

The confusion caused by this overloading of terms is, IIUC, why @linclark proposed renaming the ESM instantiation phase.

Copy link
Contributor

@xtuc xtuc left a comment

Choose a reason for hiding this comment

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

Could we mention in the spec how it relates to task/microtask? (It's unclear to me)

For WebAssembly, instantiation will include initialization for all [external types](https://webassembly.github.io/spec/core/exec/modules.html#external-typing). External types currently include:
When `Module.Instantiate` is called on a WebAssembly module, the module will go through the same process. A Lexical Environment is set up, and all exports (for functions, tables, memories and globals) have a binding created for them, which is initialized to TDZ. This process is not WebAssembly module instantiation--WebAssembly validation is not run at this point, and the start function is not executed. Unlike in JavaScript, functions will not be initialized at this point. Function exports, like other exports, start out as TDZ. Initialization of these bindings to take them out of TDZ for all values happens during evaluation.

WebAssembly exports can be any one of the [external types](https://webassembly.github.io/spec/core/exec/modules.html#external-typing), which currently include:
Copy link
Contributor

Choose a reason for hiding this comment

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

I would phrase it a bit diffrently, because it sounds like exporting multiple memories is possible.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The WebAssembly specs use the term "memories" all over the place. In the future, we may support multiple memories. I can add a note saying, currently at most one, if that'd help.

Copy link
Contributor

Choose a reason for hiding this comment

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

Yeah that make sense, think after it more I think it's ok. Users should refer to the limits.

During evaluation, the code is evaluated to assign values to the exported bindings. In JS, this means running the top-level module code.

One important limitation of this approach to instantiation is that function imports won't work in JS in JS<->wasm cycles where the JS module is higher in the graph. In that case, the WebAssembly module is instantiated before the JS module, which means that functions haven't been initialized and would throw the same error as non-function imports.
WebAssembly has explicit types for both imports and exports. The type of exports is used to populate the lexical environment with a JavaScript value based on the WebAssembly export, from the [WebAssembly JS API](https://webassembly.github.io/spec/js-api/index.html). The type of imports is used to type check or convert imports to the required WebAssembly type.
Copy link
Contributor

Choose a reason for hiding this comment

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

WebAssembly has explicit types for both imports and exports.

It sounds a bit strange to me, could we remove that sentence? The following one implies that the export is typed, so I think it's understandable.

Copy link
Contributor

Choose a reason for hiding this comment

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

[...] based on the WebAssembly export

this seems duplicated.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

The reason I added this paragraph is because some of the below tables include behavior which is based on the import type, and others include behavior based on the export type. The previous version was all based on the export type, and it took me a while to decode the intentions. Many bytecode formats only include the types of exports and not imports, so this is a relevant detail.

- running the start function, which corresponds to running the top-level module code
For JavaScript ES modules, an export name is bound to a slot on the Module Record. For all of the values that we currently allow, this slot will contain a reference to another memory location, which contains the object (e.g. the `WebAssembly.Global` object that is being imported).

The term "snapshot" means that WebAssembly will snapshot the reference. This does result in an observable difference between the way JavaScript and WebAssembly handle updates. In JavaScript modules, it is possible for the exporting module to update an export to point to a different object. Other JavaScript modules that are importing that export will then get the new object. In contrast, while WebAssembly modules will see changes to the object that the export was first bound to (e.g. when the exporting module calls `WebAssembly.Global.prototype.set` to change the value), the WebAssembly module will never see updated bindings.
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think WebAssembly.Global is a good example here, because it could be confusing when using mutable globals.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I don't quite understand--the point of this paragraph is to work through that confusion and explain what the semantics are.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok now I understand, nevermind

\*While WebAssembly only has the concept of globals, JS could export either a regular JS value or a `WebAssembly.Global`.
| import type | behavior |
|-------------|----------|
| global | If the exported value is a WebAssembly.Global object, throw an exception if types mismatch, otherwise take that object as the import. Otherwise, if the imported type is `const`, cast the imported value to the appropriate numeric type, and create a new const global to hold the result. Otherwise, throw an exception. |
Copy link
Contributor

Choose a reason for hiding this comment

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

We could allow importing mutable global now? I assuming that the blocker was the bare number, correct?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes, we'd allow importing mutable globals. Lin's earlier proposals would allow this as well; that's not a change in this version. Mutable globals would be "snapshotted" in the sense that, if the export binding changes to a different WebAssembly.Global object, the imported global would not be updated.

I'm not completely sure what the blocker for bare Numbers ever was, as I mentioned in #16 .


The sequence of operations for a wasm module which depends on a JS module is as follows:

1. wasm module is parsed
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: parsed -> decoded

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I could also say "compiled" here... not sure what is clearest. Parse is what the Wasm core specification uses, but probably it's not clearest for most developers.

Copy link
Member

Choose a reason for hiding this comment

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

The core spec says "decode" for binary and "parse" for text format. Maybe "decode and compile"? The compilation step isn't actually observable (which is why the core spec does not mention it), but given that the API exposes these as compile functions it makes sense to make the intention explicit. Alternatively, there could be a note pointing out the intention.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Thanks, I'll say "compile" then.

1. JS module is instantiated. Function declaration exports are initialized. Other exports are set to undefined or are in TDZ.
1. wasm module has an exports lexical environment created. Imports are bound to memory locations but values are not snapshotted yet.
1. JS module is evaluated. All of its remaining exports (i.e. non-fuction values) are initialized. Any top-level statements are executed.
1. wasm module is instantiated and its start function runs. All imports are snapshotted. All exports are initialized.
Copy link
Contributor

Choose a reason for hiding this comment

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

All imports are snapshotted should be first, the start function could use them.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, will fix.

@littledan
Copy link
Collaborator Author

Could we mention in the spec how it relates to task/microtask? (It's unclear to me)

All pauses in algorithms invoked here are tasks, and none are microtasks. I'm not sure where would be best to write that in the document.

@ByteEater-pl
Copy link

Shouldn't the text use the name ECMAScript instead of JavaScript?

@littledan
Copy link
Collaborator Author

@ByteEater-pl I prefer the term JavaScript, as explained in the JavaScript section of https://html.spec.whatwg.org/#dependencies .

@littledan
Copy link
Collaborator Author

There's a lot of improvements still needed to this PR, but it's my current best guess for how WebAssembly/ESM integration should work. Presented in TPAC 2018 to generally positive feedback from the WebAssembly CG. Merging the PR and planning to clarify things further incrementally from here.

@littledan littledan merged commit 2aa5c2d into WebAssembly:master Feb 15, 2019
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.

6 participants