调用从 Wasmtime 返回字符串的 WASM 函数

Kei*_*arp 3 rust webassembly wasmtime rust-wasm

在较高级别上,我尝试创建一个 Rust 主机程序,该程序在运行时使用 Wasmtime 加载 WASM 模块并调用返回字符串的 WASM 函数。我可以使用数字类型(例如 )来实现此功能usize,但无法弄清楚如何处理字符串(或其他复杂类型)。

在我的插件箱中,我有一个lib.rs编译用的cargo build --target=wasm32-unknown-unknown --release

use std::ffi::CString;
use std::os::raw::c_char;

static PLUGIN_NAME: &'static str = "Test Plugin";

#[no_mangle]
pub extern "C" fn plugin_name() -> *mut c_char {
    let s = CString::new(PLUGIN_NAME).unwrap();
    s.into_raw()
}

#[no_mangle]
pub fn plugin_name_len() -> usize {
    PLUGIN_NAME.len()
}
Run Code Online (Sandbox Code Playgroud)

这是基于这个问题的答案之一中的代码,该代码与我正在寻找的内容很接近,但在主机端使用 JavaScript。

在我的主机箱中我有一个main.rs

use wasmtime::{Engine, Linker, Module, Store};
use wasmtime_wasi::WasiCtxBuilder;

fn main() -> anyhow::Result<()> {
    let engine = Engine::default();

    let Some(file) = std::env::args().nth(1) else {
        anyhow::bail!("USAGE: host <WASM FILE>");
    };

    let module = Module::from_file(&engine, file)?;

    let linker = Linker::new(&engine);
    let wasi = WasiCtxBuilder::new()
        .inherit_stdio()
        .inherit_args()
        .expect("should always be able to inherit args")
        .build();
    let mut store = Store::new(&engine, wasi);

    let instance = linker.instantiate(&mut store, &module)?;

    let Ok(plugin_name_len) = instance.get_typed_func::<(), u32>(&mut store, "plugin_name_len") else {
        anyhow::bail!("Failed to get plugin_name_len");
    };

    let len = plugin_name_len.call(&mut store, ())?;
    println!("Length: {}", len);

    let Ok(plugin_name) = instance.get_typed_func::<(), &str>(&mut store, "plugin_name") else {
        anyhow::bail!("Failed to get plugin_name");
    };

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

但这不会编译,因为你不能&str在调用中使用instance.get_typed_func()

有人可以分享一个如何调用从 Rust Wasmtime 主机程序返回字符串的 WASM 函数的示例(和解释)吗?

Kei*_*arp 6

我已经完成了这个工作。

TL;DR 插件 WASM 函数plugin_name()返回一个 32 位整数,它实际上是指向 WASM 内存的指针。要访问 WASM 内存,您需要Memory通过"memory"访问Instance. 然后,您可以使用从调用中获得的 32 位整数plugin_name()作为字符串第一个字符的偏移量,并将该偏移量 + 字符串的长度作为最后一个字符。将这片u8s 转换为 Vec 并将其输入String::from_utf8(),你就得到了一个String

已更新,正在运行main.rs

use wasmtime::{Engine, Linker, Module, Store};
use wasmtime_wasi::WasiCtxBuilder;

fn main() -> anyhow::Result<()> {
    let engine = Engine::default();

    let Some(file) = std::env::args().nth(1) else {
        anyhow::bail!("USAGE: host <WASM FILE>");
    };

    let module = Module::from_file(&engine, file)?;

    let linker = Linker::new(&engine);
    let wasi = WasiCtxBuilder::new()
        .inherit_stdio()
        .inherit_args()
        .expect("should always be able to inherit args")
        .build();
    let mut store = Store::new(&engine, wasi);

    let instance = linker.instantiate(&mut store, &module)?;

    let Ok(plugin_name_len) = instance.get_typed_func::<(), u32>(&mut store, "plugin_name_len") else {
        anyhow::bail!("Failed to get plugin_name_len");
    };
    let len = plugin_name_len.call(&mut store, ())? as usize;

    let Ok(plugin_name) = instance.get_typed_func::<(), u32>(&mut store, "plugin_name") else {
        anyhow::bail!("Failed to get plugin_name");
    };
    let ptr = plugin_name.call(&mut store, ())? as usize;

    let Some(memory) = instance.get_memory(&mut store, "memory") else {
        anyhow::bail!("Failed to get WASM memory");
    };

    let data = memory.data(&store)[ptr..(ptr + len)].to_vec();
    let name = String::from_utf8(data)?;
    println!("Plugin name: {}", name);

    Ok(())
}
Run Code Online (Sandbox Code Playgroud)

注意 - 我正在将lenptr从转换u32usize,以便我可以使用它们从 Rust 索引到 WASM 内存。