如何导入WASM(Rust)中的WASM模块并传递字符串参数
我想从WASM内部实例化WASM模块,之后此JS-Sys示例。在示例中,调用add
函数,该功能传递了i32参数。
我创建了一个Hello World函数,该功能将字符串作为参数并返回字符串。但是,将此功能调用不起作用,因为它返回未定义。
通常,Wasm bindgen生成胶水代码,该胶代码创建上下文并将字符串放在堆栈上。但是,生锈没有生成这样的代码。
如何从Rust中的WASM加载并执行Hello
函数?
imported_lib.rs
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()
}
main_lib.rs
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();
});
}
I want to instantiate a Wasm module from inside a Wasm module, following this js-sys example. In the example, the add
function is called which passes i32 parameters.
I've created a hello world function, which takes a string as a parameter and returns a string. However, calling this function doesn't work, as it returns undefined.
Normally wasm bindgen generates glue code which creates a context and puts the string on the stack. However, no such code is generated for Rust.
How can I load and execute the hello
function from Wasm in Rust?
imported_lib.rs
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()
}
main_lib.rs
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();
});
}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
我确实花了几天的时间来解决这个问题。我希望它有帮助!
由于在这里包装很多信息。我将尝试简短,但是如果您想了解更多,请让我知道,我会扩展我的答案。
简短的解释为什么发生这种情况会
发生这种情况,因为默认情况下,WASM不返回
strings
,因此wasm-bindgen的智能人员
在运行时做了一些事情-pack Build
它生成了为您执行此操作的JS代码。功能Hello
不会返回字符串
,而是返回指针。要证明这一点,您可以检查构建imported_lib.rs
时生成的文件,您可以看到它生成了文件
imported_lib.wasm.d.ts
此:add
确实与您声明的方式匹配,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
的事情。这就是我的方式:在您的主项目中解决方案
创建一个文件夹调用
js
,在该文件夹中创建文件调用gets> gets tring.js
。您的项目文件系统应该看起来像这样:并且文件应该具有以下方式:
在您的
main_lib.rs
中,请添加以下内容:应该完全工作!
It really took me days to solve this problem. I hope it helps!
Since it's a lot of info to pack here. I will try to keep it short but If you want to know more let me know and I'll expand on my answer.
Short explanation of why this is happening
This actually happens because wasm by default does not return
Strings
so the smart people atwasm-bindgen
did something so when you runwasm-pack build
it generates a js code that do this for you. The functionhello
does not return astring
, instead returns a pointer. To proof this, you can check the files generated when you build theimported_lib.rs
You can see that it generates the file
imported_lib.wasm.d.ts
that looks something like this:add
does match how you declared, 2 parameters and returns anumber
. In the other hand you can see that the functionhello
takes 3 parameters and return avoid
(very different to how you declared)wasp-pack build
generated some extra functions like (__wbindgen_add_to_stack_pointer
,__wbindgen_free
, etc). With these functions they are able to get the string.The other file that the command
wasm-pack build
generates isimported_lib_bg.js
. In this file you can see that they export the functionhello
. Here it's where JavaScript call the compiled wasm function and "translate" the pointer to the actual string.So basically you would have to do something similar to what it is in the file
imported_lib_bg.js
. This is how I did it:Solution
In your main project create a folder call
js
, and inside that folder create a file callgetString.js
. Your project filesystem should look something like this:And the file should have this:
In your
main_lib.rs
add this:That should totally work!