|
| 1 | +# `host_binding` |
| 2 | + |
| 3 | +The `host_binding` attribute is the converse of the [`structural` |
| 4 | +attribute](structural.html). It configures how `wasm-bindgen` will generate JS |
| 5 | +glue to call the imported function. The naming here is intended convey that this |
| 6 | +attribute is intended to implement the semantics of the future [host bindings |
| 7 | +proposal][host-bindings] for WebAssembly. |
| 8 | + |
| 9 | +[host-bindings]: https://github.com/WebAssembly/host-bindings |
| 10 | +[reference-types]: https://github.com/WebAssembly/reference-types |
| 11 | + |
| 12 | +The `host_binding` attribute is intended to be purely related to performance. It |
| 13 | +ideally has no user-visible effect, and well-typed `structural` imports (the |
| 14 | +default) should be able to transparently switch to `host_binding` eventually. |
| 15 | + |
| 16 | +The eventual performance aspect is that with the [host bindings |
| 17 | +proposal][host-bindings] then `wasm-bindgen` will need to generate far fewer JS |
| 18 | +shims to import than it does today. For example, consider this import today: |
| 19 | + |
| 20 | +```rust |
| 21 | +#[wasm_bindgen] |
| 22 | +extern { |
| 23 | + type Foo; |
| 24 | + #[wasm_bindgen(method)] |
| 25 | + fn bar(this: &Foo, argument: &str) -> JsValue; |
| 26 | +} |
| 27 | +``` |
| 28 | + |
| 29 | +**Without the `host_binding` attribute** the generated JS looks like this: |
| 30 | + |
| 31 | +```js |
| 32 | +// without `host_binding` |
| 33 | +export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) { |
| 34 | + let varg1 = getStringFromWasm(arg1, arg2); |
| 35 | + return addHeapObject(getObject(arg0).bar(varg1)); |
| 36 | +} |
| 37 | +``` |
| 38 | + |
| 39 | +We can see here that this JS shim is required, but it's all relatively |
| 40 | +self-contained. It does, however, execute the `bar` method in a duck-type-y |
| 41 | +fashion in the sense that it never validates `getObject(arg0)` is of type |
| 42 | +`Foo` to actually call the `Foo.prototype.bar` method. |
| 43 | + |
| 44 | +If we instead, however, write this: |
| 45 | + |
| 46 | +```rust |
| 47 | +#[wasm_bindgen] |
| 48 | +extern { |
| 49 | + type Foo; |
| 50 | + #[wasm_bindgen(method, host_binding)] // note the change here |
| 51 | + fn bar(this: &Foo, argument: &str) -> JsValue; |
| 52 | +} |
| 53 | +``` |
| 54 | + |
| 55 | +it generates this JS glue (roughly): |
| 56 | + |
| 57 | +```js |
| 58 | +const __wbg_bar_target = Foo.prototype.bar; |
| 59 | + |
| 60 | +export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) { |
| 61 | + let varg1 = getStringFromWasm(arg1, arg2); |
| 62 | + return addHeapObject(__wbg_bar_target.call(getObject(arg0), varg1)); |
| 63 | +} |
| 64 | +``` |
| 65 | + |
| 66 | +The difference here is pretty subtle, but we can see how the function being |
| 67 | +called is hoisted out of the generated shim and is bound to always be |
| 68 | +`Foo.prototype.bar`. This then uses the `Function.call` method to invoke that |
| 69 | +function with `getObject(arg0)` as the receiver. |
| 70 | + |
| 71 | +But wait, there's still a JS shim here even with `host_binding`! That's true, |
| 72 | +and this is simply a fact of future WebAssembly proposals not being implemented |
| 73 | +yet. The semantics, though, match the future [host bindings |
| 74 | +proposal][host-bindings] because the method being called is determined exactly |
| 75 | +once, and it's located on the prototype chain rather than being resolved at |
| 76 | +runtime when the function is called. |
| 77 | + |
| 78 | +## Interaction with future proposals |
| 79 | + |
| 80 | +If you're curious to see how our JS shim will be eliminated entirely, let's take |
| 81 | +a look at the generated bindings. We're starting off with this: |
| 82 | + |
| 83 | +```js |
| 84 | +const __wbg_bar_target = Foo.prototype.bar; |
| 85 | + |
| 86 | +export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) { |
| 87 | + let varg1 = getStringFromWasm(arg1, arg2); |
| 88 | + return addHeapObject(__wbg_bar_target.call(getObject(arg0), varg1)); |
| 89 | +} |
| 90 | +``` |
| 91 | + |
| 92 | +... and once the [reference types proposal][reference-types] is implemented then |
| 93 | +we won't need some of these pesky functions. That'll transform our generated JS |
| 94 | +shim to look like: |
| 95 | + |
| 96 | +```js |
| 97 | +const __wbg_bar_target = Foo.prototype.bar; |
| 98 | + |
| 99 | +export function __wbg_bar_a81456386e6b526f(arg0, arg1, arg2) { |
| 100 | + let varg1 = getStringFromWasm(arg1, arg2); |
| 101 | + return __wbg_bar_target.call(arg0, varg1); |
| 102 | +} |
| 103 | +``` |
| 104 | + |
| 105 | +Getting better! Next up we need the host bindings proposal. Note that the |
| 106 | +proposal is undergoing some changes right now so it's tough to link to reference |
| 107 | +documentation, but it suffices to say that it'll empower us with at least two |
| 108 | +different features. |
| 109 | + |
| 110 | +First, host bindings promises to provide the concept of "argument conversions". |
| 111 | +The `arg1` and `arg2` values here are actually a pointer and a length to a utf-8 |
| 112 | +encoded string, and with host bindings we'll be able to annotate that this |
| 113 | +import should take those two arguments and convert them to a JS string (that is, |
| 114 | +the *host* should do this, the WebAssembly engine). Using that feature we can |
| 115 | +futher trim this down to: |
| 116 | + |
| 117 | +```js |
| 118 | +const __wbg_bar_target = Foo.prototype.bar; |
| 119 | + |
| 120 | +export function __wbg_bar_a81456386e6b526f(arg0, varg1) { |
| 121 | + return __wbg_bar_target.call(arg0, varg1); |
| 122 | +} |
| 123 | +``` |
| 124 | + |
| 125 | +And finally, the second promise of the host bindings proposal is that we can |
| 126 | +flag a function call to indicate the first argument is the `this` binding of the |
| 127 | +function call. Today the `this` value of all called imported functions is |
| 128 | +`undefined`, and this flag (configured with host bindings) will indicate the |
| 129 | +first argument here is actually the `this`. |
| 130 | + |
| 131 | +With that in mind we can further transform this to: |
| 132 | + |
| 133 | +```js |
| 134 | +export const __wbg_bar_a81456386e6b526f = Foo.prototype.bar; |
| 135 | +``` |
| 136 | + |
| 137 | +and voila! We, with [reference types][reference-types] and [host |
| 138 | +bindings][host-bindings], now have no JS shim at all necessary to call the |
| 139 | +imported function! |
0 commit comments