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

Document wasm JS builtins #37201

Merged
merged 51 commits into from
Dec 20, 2024
Merged
Show file tree
Hide file tree
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 Dec 13, 2024
87b16ce
Add compileOptions to appropriate ref pages
chrisdavidmills Dec 13, 2024
f953a67
Fix document flaws
chrisdavidmills Dec 13, 2024
cd055ff
Fixes for jakobkummerow review comments
chrisdavidmills Dec 14, 2024
b896f4d
Merge branch 'main' into wasm-js-string-builtins
chrisdavidmills Dec 14, 2024
c47fa7b
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
2969e36
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
9396fa8
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
96c3aff
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
7089330
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
833ddc1
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
3592d39
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
e7ac343
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
30584aa
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
520eb5d
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
3232f3a
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
35984c6
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
fbf7da5
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
773b4bc
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
215c7a6
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 16, 2024
a426db3
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills Dec 16, 2024
70af22e
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills Dec 16, 2024
3fcdd25
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills Dec 16, 2024
fd0548b
Update files/en-us/webassembly/javascript_interface/module/module/ind…
chrisdavidmills Dec 16, 2024
d126b33
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills Dec 16, 2024
80b26b5
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills Dec 16, 2024
a6124be
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills Dec 16, 2024
5c24913
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills Dec 16, 2024
db2f5a7
Update files/en-us/webassembly/javascript_interface/compilestreaming_…
chrisdavidmills Dec 16, 2024
57c4392
Update files/en-us/webassembly/javascript_interface/compilestreaming_…
chrisdavidmills Dec 16, 2024
c25e23f
Update files/en-us/webassembly/javascript_interface/compilestreaming_…
chrisdavidmills Dec 16, 2024
4eaf706
Update files/en-us/webassembly/javascript_interface/instantiate_stati…
chrisdavidmills Dec 16, 2024
94a89d6
Update files/en-us/webassembly/javascript_interface/instantiate_stati…
chrisdavidmills Dec 16, 2024
76ef925
Update files/en-us/webassembly/javascript_interface/instantiate_stati…
chrisdavidmills Dec 16, 2024
408c297
Update files/en-us/webassembly/javascript_interface/instantiatestream…
chrisdavidmills Dec 16, 2024
7e9a50e
Update files/en-us/webassembly/javascript_interface/instantiatestream…
chrisdavidmills Dec 16, 2024
c89a7b2
Update files/en-us/webassembly/javascript_interface/instantiatestream…
chrisdavidmills Dec 16, 2024
b870310
Update files/en-us/webassembly/javascript_interface/module/module/ind…
chrisdavidmills Dec 16, 2024
609d996
Update files/en-us/webassembly/javascript_interface/module/module/ind…
chrisdavidmills Dec 16, 2024
9675e55
Fix another few instances of wasm -> Wasm
chrisdavidmills Dec 16, 2024
f6d884e
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 18, 2024
3d01b45
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 18, 2024
2388126
Update files/en-us/webassembly/javascript_builtins/index.md
chrisdavidmills Dec 18, 2024
77128f2
Update files/en-us/webassembly/javascript_interface/compile_static/in…
chrisdavidmills Dec 18, 2024
4ac2435
Update files/en-us/webassembly/javascript_interface/validate_static/i…
chrisdavidmills Dec 18, 2024
0abe025
Updates from review
chrisdavidmills Dec 18, 2024
89c3628
Fixes for latest review comments from jakobkummerow
chrisdavidmills Dec 19, 2024
3020667
Add note about equals and null
chrisdavidmills Dec 19, 2024
c8395f2
Merge branch 'main' into wasm-js-string-builtins
chrisdavidmills Dec 19, 2024
d84503d
Merge branch 'main' into wasm-js-string-builtins
chrisdavidmills Dec 20, 2024
c90e6f6
Last few fixes
chrisdavidmills Dec 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
155 changes: 155 additions & 0 deletions files/en-us/webassembly/imported_string_constants/index.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
---
Copy link
Collaborator

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.

Copy link
Contributor Author

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.

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.
Loading
Loading