mirror of
https://github.com/fluencelabs/wasmer
synced 2025-03-16 16:20:49 +00:00
1331: feat(interface-types) Implement the `record` instructions r=Hywan a=Hywan ### Description This PR implements the `record` WIT type, along with the `record.lift` and `record.lower` instructions. With my current understanding of the draft/specification, here is how it works. Let's say we want to represent a Rust struct like the following: ```rust struct S { x: String, y: i32 } ``` First declare a WIT type, such as: ```wat (@interface type (record (field string) (field i32))) ``` The `record` type is supported by the binary encoder, the WAT encoder, the binary decoder, and the WAT decoder. A new `TypeKind` node has been introduced in the AST to differentiate a function type (`(@interface type (func (param …) (result …)))`) of a record type (see above). Second, the `record.lower` transforms a host value (here Rust value, `S`) into a WIT value. In our implementation, a record value is defined as: ```rust InterfaceValue::Record(Vec<InterfaceValue>) ``` Multiple mechanisms are used to type check a record value based on a record type. The code of the `record.lower` is pretty straightforward. Because transforming a host value into a WIT value isn't obvious, a `Serializer` has been implemented, based on [`serde`](https://serde.rs/). This feature is behind the `serde` flag, which is turned on by default. Serde is only used to cross the host/Wasm boundary, but it's not used to represent the value in memory or anything. It's only a shortcut to transform a host value into a WIT value, and vice versa. Use the following code to transform `S` into a WIT value: ```rust #[derive(Serialize, Deserialize, Debug, PartialEq)] struct S { x: String, y: i32, } let host_value = S { x: "hello".to_string(), y: 42 }; let wit_value = to_interface_value(&host_value).unwrap(); assert_eq!( wit_value, InterfaceValue::Record(vec![ InterfaceValue::String("hello".to_string()), InterfaceValue::I32(42), ]) ); ``` Third, the `record.lift` instruction does the opposite of `record.lower`: It transforms WIT values into a host value. To also facilitate the user experience, this PR contains a `Deserializer` implementation, still based on `serde`, with the `from_interface_values` function. It looks like this: ```rust let wit_values = vec![ InterfaceValue::Record(vec![ InterfaceValue::String("hello".to_string()), InterfaceValue::I32(42), ]) ]; let host_value = from_interface_values::<S>(&wit_values).unwrap(); assert_eq!( host_value, S { x: "hello".to_string(), y: 42 }, ); ``` With the `Serializer` and `Deserializer`, it's super easy for the user to send or receive values from WIT. The `record.lift` and `record.lower` instructions are kind of basic. The `record.lift` instruction has a little trick to reduce vector allocations, but there is a documentation for that. #### Opened questions Records of dimension 1 do not raise any issue. With `record.lift`, all values on the stack (the WIT interpreter stack) are popped, and are used as record field values. Something like: ``` [stack] i32(1) i64(2), string("hello") record.lift <record_type> ``` generates ``` [stack] record { i32(1), i64(2), string("hello") } ``` But it's not clear what happens with record of dimension > 1, for instance for a type like `record (field i32) (record (field i32) (field i32)) (field string)`, it is assumed (in this PR) that the stack must be like this: ``` [stack] i32(1) i32(2) i32(3) string("hello") record.lift <record_type> ``` to generate: ``` [stack] record { i32(1), record { i32(2), i32(3) }, string("hello") } ``` If we want the stack to contain an intermediate record, we should have something like this: ``` [stack] i32(1) i32(2) i32(3) record.lift <record_type_2> string("hello") record.lift <record_type_1> ``` But it would imply that `record_type_1` is defined as `record (field i32) (record (type record_type_2)) (field i32)`. A sub-record defined by another record type isn't support, as it is not specified in the draft. I believe my assumption is fine enough for a first implementation of records in WIT. ### To do - [x] Encode and decode record type (`(@interface type (record string i32))`): - [x] Binary encoder/decoder - [x] WAT encoder/decoder - [x] Implement the `record.lift` instruction - [x] Implement the `record.lower` instruction - [x] Test - [x] Documentation - [x] Surprise! - [x] Serialize a Rust value to WIT values (useful for `record`s) - [x] Deserialize WIT values to a Rust value (useful for `record`s) Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
Wasmer is a standalone WebAssembly runtime:
- Universal: Wasmer is available in Linux, macOS and Windows (for both Desktop and ARM)
- Fast: Wasmer aims to run WebAssembly at near-native speed
- Pluggable: Wasmer can be used from almost any programming language
- Safe: supporting WASI and Emscripten
It is used to run software fast, universally and safely: standalone applications and universal libraries.
Contents
Quickstart
Get started with Wasmer:
1. Install Wasmer
curl https://get.wasmer.io -sSfL | sh
Note: Wasmer is also available on Windows
Alternative: Install with Homebrew
brew install wasmer
2. Use Wasmer
Download a WASM file, and use it universally! You can start with QuickJS: qjs.wasm
wasmer qjs.wasm
3. Next steps
Here is what you can do next:
Language Integrations
Wasmer runtime can be used as a library embedded in different languages, so you can use WebAssembly anywhere 🎉
Language | Docs | Author(s) | Maintenance | Release | Stars | |
---|---|---|---|---|---|---|
Rust | Docs | Wasmer | actively developed | |||
C/C++ | Docs | Wasmer | actively developed | |||
Python | Docs | Wasmer | actively developed | |||
Go | Docs | Wasmer | actively developed | |||
PHP | Docs | Wasmer | actively developed | |||
Ruby | Docs | Wasmer | actively developed | |||
Postgres | Wasmer | actively developed | ||||
JavaScript | Docs | Wasmer | actively developed | |||
C#/.Net | Docs | Miguel de Icaza | actively developed | |||
R | Docs | Dirk Schumacher | actively developed | |||
![]() |
Elixir | Docs | Philipp Tessenow | actively developed | ||
❓ | your language is missing? |
Contribute
We welcome any form of contribution, especially from new members of our community 💜
You can check how to build the Wasmer runtime in our awesome docs!
Testing
Test you want? The Wasmer docs will show you how.
Community
Wasmer has an amazing community developers and contributors. Welcome, please join us! 👋
Channels
Languages
WebAssembly
43.1%
Rust
40.8%
C
12.9%
C++
2.3%
Shell
0.4%
Other
0.2%