From 98686fd74a3bf4afbbf54a7f16fe65b92248bd2a Mon Sep 17 00:00:00 2001 From: Chris Mills Date: Fri, 13 Dec 2024 09:41:57 +0000 Subject: [PATCH 01/48] Document wasm JS builtins --- .../webassembly/javascript_builtins/index.md | 149 ++++++++++++++++++ files/sidebars/webassemblysidebar.yaml | 1 + 2 files changed, 150 insertions(+) create mode 100644 files/en-us/webassembly/javascript_builtins/index.md diff --git a/files/en-us/webassembly/javascript_builtins/index.md b/files/en-us/webassembly/javascript_builtins/index.md new file mode 100644 index 000000000000000..ee07600c268fd24 --- /dev/null +++ b/files/en-us/webassembly/javascript_builtins/index.md @@ -0,0 +1,149 @@ +--- +title: WebAssembly JavaScript builtins +slug: WebAssembly/JavaScript_builtins +page-type: guide +--- + +{{WebAssemblySidebar}} + +WebAssembly JavaScript builtins are wasm equivalents of JavaScript operations that provide a way to use JavaScript features inside wasm modules without having to import JavaScript glue code to provide a bridge between JavaScript and WebAssembly values and calling conventions. + +This provides performance improvements — importing glue code for primitives such as {{jsxref("String")}} can come with a significant overhead. WebAssembly and most languages that target it expect a tight sequence of inline operations rather than an indirect function call, which is how regular imported functions work. + +This article explains how builtins work and which ones are available, then provides a usage example. + +## Problems with importing JavaScript functions + +There are several problems with importing functions from JavaScript to WebAssembly modules: + +- Existing APIs require a conversion to handle differences around the [`this`](/en-US/docs/Web/JavaScript/Reference/Operators/this) value, which WebAssembly function `import` calls leave as `undefined`. +- Certain primitives use JavaScript operators such as [`===`](/en-US/docs/Web/JavaScript/Reference/Operators/Strict_equality) and [`<`](/en-US/docs/Web/JavaScript/Reference/Operators/Less_than) that cannot be imported. +- Most JavaScript functions are extremely permissive of the types of values they accept, and it's desirable to leverage WebAssembly's type system to remove those checks and coercions wherever we can. + +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 + +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. + +## How do you use builtins? + +Builtins work in a similar way to functions imported from JavaScript, except that you are using standard wasm function equivalents for performing JavaScript operations that are defined in a reserved namespace (`wasm:`). This being the case, browsers can predict and generate optimal code for them. + +Builtins are enabled at compile-time by specifying the `builtins` property during compilation. This goes inside the `compileOptions` object, and its value is an array of the builtin types you want to enable: + +```js +WebAssembly.compile(bytes, { builtins: ["js-string"] }); +``` + +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 + +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: + +```js +WebAssembly.compile(bytes, { + builtins: ["js-string"], + importedStringConstants: "#", +}); +``` + +> [!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. + +Over in your webassembly module, you can now import string literals, specifying the same identifier, and import builtins of the types specified in the `compileOptions` object from the `wasm:` namespace (in this case, the [`concat()`](/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat) function; see also the [equivalent built-in definition](https://github.com/WebAssembly/js-string-builtins/blob/main/proposals/js-string-builtins/Overview.md#wasmjs-string-concat)). + +```wasm +(global $h (import "#" "hello ") externref) +(global $w (import "#" "world!") externref) +(func $concat (import "wasm:js-string" "concat") + (param externref externref) (result (ref extern))) +``` + +## Builtins example + +Let's work through a basic but complete example to show how builtins are used. This example will define a function inside a wasm module that concatenates two strings together and prints the result to the console, then export it. We will then call the exported function from JavaScript. + +The example we'll be referring to uses the [`WebAssembly.instantiate()`](/en-US/docs/WebAssembly/JavaScript_interface/instantiate_static) function on the webpage to handle the compilation and instantiation; you can find this and other examples on our `webassembly-examples` repo — see [`js-builtin-examples`](https://github.com/mdn/webassembly-examples/tree/main/js-builtin-examples). + +You can build up the example by following the steps below; also [see it running live](https://mdn.github.io/webassembly-examples/js-builtin-examples/instantiate/). + +### The JavaScript + +The JavaScript for the example is shown below. To test this locally, include it in an HTML page using a method of your chooosing (for example, inside {{htmlelement("script")}} tags, or in an external `.js` file referenced via `