Types in wasm-bindgen - The wasm-bindgen
Guide (original) (raw)
- Introduction
- 1. Examples
- 1.1. Hello, World!
- 1.2. Using console.log
- 1.3. Small Wasm files
- 1.4. Without a Bundler
- 1.5. Synchronous Instantiation
- 1.6. Importing functions from JS
- 1.7. Working with char
- 1.8. js-sys: WebAssembly in WebAssembly
- 1.9. web-sys: DOM hello world
- 1.10. web-sys: Closures
- 1.11. web-sys: performance.now
- 1.12. web-sys: using fetch
- 1.13. web-sys: Weather report
- 1.14. web-sys: canvas hello world
- 1.15. web-sys: canvas Julia set
- 1.16. web-sys: WebAudio
- 1.17. web-sys: WebGL
- 1.18. web-sys: WebSockets
- 1.19. web-sys: WebRTC DataChannel
- 1.20. web-sys: requestAnimationFrame
- 1.21. web-sys: A Simple Paint Program
- 1.22. web-sys: Wasm in Web Worker
- 1.23. Parallel Raytracing
- 1.24. Wasm Audio Worklet
- 1.25. web-sys: A TODO MVC App
- 2. Reference
- 2.1. Deployment
- 2.2. JS snippets
- 2.3. Static JS Objects
- 2.4. Passing Rust Closures to JS
- 2.5. Receiving JS Closures in Rust
- 2.6. Promises and Futures
- 2.7. Iterating over JS Values
- 2.8. Arbitrary Data with Serde
- 2.9. Accessing Properties of Untyped JS Values
- 2.10. Working with Duck-Typed Interfaces
- 2.11. Command Line Interface
- 2.12. Optimizing for Size
- 2.13. Supported Rust Targets
- 2.14. Supported Browsers
- 2.15. Support for Weak References
- 2.16. Support for Reference Types
- 2.17. Supported Types
- 2.18. #[wasm_bindgen] Attributes
- 2.18.1. On JavaScript Imports
- 2.18.1.1. catch
- 2.18.1.2. constructor
- 2.18.1.3. extends
- 2.18.1.4. getter and setter
- 2.18.1.5. final
- 2.18.1.6. indexing_getter, indexing_setter, and indexing_deleter
- 2.18.1.7. js_class = "Blah"
- 2.18.1.8. js_name
- 2.18.1.9. js_namespace
- 2.18.1.10. method
- 2.18.1.11. module = "blah"
- 2.18.1.12. raw_module = "blah"
- 2.18.1.13. no_deref
- 2.18.1.14. static_method_of = Blah
- 2.18.1.15. structural
- 2.18.1.16. typescript_type
- 2.18.1.17. variadic
- 2.18.1.18. vendor_prefix
- 2.18.1.1. catch
- 2.18.2. On Rust Exports
- 2.18.2.1. constructor
- 2.18.2.2. js_name = Blah
- 2.18.2.3. js_class = Blah
- 2.18.2.4. readonly
- 2.18.2.5. skip
- 2.18.2.6. skip_jsdoc
- 2.18.2.7. start
- 2.18.2.8. main
- 2.18.2.9. typescript_custom_section
- 2.18.2.10. getter and setter
- 2.18.2.11. inspectable
- 2.18.2.12. skip_typescript
- 2.18.2.13. getter_with_clone
- 2.18.2.14. unchecked_return_type and unchecked_param_type
- 2.18.2.15. return_description and param_description
- 2.18.2.1. constructor
- 3. web-sys
- 4. Testing with wasm-bindgen-test
- 5. Contributing to wasm-bindgen
- 5.2. Internal Design
- 5.3. js-sys
- 5.4. web-sys
- 5.5. Publishing
- 5.6. Team
The `wasm-bindgen` Guide
Communicating types to wasm-bindgen
The last aspect to talk about when converting Rust/JS types amongst one another is how this information is actually communicated. The #[wasm_bindgen]
macro is running over the syntactical (unresolved) structure of the Rust code and is then responsible for generating information that wasm-bindgen
the CLI tool later reads.
To accomplish this a slightly unconventional approach is taken. Static information about the structure of the Rust code is serialized via JSON (currently) to a custom section of the Wasm executable. Other information, like what the types actually are, unfortunately isn't known until later in the compiler due to things like associated type projections and typedefs. It also turns out that we want to convey "rich" types like FnMut(String, Foo, &JsValue)
to the wasm-bindgen
CLI, and handling all this is pretty tricky!
To solve this issue the #[wasm_bindgen]
macro generates executable functions which "describe the type signature of an import or export". These executable functions are what the WasmDescribe
trait is all about:
# #![allow(unused_variables)]
#fn main() {
pub trait WasmDescribe {
fn describe();
}
#}
While deceptively simple this trait is actually quite important. When you write, an export like this:
# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen]
fn greet(a: &str) {
// ...
}
#}
In addition to the shims we talked about above which JS generates the macro_also_ generates something like:
#[no_mangle]
pub extern "C" fn __wbindgen_describe_greet() {
<dyn Fn(&str)>::describe();
}
Or in other words it generates invocations of describe
functions. In doing so the __wbindgen_describe_greet
shim is a programmatic description of the type layouts of an import/export. These are then executed when wasm-bindgen
runs! These executions rely on an import called __wbindgen_describe
which passes oneu32
to the host, and when called multiple times gives a Vec<u32>
effectively. This Vec<u32>
can then be reparsed into an enum Descriptor
which fully describes a type.
All in all this is a bit roundabout but shouldn't have any impact on the generated code or runtime at all. All these descriptor functions are pruned from the emitted Wasm file.