从 JS 导入函数

既然我们已经向 JS 导出了一些丰富的功能,现在也是时候导入一些了!这里的目标基本上是在 Rust 中实现 JS import 语句,并使用所有花哨的类型。

首先,假设我们反转上面的函数,改为在 JS 中生成问候语,但从 Rust 中调用它。例如,我们可能有


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

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

导入的基本思想与导出相同,即我们将在 JS 和 Rust 中都有垫片来执行必要的转换。让我们首先看看 JS 垫片的作用

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;
}

getStringFromWasmpassStringToWasm 与我们之前看到的相同,并且与上面的 __wbindgen_object_drop_ref 一样,我们现在有了来自我们模块的这个奇怪的导出!__wbg_f_greet 函数是由 wasm-bindgen 生成的,用于实际在 foo.wasm 模块中导入。

我们看到的生成的 foo.js./greet 模块导入,名称为 greet(Rust 中的函数导入就是这么说的),然后 __wbg_f_greet 函数会垫片该导入。

这里发生了一些棘手的 ABI 业务,所以让我们也看一下生成的 Rust。和以前一样,这是从实际生成的代码简化而来的。


# #![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)
        )
    }
}
#}

在这里我们可以看到,greet 函数已经生成,但它基本上只是对我们正在调用的 __wbg_f_greet 函数的垫片。参数的 ptr/len 对作为两个参数传递,而对于返回值,我们间接接收一个值(长度),同时直接接收返回的指针。