Jas*_*els 9 rust webassembly wasm-bindgen
我想按照这个 js-sys 示例从 Wasm 模块内部实例化 Wasm 模块。在示例中,add
调用的函数传递 i32 参数。
我创建了一个 hello world 函数,它接受一个字符串作为参数并返回一个字符串。但是,调用此函数不起作用,因为它返回未定义。
通常,wasm bindgen 会生成粘合代码,该代码创建上下文并将字符串放入堆栈中。然而,Rust 不会生成这样的代码。
如何hello
在 Rust 中加载并执行 Wasm 中的函数?
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
#[wasm_bindgen]
pub fn hello(name: String) -> String {
format!("hello {:?}", name).into()
}
Run Code Online (Sandbox Code Playgroud)
use js_sys::{Function, Object, Reflect, WebAssembly};
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
use wasm_bindgen_futures::{spawn_local, JsFuture};
// lifted from the `console_log` example
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(a: &str);
}
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
const WASM: &[u8] = include_bytes!("imported_lib.wasm");
async fn run_async() -> Result<(), JsValue> {
let a = JsFuture::from(WebAssembly::instantiate_buffer(WASM, &Object::new())).await?;
let b: WebAssembly::Instance = Reflect::get(&a, &"instance".into())?.dyn_into()?;
let c = b.exports();
let add = Reflect::get(c.as_ref(), &"add".into())?
.dyn_into::<Function>()
.expect("add export wasn't a function");
let three = add.call2(&JsValue::undefined(), &1.into(), &2.into())?;
console_log!("1 + 2 = {:?}", three); // 1 + 2 = JsValue(3)
let hello = Reflect::get(c.as_ref(), &"hello".into())?
.dyn_into::<Function>()
.expect("hello export wasn't a function");
let hello_world = hello.call1(&JsValue::undefined(), &"world".into());
console_log!("{:?}", hello_world); // JsValue(undefined)
Ok(())
}
#[wasm_bindgen(start)]
pub fn run() {
spawn_local(async {
run_async().await.unwrap_throw();
});
}
Run Code Online (Sandbox Code Playgroud)
我真的花了几天时间才解决这个问题。我希望它能有所帮助!\n因为这里需要包含大量信息。我会尽量保持简短,但如果您想了解更多信息,请告诉我,我将扩展我的答案。
\n这实际上是因为 wasm 默认情况下不会返回Strings
,所以聪明人wasm-bindgen
做了一些事情,所以当你运行它时wasm-pack build
,它会生成一个 js 代码来为你做这件事。该函数hello
不返回string
,而是返回一个指针。为了证明这一点,您可以检查构建时生成的文件imported_lib.rs
可以看到生成了文件imported_lib.wasm.d.ts
您可以看到它生成了如下所示的
export const memory: WebAssembly.Memory;\nexport function add(a: number, b: number): number;\nexport function hello(a: number, b: number, c: number): void;\nexport function popo(a: number): void;\nexport function __wbindgen_add_to_stack_pointer(a: number): number;\nexport function __wbindgen_malloc(a: number): number;\nexport function __wbindgen_realloc(a: number, b: number, c: number): number;\nexport function __wbindgen_free(a: number, b: number): void;\n
Run Code Online (Sandbox Code Playgroud)\nadd
确实与您声明的方式匹配,2 个参数并返回一个number
. 另一方面,你可以看到该函数hello
有 3 个参数并返回一个void
(与您声明的方式非常不同)wasp-pack build
生成了一些额外的函数,例如 ( __wbindgen_add_to_stack_pointer
,__wbindgen_free
等)。通过这些函数,他们能够获取字符串。该命令wasm-pack build
生成的另一个文件是imported_lib_bg.js
. 在这个文件中你可以看到他们导出了函数hello
。这里是 JavaScript 调用编译后的 wasm 函数并将指针“翻译”为实际字符串的地方。
所以基本上你必须做一些类似于文件中的事情imported_lib_bg.js
。我就是这样做的:
在您的主项目中创建一个文件夹 call js
,并在该文件夹内创建一个文件 callgetString.js
。您的项目文件系统应如下所示:
mainProject\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 js\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 getString.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main_lib.rs\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ...\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 www\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ...\n
Run Code Online (Sandbox Code Playgroud)\n该文件应该包含以下内容:
\nmainProject\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 js\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 getString.js\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 src\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 main_lib.rs\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ...\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 www\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 ...\n
Run Code Online (Sandbox Code Playgroud)\n在你的main_lib.rs
添加这个:
function getInt32Memory0(wasm_memory_buffer) {\n let cachedInt32Memory0 = new Int32Array(wasm_memory_buffer);\n return cachedInt32Memory0;\n}\n\nfunction getStringFromWasm(ptr, len, wasm_memory_buffer) {\n const mem = new Uint8Array(wasm_memory_buffer);\n const slice = mem.slice(ptr, ptr + len);\n const ret = new TextDecoder(\'utf-8\').decode(slice);\n return ret;\n}\n\nlet WASM_VECTOR_LEN = 0;\n\nfunction getUint8Memory0(wasm_memory_buffer) {\n let cachedUint8Memory0 = new Uint8Array(wasm_memory_buffer);\n return cachedUint8Memory0;\n}\n\nconst lTextEncoder = typeof TextEncoder === \'undefined\' ? (0, module.require)(\'util\').TextEncoder : TextEncoder;\n\nlet cachedTextEncoder = new lTextEncoder(\'utf-8\');\n\nconst encodeString = (typeof cachedTextEncoder.encodeInto === \'function\'\n ? function (arg, view) {\n return cachedTextEncoder.encodeInto(arg, view);\n}\n : function (arg, view) {\n const buf = cachedTextEncoder.encode(arg);\n view.set(buf);\n return {\n read: arg.length,\n written: buf.length\n };\n});\n\nfunction passStringToWasm0(arg, malloc, realloc, wasm_memory_buffer) {\n\n if (realloc === undefined) {\n const buf = cachedTextEncoder.encode(arg);\n const ptr = malloc(buf.length);\n getUint8Memory0(wasm_memory_buffer).subarray(ptr, ptr + buf.length).set(buf);\n WASM_VECTOR_LEN = buf.length;\n return ptr;\n }\n\n let len = arg.length;\n let ptr = malloc(len);\n\n const mem = getUint8Memory0(wasm_memory_buffer);\n\n let offset = 0;\n\n for (; offset < len; offset++) {\n const code = arg.charCodeAt(offset);\n if (code > 0x7F) break;\n mem[ptr + offset] = code;\n }\n\n if (offset !== len) {\n if (offset !== 0) {\n arg = arg.slice(offset);\n }\n ptr = realloc(ptr, len, len = offset + arg.length * 3);\n const view = getUint8Memory0(wasm_memory_buffer).subarray(ptr + offset, ptr + len);\n const ret = encodeString(arg, view);\n\n offset += ret.written;\n }\n\n WASM_VECTOR_LEN = offset;\n return ptr;\n}\n\n\n/**\n* @param {&JsValue} wasm: wasm object\n* @param {string} fn_name: function\'s name to call in the wasm object\n* @param {string} name: param to give to fn_name\n* @returns {string}\n*/\nexport function getString(wasm, fn_name, name) {\n try {\n const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);\n const ptr0 = passStringToWasm0(name, wasm.__wbindgen_malloc, wasm.__wbindgen_realloc, wasm.memory.buffer);\n const len0 = WASM_VECTOR_LEN;\n //wasm.hello(retptr, ptr0, len0);\n wasm[fn_name](retptr, ptr0, len0);\n var r0 = getInt32Memory0(wasm.memory.buffer)[retptr / 4 + 0];\n var r1 = getInt32Memory0(wasm.memory.buffer)[retptr / 4 + 1];\n return getStringFromWasm(r0, r1, wasm.memory.buffer);\n } finally {\n wasm.__wbindgen_add_to_stack_pointer(16);\n wasm.__wbindgen_free(r0, r1);\n }\n}\n
Run Code Online (Sandbox Code Playgroud)\n那应该完全有效!
\n 归档时间: |
|
查看次数: |
4289 次 |
最近记录: |