從 JS 匯入函式

既然我們已經將一些豐富的功能匯出到 JS,現在也是時候匯入一些功能了!這裡的目標基本上是在 Rust 中實作 JS 的 import 語句,並具有精美的類型和所有功能。

首先,假設我們反轉上面的函式,而是想在 JS 中產生問候語,但從 Rust 中呼叫它。例如,我們可能有

#![allow(unused)]
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)]
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 對是作為兩個引數傳遞的,而對於回傳值,我們間接接收一個值(長度),同時直接接收回傳的指標。