Importing a function from JS

Now that we've exported some rich functionality to JS it's also time to import some! The goal here is to basically implement JS import statements in Rust, with fancy types and all.

First up, let's say we invert the function above and instead want to generate greetings in JS but call it from Rust. We might have, for example:


# #![allow(unused_variables)]
#fn main() {
#[wasm_bindgen(module = "./greet")]
extern "C" {
    fn greet(a: &str) -> String;
}

fn other_code() {
    let greeting = greet("foo");
    // ...
}
#}

The basic idea of imports is the same as exports in that we'll have shims in both JS and Rust doing the necessary translation. Let's first see the JS shim in action:

import * as wasm from './foo_bg';

import { greet } from './greet';

// ...

export function __wbg_f_greet(ptr0, len0, wasmretptr) {
  const [retptr, retlen] = passStringToWasm(greet(getStringFromWasm(ptr0, len0)));
  (new Uint32Array(wasm.memory.buffer))[wasmretptr / 4] = retlen;
  return retptr;
}

The getStringFromWasm and passStringToWasm are the same as we saw before, and like with __wbindgen_object_drop_ref far above we've got this weird export from our module now! The __wbg_f_greet function is what's generated by wasm-bindgen to actually get imported in the foo.wasm module.

The generated foo.js we see imports from the ./greet module with the greet name (was the function import in Rust said) and then the __wbg_f_greet function is shimming that import.

There's some tricky ABI business going on here so let's take a look at the generated Rust as well. Like before this is simplified from what's actually generated.


# #![allow(unused_variables)]
#fn main() {
extern "C" fn greet(a: &str) -> String {
    extern "C" {
        fn __wbg_f_greet(a_ptr: *const u8, a_len: usize, ret_len: *mut usize) -> *mut u8;
    }
    unsafe {
        let a_ptr = a.as_ptr();
        let a_len = a.len();
        let mut __ret_strlen = 0;
        let mut __ret_strlen_ptr = &mut __ret_strlen as *mut usize;
        let _ret = __wbg_f_greet(a_ptr, a_len, __ret_strlen_ptr);
        String::from_utf8_unchecked(
            Vec::from_raw_parts(_ret, __ret_strlen, __ret_strlen)
        )
    }
}
#}

Here we can see that the greet function was generated but it's largely just a shim around the __wbg_f_greet function that we're calling. The ptr/len pair for the argument is passed as two arguments and for the return value we're receiving one value (the length) indirectly while directly receiving the returned pointer.