|
| 1 | +# Development Notes |
| 2 | + |
| 3 | +## Ownerships |
| 4 | + |
| 5 | +The [`wasm.h`] header thankfully defines the [`own`] “annotation”. It |
| 6 | +specifies _who_ owns _what_. For example, in the following code: |
| 7 | + |
| 8 | +```c |
| 9 | +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( |
| 10 | + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); |
| 11 | +``` |
| 12 | +
|
| 13 | +We must read that `wasm_importtype_new` takes the ownership of all its |
| 14 | +three arguments. This function is then responsible to free those |
| 15 | +data. We must also read that the returned value is owned by the caller |
| 16 | +of this function. |
| 17 | +
|
| 18 | +### Rust Pattern |
| 19 | +
|
| 20 | +This ownership property translates well in Rust. We have decided to |
| 21 | +use the `Box<T>` type to represent an owned pointer. `Box<T>` drops |
| 22 | +its content when it's dropped. |
| 23 | +
|
| 24 | +Consequently, apart from other patterns, the code above can be written |
| 25 | +as follows in Rust: |
| 26 | +
|
| 27 | +```rust |
| 28 | +#[no_mangle] |
| 29 | +pub extern "C" fn wasm_importtype_new( |
| 30 | + module: Box<wasm_name_t>, |
| 31 | + name: Box<wasm_name_t>, |
| 32 | + extern_type: Box<wasm_externtype_t>, |
| 33 | +) -> Box<wasm_importtype_t> { |
| 34 | + … |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +By reading the code, it is clear that `wasm_importtype_new` takes the |
| 39 | +ownership for `module`, `name`, and `extern_type`, and that the result |
| 40 | +is owned by the caller. |
| 41 | + |
| 42 | +## `const *T` |
| 43 | + |
| 44 | +A constant pointer can be interpreted in C as an immutable |
| 45 | +pointer. Without the [`own`] annotation, it means the ownership is not |
| 46 | +transfered anywhere (see [the Ownerships Section][#ownerships]). |
| 47 | + |
| 48 | +### Rust Pattern |
| 49 | + |
| 50 | +`const *T` translates to Rust as `&T`, it's a reference. |
| 51 | + |
| 52 | +## Null Pointer |
| 53 | + |
| 54 | +The [`wasm.h`] header does not say anything about null pointer. The |
| 55 | +behavior we agreed on in that passing a null pointer where it is not |
| 56 | +expected (i.e. everywhere) will make the function to return null too |
| 57 | +with no error. |
| 58 | + |
| 59 | +### Rust Pattern |
| 60 | + |
| 61 | +A nice type property in Rust is that it is possible to write |
| 62 | +`Option<NonNull<T>>` to nicely handle null pointer of kind `T`. For an |
| 63 | +argument, it translates as follows: |
| 64 | + |
| 65 | +* When the given pointer is null, the argument holds `None`, |
| 66 | +* When the given pointer is not null, the arguments holds |
| 67 | + `Some(NonNull<T>)`. |
| 68 | + |
| 69 | +Considering [the Ownerships Section][#ownerships], if the pointer is |
| 70 | +owned, we can also write `Option<Box<T>>`. This pattern is largely |
| 71 | +used in this codebase to represent a “nullable” owned |
| 72 | +pointer. Consequently, a code like: |
| 73 | + |
| 74 | +```c |
| 75 | +WASM_API_EXTERN own wasm_importtype_t* wasm_importtype_new( |
| 76 | + own wasm_name_t* module, own wasm_name_t* name, own wasm_externtype_t*); |
| 77 | +``` |
| 78 | +
|
| 79 | +translates into Rust as: |
| 80 | +
|
| 81 | +```rust |
| 82 | +#[no_mangle] |
| 83 | +pub extern "C" fn wasm_importtype_new( |
| 84 | + module: Option<Box<wasm_name_t>>, |
| 85 | + name: Option<Box<wasm_name_t>>, |
| 86 | + extern_type: Option<Box<wasm_externtype_t>>, |
| 87 | +) -> Option<Box<wasm_importtype_t>> { |
| 88 | + Some(Box::new(wasm_importtype_t { |
| 89 | + name: name?, |
| 90 | + module: module?, |
| 91 | + extern_type: extern_type?, |
| 92 | + })) |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +What `name?` (and others) means? It is basically [the `Try` trait |
| 97 | +implemented for |
| 98 | +`Option`](https://doc.rust-lang.org/std/ops/trait.Try.html#impl-Try): |
| 99 | +It returns `None` if the value is `None`, otherwise it unwraps the |
| 100 | +`Option`. |
| 101 | + |
| 102 | +Because the function returns `Option<Box<T>>`, `None` represents a |
| 103 | +null pointer. |
| 104 | + |
| 105 | +Considering [the `const *T` Section][#const-t], if the pointer is not |
| 106 | +owned, we can either write `Option<NonNull<T>>` or `Option<&T>`. It |
| 107 | +has been decided to use the second pattern in all the codebase. |
| 108 | + |
| 109 | +## Destructors |
| 110 | + |
| 111 | +The [`wasm.h`] defines `wasm_*_delete` functions. It represents destructors. |
| 112 | + |
| 113 | +## Rust Pattern |
| 114 | + |
| 115 | +The destructors in Rust translate as follow: |
| 116 | + |
| 117 | +```rust |
| 118 | +#[no_mangle] |
| 119 | +pub unsafe extern "C" fn wasm_*_delete(_: Option<Box<wasm_*_t>>) {} |
| 120 | +``` |
| 121 | + |
| 122 | +`Box<T>` will take the ownership of the value. It means that Rust will |
| 123 | +drop it automatically as soon as it goes out of the |
| 124 | +scope. Consequently, the “C destructors” really are the “Rust |
| 125 | +destructors”. |
| 126 | + |
| 127 | +The `Option` is here to handle the situation where a null pointer is |
| 128 | +passed to the destructor. |
| 129 | + |
| 130 | + |
| 131 | +[`own`](https://github.com/wasmerio/wasmer/blob/f548f268f2335693b97ad7ca08af72c320daf59a/lib/c-api/tests/wasm_c_api/wasm-c-api/include/wasm.h#L46-L65) |
| 132 | +[`wasm.h`](https://github.com/wasmerio/wasmer/blob/f548f268f2335693b97ad7ca08af72c320daf59a/lib/c-api/tests/wasm_c_api/wasm-c-api/include/wasm.h) |
0 commit comments