-
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
base: main
Are you sure you want to change the base?
Document wasm JS builtins #37201
Conversation
``` | ||
|
||
### Parameters | ||
|
||
- `bufferSource` | ||
- : A [typed array](/en-US/docs/Web/JavaScript/Guide/Typed_arrays) or {{jsxref("ArrayBuffer")}} | ||
containing the binary code of the Wasm module you want to compile. | ||
- `compileOptions` {{optional_inline}} | ||
- : An object containing compilation options. Properties can include: | ||
- `builtins` |
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.
builtins
is just as optional as importedStringConstants
, so this line should have {{optional_inline}}
.
Same in the five other copies of this paragraph.
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.
Good call; I've updated these to add the "optional" label in my next commit.
WebAssembly.instantiate(bufferSource, importObject); | ||
WebAssembly.instantiate(bufferSource, compileOptions); |
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.
Nope, this won't work. compileOptions
, if present, must be the third argument. You'll have to pass an empty object ({}
) as importObject
if the module needs no imports (which is exceedingly rare in practice).
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.
D'oh, that was silly. I have fixed these instances.
WebAssembly.instantiate(module, importObject); | ||
WebAssembly.instantiate(module, compileOptions); |
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.
Same here: compileOptions
, if present, must be the third parameter (as in line 80).
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.
Fixed
@@ -19,6 +19,8 @@ is the most efficient, optimized way to load Wasm code. | |||
|
|||
```js-nolint | |||
WebAssembly.instantiateStreaming(source, importObject) | |||
WebAssembly.instantiateStreaming(source, compileOptions) |
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.
Same here: compileOptions
must be the third parameter.
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.
Fixed.
@@ -53,6 +60,22 @@ fetch("simple.wasm") | |||
}); | |||
``` | |||
|
|||
### Feature detecting WebAssembly JavaScript builtins | |||
|
|||
This example validates a wasm module with JavaScript string builtins and imported global string constants enabled, logging `"wasm module valid: true"` to the console if it is valid, and `"wasm module valid: false"` if it isn't. [See it running live](https://mdn.github.io/webassembly-examples/js-builtin-examples/validate/). |
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.
Yes, but that's not at all how you'd feature-detect the JS String Builtins. When this feature is present, then type checks will be stricter than without it (because it imposes certain rules on the imports that will get "magic" treatment).
So to detect this feature, you define a module that's invalid with the feature present, but valid without it, and return true
when validation failed. A simple/short way to do this is:
(module
(function (import "wasm:js-string" "cast")))
Without JS String Builtins, that's perfectly valid, because you can import any function you want with any signature you want (in this case: no parameters and no return values).
With JS String Builtins, this module is invalid, because the now-special-cased "wasm:js-string" "cast"
function must have a specific signature (consuming an externref
, returning a non-nullable (ref extern)
).
In wire bytes, that module is so short that you can just embed the literal:
function IsJsStringBuiltinsSupported() {
let bytes = new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 4, 1, 96, 0,
0, 2, 23, 1, 14, 119, 97, 115, 109, 58, 106, 115, 45,
115, 116, 114, 105, 110, 103, 4, 99, 97, 115, 116, 0, 0
]);
return !WebAssembly.validate(bytes, {builtins: ["js-string"]}); // Note '!'!
}
That said, detecting this feature is often not even necessary, because it can so easily be polyfilled. You can simply provide real imports to take the place of the magic imports, and if the engine supports JS String Builtins, it'll ignore those fallbacks.
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.
Ah, thanks, this is really helpful! I've repurposed my existing example as a basic "this is how to validate a module along with builtins", and taken the liberty of including your above explanation as an additional feature detection example. I hope you don't mind, but this is too useful to not include.
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.
It'll be present when I push my next commit.
|
||
## WebAssembly JavaScript builtin types | ||
|
||
The first builtin types to be implemented are {{jsxref("String")}} operations. This is because the most pressing use case is languages that would benefit from using the JavaScript `String` type to implement their strings. You can find a list of the `String` operations with builtin equivalents defined for them at [JS String Builtin API](https://github.com/WebAssembly/js-string-builtins/blob/main/proposals/js-string-builtins/Overview.md#js-string-builtin-api). |
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.
We're not implementing builtin types, but operations. As far as WebAssembly types are concerned, every imported string is just a generic externref
.
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.
OK, understood. I've gone through the pages and made sure to find other ways to refer to "different available builtins" than "builtin types", given that "type" has a specific meaning that may confuse.
|
||
The first builtin types to be implemented are {{jsxref("String")}} operations. This is because the most pressing use case is languages that would benefit from using the JavaScript `String` type to implement their strings. You can find a list of the `String` operations with builtin equivalents defined for them at [JS String Builtin API](https://github.com/WebAssembly/js-string-builtins/blob/main/proposals/js-string-builtins/Overview.md#js-string-builtin-api). | ||
|
||
Other primitive types are likely to be supported via builtins in the future. |
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.
"Other builtins are likely to be supported in the future."
It's not about types, and not about the JS concept of "primitive" types/values. For example, one of the next candidates could be DataView
-related operations, but that's totally TBD (i.e. probably too early to mention here).
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.
Good call; I've used your wording here. I've also scanned through the pages and made sure that there are no other problematic uses of "primitives" remaining.
|
||
Considering these problems, creating built-in definitions that adapt existing JavaScript primitives to WebAssembly is simpler, more flexible, and better for performance than importing them. | ||
|
||
## WebAssembly JavaScript builtin types |
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.
"## WebAssembly JavaScript Builtins"
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.
I've changed this to "## Available WebAssembly JavaScript builtins": I wanted to be clear that here we are discussing which builtins are available (which is what led me to use "types" in the first place ;-) ), rather than just discussing builtins in general.
``` | ||
|
||
> [!NOTE] | ||
> The above example uses `"#"` as the imported global identifier for illustrative purposes. In production, however, it is best practice to use the empty string to save on module file size. The identifier is repeated for every string literal, and real-world modules can have tens of thousands of them, so the saving can be significant. |
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.
If you switch to the term "namespace" above, then please also switch here.
Might also be worth mentioning that the choice is 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 that .wasm
file expects.
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.
OK, I've updated "identifier" to namespace in the different pages, and taken heed of your suggested wording.
- [`WebAssembly.validate()`](/en-US/docs/WebAssembly/JavaScript_interface/validate_static) | ||
- The [`WebAssembly.Module()`](/en-US/docs/WebAssembly/JavaScript_interface/Module/Module) constructor | ||
|
||
You can also include the `importedStringConstants` property inside `compileOptions`, which enables and specifies an identifier for imported global string constants to be used by the builtins: |
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.
Below (line 104), you use the term "namespace". Might be good to use the same term here, to avoid confusion. Perhaps phrase it roughly like: "..., which selects one import namespace for imported global string constants that the engine will populate automatically".
(You can use these imported string constants for anything you want, not just for the imported JS String Builtins. For example, you could import additional custom functions that work on strings.)
Description
Chrome 130 supports WebAssembly JS String builtins.
Specifically, This PR adds:
compileOptions
parameter used to enable these built-ins, which is available to:WebAssembly.compile()
WebAssembly.compileStreaming()
WebAssembly.instantiate()
WebAssembly.instantiateStreaming()
WebAssembly.validate()
WebAssembly.Module()
constructorMotivation
Additional details
See https://chromestatus.com/feature/6695587390423040 for the data source.
Related issues and pull requests