bors[bot] 4d33020b35
Merge #1331
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>
2020-04-06 06:17:45 +00:00
2020-03-30 20:41:12 -07:00
2020-01-17 17:15:00 -06:00
2020-02-20 14:48:10 -08:00
2020-03-23 11:55:32 +09:00
2020-03-23 17:53:01 -07:00
2020-04-06 06:17:45 +00:00
2020-03-11 19:15:31 -07:00
2020-03-23 11:55:32 +09:00
2020-02-21 14:33:32 -08:00
2020-03-30 14:45:26 -07:00
2020-04-06 06:17:45 +00:00
2019-08-22 16:16:34 -05:00
2019-09-30 22:50:04 -07:00
2020-03-02 11:08:21 -08:00
2020-02-29 09:47:00 -08:00
2019-05-27 14:41:18 -07:00


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 logo Rust Docs Wasmer actively developed last release number of Github stars
C logo C/C++ Docs Wasmer actively developed last release number of Github stars
Python logo Python Docs Wasmer actively developed last release number of Github stars
Go logo Go Docs Wasmer actively developed last release number of Github stars
PHP logo PHP Docs Wasmer actively developed last release number of Github stars
Ruby logo Ruby Docs Wasmer actively developed last release number of Github stars
Postgres logo Postgres Wasmer actively developed last release number of Github stars
JS Logo JavaScript Docs Wasmer actively developed last release number of Github stars
C# logo C#/.Net Docs Miguel de Icaza actively developed last release number of Github stars
R logo R Docs Dirk Schumacher actively developed number of Github stars
Elixir logo Elixir Docs Philipp Tessenow actively developed last release number of Github stars
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

Description
No description provided
Readme MIT 80 MiB
Languages
WebAssembly 43.1%
Rust 40.8%
C 12.9%
C++ 2.3%
Shell 0.4%
Other 0.2%