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

why not just an ambiently-available "stdlib_strings" module? #7

Closed
bakkot opened this issue Sep 13, 2023 · 14 comments
Closed

why not just an ambiently-available "stdlib_strings" module? #7

bakkot opened this issue Sep 13, 2023 · 14 comments

Comments

@bakkot
Copy link

bakkot commented Sep 13, 2023

Maybe there's a good reason not to do this which I'm missing, but here's an alternative design:

  • specify a well-known "stdlib_strings" module with certain functions on it, akin to WASI
  • at compile time, if the wasm imports things from the "stdlib_strings" module, specialize to the actual functions (just like the explicitly-specified compile-time imports in the current proposal)
    • if it imports functions from "stdlib_strings" which the implementation doesn't know about, throw at compile time [or don't, see later]
    • maybe require the user to opt in to providing the stdlib at compile time, to preserve existing behavior (but choosing an obscure-enough name would probably be good enough)
  • if a "stdlib_strings" module is specified at instantiation time, throw [or don't, see later]

Alternatively, allow the "stdlib_strings" module to be specified at instantiation time, and throw only if a specific imported function is specified at instantiation time which was also available in the engine at compile time. This would allow polyfilling functions in the stdlib which aren't yet implemented in the current engine, though there would need to be some way to check which functions are available in the engine so the user could provide at instantiation time only those which actually need polyfills.

It's important that the stdlib functions contain only pure computation, to preserve the sandbox model, but the things provided here all meet that criteria.

I see there's some discussion in this thread which implies the expected way of using this is to specify a whole module. But since you'd only be able to use specific built-in modules like WebAssembly.String at compile time (lest you get a LinkError, or a best a non-postmessagable module), I don't see much advantage in letting the user specify the module at all, instead of just providing it automatically.

cc @michaelficarra

@conrad-watt
Copy link
Contributor

If I understand correctly, the main difference is that instead of a WebAssembly.String object being user-accessible in JS-land, the availability of the namespace becomes hard-coded into the WebAssembly.compile operation?

I think some of us just recently discussed a similar idea when we bumped into each other at Wasmcon (@eqrion, @lukewagner, @ajklein - apologies if I'm getting the combination wrong) - it seems reasonable.

@bakkot
Copy link
Author

bakkot commented Sep 13, 2023

Yup, exactly. That would be easier on users and also would mean you wouldn't need to worry about making things shareable.

@tlively
Copy link
Member

tlively commented Sep 14, 2023

I would like to hear more about the advantages of doing it this way, since making the import explicit rather than built into WebAssembly.compile seems like a more general and less surprising design at first blush.

@bakkot
Copy link
Author

bakkot commented Sep 14, 2023

If I've understood the proposal correctly, you can't include arbitrary things at compile time, just postMessage-able things, which can never be user code. So having an explicit module would appear more general, but actually wouldn't be: it looks like the API should let you use your own functions instead of the built-in module, but you couldn't. (Or maybe you could, but the resulting module would not be postMessage-able, which is even more subtle.) So I actually think it would be less surprising to make the module built in to compile. If it's going to be magic, then it should look like magic.

The other advantages are ergonomics and simplicity. As a user it's easier if things get wired up for me, instead of me needing to specify the name and value of each stdlib module. And reifying the functions means having to deal with a bunch more cases - e.g. you need to worry about what happens if some JS code does WebAssembly.string.fromCharCode = wrap(WebAssembly.string.fromCharCode) (which which happens all the time on the web).

@ajklein
Copy link
Contributor

ajklein commented Sep 14, 2023

Thanks, @conrad-watt; it was @guybedford who first suggested this at WasmCon, and had arguments for an approach like this that I don't see in @bakkot's response.

@eqrion
Copy link
Collaborator

eqrion commented Sep 14, 2023

The motivation in our discussions for using a builtin module of some sort for this instead of a 'compile-time import' was driven by esm-integration. See #3 and the linked source-phase imports issue for discussion of this.

I proposed a concrete change to the proposal there, but posted it on the source-phase-imports proposal instead of this one. Just reposted that there for visibility. See #3 (comment).

@conrad-watt
Copy link
Contributor

conrad-watt commented Sep 15, 2023

using a builtin module of some sort for this instead of a 'compile-time import'

To be very pedantic, I still personally consider this solution to involve compile-time imports, except now the namespaces available for import at compile-time are hard-coded (or controlled indirectly via flags) rather than being user-controlled (at least at a surface-level).

@tlively
Copy link
Member

tlively commented Sep 15, 2023

Yes, on the Wasm side of things, this still very much looks like compile-time imports, and this is really just a change in how those imports are supplied via the JS API for compilation. This is a much more acceptable solution than what I first thought of when I heard talk of abiently available built-in modules.

@eqrion
Copy link
Collaborator

eqrion commented Sep 15, 2023

using a builtin module of some sort for this instead of a 'compile-time import'

To be very pedantic, I still personally consider this solution to involve compile-time imports, except now the namespaces available for import at compile-time are hard-coded (or controlled indirectly via flags) rather than being user-controlled (at least at a surface-level).

You're right, and that reminded me of a subtlety and tweak of the idea that I added to in #3. In effect I think it changes and reframes it enough to not be compile-time imports.

@conrad-watt
Copy link
Contributor

conrad-watt commented Sep 15, 2023

To be clear, I think it's a Good Thing that builtin modules could be thought of as restricted compile-time imports! I responded in the other thread with some more thoughts on the latest iteration.

@wingo
Copy link
Contributor

wingo commented Oct 3, 2023

What would it look like to polyfill a stdlib_strings module? With "normal" imports, there's a straightforward story; how would you do it for ambient modules?

@conrad-watt
Copy link
Contributor

conrad-watt commented Oct 3, 2023

IIUC, if the ambient module isn't provided/enabled (in whatever form it takes, flag or otherwise), the unsatisfied imports in the client module would appear during the instantiation process as "normal" imports which could be polyfilled.

This story only works so long as JS strings are represented as externref or some other core Wasm type even in the ambient module, which IIUC is currently the case. Otherwise, we'd need a story for type imports.

@eqrion
Copy link
Collaborator

eqrion commented Oct 3, 2023

Yeah, what Conrad says is accurate.

Specifically in the sketch laid out in #3 (comment), if you provide the flag to opt-in at compile time to having a JS string module available, then it'll be provided for you. If you want to polyfill, then remove the flag and provide it at instantiation time.

@eqrion
Copy link
Collaborator

eqrion commented Dec 5, 2023

As of #8, there is not a compile-time imports object, but instead importing from an opt-in js-string host module. I believe that resolves this.

@eqrion eqrion closed this as completed Dec 5, 2023
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

No branches or pull requests

6 participants