#api-calls #polyglot #javascript

wasm_val

提供从wasm程序调用javascript的类型安全API的库

11个版本

使用旧Rust 2015

0.3.5 2019年3月4日
0.3.4 2019年2月16日
0.3.2 2018年8月2日
0.3.1 2018年7月29日
0.1.2 2018年7月15日

#497 in WebAssembly

每月 21 次下载

MIT/Apache

33KB
711

wasm_val是一个Rust WebAssembly库,它提供了一个类型安全的API,可以动态调用javascript。

简介

wasm-val是一个Rust库,附带一个JavaScript辅助库,提供了访问javascript的动态类型安全API。

它受到了emscripten的C++ Val以及Graal-VM多语言上下文 API的启发。

虽然我意识到已经有更成熟的解决方案,如wasm-bind,可以从Rust WebAssembly调用javascript,但我相信总是可以欣赏到另一种实现方式。

这个项目的最终目标是拥有一个动态和类型安全的API,以访问浏览器提供的任何javascript API。

这是我第一个Rust项目,它已经是一个,我希望它将继续是一个很好的学习经历,因为我发现越来越多的Rust。

许可证

该项目由Apache 2和MIT许可证双重授权。

有关详细信息,请参阅LICENCE.md文件

预览

目前可以从javascript获取值,并设置它们的属性以及调用javascript对象上的方法。

还可以调用构造函数并获取重复调用函数。

以下是一个展示各种功能的示例

// You can obtain a value from the global context
let document = JsValue::get_global("document");
let body = document.get_val("body").unwrap(); 
// And get a property of such a value
let title = document.get_val("title").unwrap().as_str().unwrap();
let from_rust = format!("Hello from rust <3 your title is: {}", title);
// You can also call a constructor
let textNode = document.call_method_with_arg("createTextNode", from_rust.as_str()).unwrap();
// Pass a previously obtained javascript value back to javascript
body.call_method_with_arg("appendChild", textNode);

// If you plan to call a function multiple times for efficiently reasons you can obtain a reference to it:
let console = JSValue::get_global("console");
let console_log = console.get_val("log").unwrap();

console_log.call_with_arg("Hello world");
console_log.call_with_args(&[&"hello world", &true, &" ", &3.14]);

// You can also pass a closure on the javascript side, useful for registering callbacks
const CLOSURE: &dyn Fn(JsValue) -> () = &|val: JsValue| {
    let key_code = val.get_val("keyCode").unwrap();

    let console = JsValue::get_global("console");

    console.call_method_with_args("log", &["Keydown pressed :", &key_code]);
};

body.call_method_with_args("addEventListener", &[&"keydown", &CLOSURE]);

以下类型实现了JsSerializable trait,可以发送到rust和wasm之间

  • 布尔值
  • 原始数字类型(除了i64/u64)
  • str
  • String
  • JsValue(从javascript获取的值)
  • Fn() -> () 和 Fn(JSValue) -> () 主要用于注册事件回调

JsSerializable

JsSerializable是一个trait,为某些类型实现,允许在rust和wasm之间传递值。

如果你要传递一个返回值的闭包(例如,用于promise),则返回类型必须是Box

例如

use wasm_val::{JsValue, JsSerializable};

const CLOSURE: &dyn Fn() -> (Box<JsSerializable>) = &|val: JsValue| {
    return Box::new(42);
};

示例

在examples文件夹中提供了多个示例

  • hello_world:展示了获取和设置值以及调用函数的能力。
  • clock:利用调用构造函数的能力,并展示了由javascript管理的简单动画。
  • create_element:主要展示了调用具有多个参数的函数的能力。
  • canvas_animate_solar_system:一个更完整的例子,已经从Mozilla网站上的canvas动画示例中进行修改
  • register_event_callback:在Rust端注册回调,以在canvas中动画化矩形的移动
  • webgl_animate_squares:点击时添加一个随机的动画方块。利用了将TypedArrays传递给javascript的能力

开始吧

该项目分为两部分:wasm_val,这是一个提供API的Rust库,以及wasm_val_module,它是允许与Rust接口的javascript对应物。

在Rust端

如果您还没有这样做,请通过rustup添加wasm32-unknown-unknown目标

rustup target add wasm32-unknown-unknown

将wasm_val依赖项添加到您的Cargo.toml中

[dependencies]
wasm_val = "0.3.5"

同样重要的是也要将您的Rust项目类型声明为cdylib。例如

[lib]
path = "src/lib.rs"
crate-type = ["cdylib"]

在您的main lib.rs中声明wasm_val包

同时导出具有#[no_mangle]属性的extern "C" main函数,以便它可以从javascript中访问。

以下是一个hello world示例。

extern crate wasm_val;

use wasm_val::{JsValue};

#[no_mangle]
pub extern "C" fn main() -> () {
    let console = JsValue::get_global("console");

    console.call_with_arg("log", "Hello from Rust :)");
}

注意:由于当前wasm32-unknown-unknown目标的限制,建议始终以发布模式构建项目。例如

cargo build --target=wasm32-unknown-unknown --release

在javascript端

假设您位于您的web应用所在的文件夹中。

首先,使用npm安装wasm_val_module

npm install [email protected]

或者从该存储库手动获取。

然后在您的index.html或等效文件中

<script type="module">
    import { WasmValModule } from "./node_modules/wasm-val-module/wasm_val_module.js";
    const mod = new WasmValModule('path/to/rust_lib.wasm', window);

    mod.run()
        .then((instance) => {
            instance.exports.main();
        });
</script>

WasmValModule构造函数接受wasm文件的路径以及一个上下文对象,还有一个可选的选项对象。

上下文对象是提供Rust端可访问的javascript成员的内容。

默认选项是

{
    rust_panic: {
        register_hook: true,
        panic_fn: console.error,
    }
}

有关更多信息,请参阅wasm_val_module的文档。

希望这就足够了。

已知问题和限制

目前,发送到javascript端(如事件监听器)的Rust闭包(如)永远不会被垃圾回收。请谨慎使用。

依赖项

~2MB
~51K SLoC