From 84d807716e5229dd92ca7a9a9ee718df26fae0ec Mon Sep 17 00:00:00 2001 From: Ivan Enderlin Date: Thu, 12 Nov 2020 14:37:51 +0100 Subject: [PATCH] doc(c-api) Add Dvelopment Notes. --- lib/c-api/src/wasm_c_api/DEVELOPMENT.md | 107 ++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 lib/c-api/src/wasm_c_api/DEVELOPMENT.md diff --git a/lib/c-api/src/wasm_c_api/DEVELOPMENT.md b/lib/c-api/src/wasm_c_api/DEVELOPMENT.md new file mode 100644 index 000000000..5cc4f803a --- /dev/null +++ b/lib/c-api/src/wasm_c_api/DEVELOPMENT.md @@ -0,0 +1,107 @@ +# Development Notes + +## Ownerships + +The `wasm.h` header thankfully defines the `own` “annotation”. It +specifies _who_ owns _what_. For example, in the following code: + +```c +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); +``` + +We must read that `wasm_importtype_new` takes the ownership of all its +three arguments. This function is then responsible to free those +data. We must also read that the returned value is owned by the caller +of this function. + +### Rust Pattern + +This ownership property translates well in Rust. We have decided to +use the `Box` type to represent an owned pointer. `Box` drops +its content when it's dropped. + +Consequently, apart from other patterns, the code above can be written +as follows in Rust: + +```rust +#[no_mangle] +pub extern "C" fn wasm_importtype_new( + module: Box, + name: Box, + extern_type: Box, +) -> Box { + … +} +``` + +By reading the code, it is clear that `wasm_importtype_new` takes the +ownership for `module`, `name`, and `extern_type`, and that the result +is owned by the caller. + +## Null Pointer + +The `wasm.h` header does not say anything about null pointer. The +behavior we agreed on in that passing a null pointer where it is not +expected (i.e. no where) will make the function to return null too +without any error. + +### Rust Pattern + +A nice type property in Rust is that it is possible to write +`Option>` to nicely handle null pointer of kind `T`. For an +argument, it translates as follows: + +* When the given pointer is null, the argument holds `None`, +* When the given pointer is not null, the arguments holds + `Some(NonNull)`. + +Considering [the Ownerships Section][#ownerships], if the pointer is +owned, we can also write `Option>`. This pattern is largely +used in this codebase to represent a “nullable” owned +pointer. Consequently, a code like: + +```c +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); +``` + +translates into Rust as: + +```rust +#[no_mangle] +pub extern "C" fn wasm_importtype_new( + module: Option>, + name: Option>, + extern_type: Option>, +) -> Option> { + Some(Box::new(wasm_importtype_t { + name: name?, + module: module?, + extern_type: extern_type?, + })) +} +``` + +What `name?` (and others) means? It is basically [the `Try` trait +implemented for +`Option`](https://doc.rust-lang.org/std/ops/trait.Try.html#impl-Try): +It returns `None` if the value is `None`, otherwise it unwraps the +`Option`. + +Because the function returns `Option>`, `None` represents a +null pointer. + +## `const *T` + +A constant pointer can be interpreted in C as an immutable +pointer. Without the `own` annotation, it means the ownership is not +transfered anywhere (see [the Ownerships Section][#ownerships]). + +### Rust Pattern + +`const *T` translates to Rust as `&T`, it's a reference. + +Note: It could translate to `Option>` and then we could +call `x?.as_ref()` to get a `&T`. Whether we should use this pattern +in all the codebase is still under discussion.