1362: feat(interface-types) Remove allocator index from `string.lower_memory` r=Hywan a=Hywan
This PR updates `string.lower_memory` to remove the allocator index. Indeed, the string pointer is assumed to be present on the stack.
Also, this PR updates `string.size` to pop, and not to peek, the string to compute the length from.
That way, it matches the WIT proposal.
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
1369: Fix broken makefile commands and improve test consistency r=MarkMcCaskey a=MarkMcCaskey
# Review
- [ ] Add a short description of the the change to the CHANGELOG.md file
Co-authored-by: Mark McCaskey <mark@wasmer.io>
1367: Move `dev-utils` crate into `tests` r=MarkMcCaskey a=MarkMcCaskey
<!--
Prior to submitting a PR, review the CONTRIBUTING.md document for recommendations on how to test:
https://github.com/wasmerio/wasmer/blob/master/CONTRIBUTING.md#pull-requests
-->
# Description
<!--
Provide details regarding the change including motivation,
links to related issues, and the context of the PR.
-->
# Review
- [ ] Add a short description of the the change to the CHANGELOG.md file
Co-authored-by: Mark McCaskey <mark@wasmer.io>
Co-authored-by: Mark McCaskey <5770194+MarkMcCaskey@users.noreply.github.com>
1363: fix(interface-types) Don't duplicate code in the binary encoder. r=MarkMcCaskey a=Hywan
Use the `ToBytes` implementation of `RecordType` to encode the inner
record type of a type, so that it avoids code duplication.
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
1361: doc(interface-types) Add `record` instructions in the `README.md` r=MarkMcCaskey a=Hywan
This PR keeps the documentation up-to-date.
Co-authored-by: Ivan Enderlin <ivan@mnt.io>
1357: Refactored bin commands into separate files r=syrusakbary a=syrusakbary
<!--
Prior to submitting a PR, review the CONTRIBUTING.md document for recommendations on how to test:
https://github.com/wasmerio/wasmer/blob/master/CONTRIBUTING.md#pull-requests
-->
# Description
Refactored bin commands into separate files.
This PR does not do any sustancial changes other than refactoring into different files for better readability.
<!--
Provide details regarding the change including motivation,
links to related issues, and the context of the PR.
-->
# Review
- [x] Add a short description of the the change to the CHANGELOG.md file
Co-authored-by: Syrus <me@syrusakbary.com>
Co-authored-by: Syrus Akbary <me@syrusakbary.com>
1354: test(middleware-common) Remove an unused import r=MarkMcCaskey a=Hywan
This PR removes an unused import.
Co-authored-by: Ivan Enderlin <ivan.enderlin@hoa-project.net>
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>