-
Notifications
You must be signed in to change notification settings - Fork 22.5k
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
Document wasm JS builtins #37201
Merged
chrisdavidmills
merged 51 commits into
mdn:main
from
chrisdavidmills:wasm-js-string-builtins
Dec 20, 2024
Merged
Document wasm JS builtins #37201
Changes from 49 commits
Commits
Show all changes
51 commits
Select commit
Hold shift + click to select a range
98686fd
Document wasm JS builtins
chrisdavidmills 87b16ce
Add compileOptions to appropriate ref pages
chrisdavidmills f953a67
Fix document flaws
chrisdavidmills cd055ff
Fixes for jakobkummerow review comments
chrisdavidmills b896f4d
Merge branch 'main' into wasm-js-string-builtins
chrisdavidmills c47fa7b
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 2969e36
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 9396fa8
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 96c3aff
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 7089330
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 833ddc1
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 3592d39
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills e7ac343
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 30584aa
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 520eb5d
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 3232f3a
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 35984c6
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills fbf7da5
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 773b4bc
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 215c7a6
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills a426db3
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills 70af22e
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills 3fcdd25
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills fd0548b
Update files/en-us/webassembly/javascript_interface/module/module/ind…
chrisdavidmills d126b33
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills 80b26b5
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills a6124be
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills 5c24913
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills db2f5a7
Update files/en-us/webassembly/javascript_interface/compilestreaming_…
chrisdavidmills 57c4392
Update files/en-us/webassembly/javascript_interface/compilestreaming_…
chrisdavidmills c25e23f
Update files/en-us/webassembly/javascript_interface/compilestreaming_…
chrisdavidmills 4eaf706
Update files/en-us/webassembly/javascript_interface/instantiate_stati…
chrisdavidmills 94a89d6
Update files/en-us/webassembly/javascript_interface/instantiate_stati…
chrisdavidmills 76ef925
Update files/en-us/webassembly/javascript_interface/instantiate_stati…
chrisdavidmills 408c297
Update files/en-us/webassembly/javascript_interface/instantiatestream…
chrisdavidmills 7e9a50e
Update files/en-us/webassembly/javascript_interface/instantiatestream…
chrisdavidmills c89a7b2
Update files/en-us/webassembly/javascript_interface/instantiatestream…
chrisdavidmills b870310
Update files/en-us/webassembly/javascript_interface/module/module/ind…
chrisdavidmills 609d996
Update files/en-us/webassembly/javascript_interface/module/module/ind…
chrisdavidmills 9675e55
Fix another few instances of wasm -> Wasm
chrisdavidmills f6d884e
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 3d01b45
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 2388126
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills 77128f2
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills 4ac2435
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills 0abe025
Updates from review
chrisdavidmills 89c3628
Fixes for latest review comments from jakobkummerow
chrisdavidmills 3020667
Add note about equals and null
chrisdavidmills c8395f2
Merge branch 'main' into wasm-js-string-builtins
chrisdavidmills d84503d
Merge branch 'main' into wasm-js-string-builtins
chrisdavidmills c90e6f6
Last few fixes
chrisdavidmills File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
155 changes: 155 additions & 0 deletions
155
files/en-us/webassembly/imported_string_constants/index.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
--- | ||
title: WebAssembly Imported global string constants | ||
slug: WebAssembly/Imported_string_constants | ||
page-type: guide | ||
--- | ||
|
||
{{WebAssemblySidebar}} | ||
|
||
WebAssembly imported global string constants make working with JavaScript strings inside Wasm modules easier by removing the need for a lot of the boilerplate associated with traditional string imports. | ||
|
||
This article explains how imported global string constants work. | ||
|
||
## Problems with traditional string imports | ||
|
||
Let's start by exploring how string imports have traditionally worked in WebAssembly. In a Wasm module, you could import a couple of strings from a namespace called `"string_constants"` with the following snippet: | ||
|
||
```wasm | ||
(global (import "string_constants" "string_constant_1") externref) | ||
(global (import "string_constants" "string_constant_2") externref) | ||
``` | ||
|
||
In your JavaScript, you would then provide the strings to import in an `importObject`: | ||
|
||
```js | ||
importObject = { | ||
..., | ||
string_constants: { | ||
string_constant_1: "hello ", | ||
string_constant_2: "world!", | ||
... | ||
}, | ||
}; | ||
``` | ||
|
||
Before compiling/instantiating the module to make use of its functions: | ||
|
||
```js | ||
WebAssembly.instantiateStreaming(fetch("my-module.wasm"), importObject).then( | ||
(obj) => obj.instance.exports.exported_func(), | ||
); | ||
``` | ||
|
||
This is sub-optimal for several reasons: | ||
|
||
1. The download size is increased for each new string imported, and this increase is more than just the strings themselves — for every string you need the definition of the imported global in the Wasm module, and the definition of the value on the JavaScript side. For a Wasm module with thousands of imported strings, this can really add up. | ||
2. All these bytes also take time to parse before the Wasm module can be instantiated. | ||
3. For Wasm module optimization, it is an added inconvenience to have to modify an accompanying JavaScript file along with the Wasm module, for example when removing unused string constants at compile time. | ||
|
||
Import names can be any unicode string you like, so developers often set the entire string as the import name for convenience (for example, when debugging). This would result in our above Wasm snippet being rewritten like so: | ||
|
||
```wasm | ||
(global (import "string_constants" "hello ") externref) | ||
(global (import "string_constants" "world!") externref) | ||
``` | ||
|
||
And the accompanying `importObject` like this: | ||
|
||
```js | ||
importObject = { | ||
..., | ||
string_constants: { | ||
"hello ": "hello ", | ||
"world!": "world!", | ||
... | ||
}, | ||
}; | ||
``` | ||
|
||
Looking at the above code, it makes sense to let the browser automate away some of this boilerplate, and that's exactly what the imported global string constants feature does. | ||
|
||
## How do you use imported global string constants? | ||
|
||
Now we'll look at how imported global string constants improve on the above situation. | ||
|
||
### JavaScript API | ||
|
||
Imported global string constants are enabled by including the `compileOptions.importedStringConstants` property when calling methods to compile and/or instantiate a module. Its value is an import namespace for imported global string constants that the Wasm engine will populate automatically: | ||
|
||
```js | ||
WebAssembly.compile(bytes, { | ||
importedStringConstants: "string_constants", | ||
}); | ||
``` | ||
|
||
That's it! No lists of strings required in the import object. | ||
|
||
The `compileOptions` object is available to the following functions: | ||
|
||
- [`WebAssembly.compile()`](/en-US/docs/WebAssembly/JavaScript_interface/compile_static) | ||
- [`WebAssembly.compileStreaming()`](/en-US/docs/WebAssembly/JavaScript_interface/compileStreaming_static) | ||
- [`WebAssembly.instantiate()`](/en-US/docs/WebAssembly/JavaScript_interface/instantiate_static) | ||
- [`WebAssembly.instantiateStreaming()`](/en-US/docs/WebAssembly/JavaScript_interface/instantiateStreaming_static) | ||
- [`WebAssembly.validate()`](/en-US/docs/WebAssembly/JavaScript_interface/validate_static) | ||
- The [`WebAssembly.Module()`](/en-US/docs/WebAssembly/JavaScript_interface/Module/Module) constructor | ||
|
||
### WebAssembly module features | ||
|
||
Over in your WebAssembly module, you can now import string literals, specifying the same namespace you specified in `importedStringConstants` over in the JavaScript: | ||
|
||
```wasm | ||
(global $h (import "string_constants" "hello ") externref) | ||
(global $w (import "string_constants" "world!") externref) | ||
``` | ||
|
||
The Wasm engine then looks at all the imported globals in the `string_constants` namespace, and creates a string equal to each specified import name. | ||
|
||
### A note on namespace choices | ||
|
||
The above example uses `"string_constants"` as the imported global string namespace for illustrative purposes. In production, however, it is best practice to use the empty string (`""`) to save on module file size. The namespace is repeated for every string literal, and real-world modules can have thousands of them, so the saving can be significant. | ||
|
||
If you are already using the `""` namespace for some other purpose, you should consider using a single-character namespace for your strings such as `"s"`, `"'"`, or `"#"`. | ||
|
||
The namespace choice is generally made by the authors of the toolchain that will generate the Wasm modules. Once you have a `.wasm` file and want to embed it in your JavaScript, you can't freely choose this namespace any more; you have to use what the `.wasm` file expects. | ||
|
||
## Imported global string example | ||
|
||
You can see an example that uses imported global strings [running live](https://mdn.github.io/webassembly-examples/js-builtin-examples/instantiate/) — open your browser's JavaScript console to see the example output. This example defines a function inside a Wasm module that concatenates two imported strings together and prints the result to the console, exports it, then calls the exported function from JavaScript. | ||
|
||
The JavaScript for the example is shown below. You can see how we've used `importedStringConstants` to enable imported global strings: | ||
|
||
```js | ||
const importObject = { | ||
// Regular import | ||
m: { | ||
log: console.log, | ||
}, | ||
}; | ||
|
||
const compileOptions = { | ||
builtins: ["js-string"], // Enable JavaScript string builtins | ||
importedStringConstants: "string_constants", // Enable imported global string constants | ||
}; | ||
|
||
fetch("log-concat.wasm") | ||
.then((response) => response.arrayBuffer()) | ||
.then((bytes) => WebAssembly.instantiate(bytes, importObject, compileOptions)) | ||
.then((result) => result.instance.exports.main()); | ||
``` | ||
|
||
The text representation of our WebAssembly module code looks like this — notice how it imports two strings in the specified namespace, which are later used in the `$concat` function: | ||
|
||
```wasm | ||
(module | ||
(global $h (import "string_constants" "hello ") externref) | ||
(global $w (import "string_constants" "world!") externref) | ||
(func $concat (import "wasm:js-string" "concat") | ||
(param externref externref) (result (ref extern))) | ||
(func $log (import "m" "log") (param externref)) | ||
(func (export "main") | ||
(call $log (call $concat (global.get $h) (global.get $w)))) | ||
) | ||
``` | ||
|
||
> [!NOTE] | ||
> This example also uses a JavaScript String builtin. See [WebAssembly JavaScript builtins](/en-US/docs/WebAssembly/JavaScript_builtins) for more information on these, and a complete walkthrough of the above example. |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a separate doc works well for me!
Arguably you don't need the justification/explanation of the old way - you could present this without the reasons the new ways is better and get the the "how you do it" much faster. I would have done it that way, but happy with this as well, because I kind of like knowing "why's". Either way, it is very clear.
FWIW part of the problem I had with this before reading the doc is the term "import", as in we import strings into the module.
When I think of import I think of something coming from somewhere else, so in the original mechanism we import strings defined in JavaScript. Here we magically import strings, but we define them in place.
Or to put it another way, the semantics from a WASM point of view are import, but from a developer point of view it is define.
I don't know if that concern/misunderstanding makes sense, but might be nice to say a few words, if possible. Could be something that no one in the WASM space would have a problem with.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, I feel like this is a bit of an overwrought worry — if you don't care about the old way, and/or know it well, then you can just skip ahead to the second section. But the first section does help relative newcomers understand why this feature is needed (this was literally the first question in my mind when I started to learn about it), including why this is semantically still importing.