#javascript-engine #v8 #wrapper #mini #object #array #value

mini-v8

为 Rust 提供的最小嵌入式 V8 JavaScript 引擎包装器

6 个版本 (3 个重大更新)

0.4.1 2023 年 8 月 26 日
0.4.0 2023 年 1 月 16 日
0.3.0 2020 年 8 月 5 日
0.2.1 2020 年 7 月 8 日
0.1.0 2019 年 9 月 21 日

#336 in 网页编程

Download history 28/week @ 2024-03-14 11/week @ 2024-03-21 25/week @ 2024-03-28 14/week @ 2024-04-04 1/week @ 2024-04-25 9/week @ 2024-05-16 22/week @ 2024-05-23 21/week @ 2024-05-30 12/week @ 2024-06-06 156/week @ 2024-06-13 91/week @ 2024-06-20 117/week @ 2024-06-27

每月 376 次下载
用于 2 crates

MIT 许可证

86KB
2K SLoC

MiniV8

MiniV8 是为 Rust 提供的最小嵌入式 V8 JavaScript 引擎 包装器。

快速浏览

extern crate mini_v8;

use mini_v8::{MiniV8, Array, Object, Function};

fn main() {
    // A `MiniV8` is a V8 context that can execute JavaScript.
    let mv8 = MiniV8::new();

    // JavaScript can be evaluated and transformed into various Rust types.
    let value: usize = mv8.eval("2 + 2").unwrap();
    assert_eq!(value, 4);
    let value: String = mv8.eval("`Two plus two is ${2 + 2}`").unwrap();
    assert_eq!(value, "Two plus two is 4".to_string());

    // JavaScript objects can be directly manipulated, without eagerly converting into Rust.
    let array: Array = mv8.eval("[123, 'abc']").unwrap();
    let element: String = array.get(1).unwrap();
    assert_eq!(element, "abc".to_string());
    array.set(0, 456).unwrap();

    // JavaScript values can be created directly, without using `mv8.eval` as above.
    let object: Object = mv8.create_object();
    let js_string = mv8.create_string("This string is owned by JavaScript!");
    object.set("someString", js_string).unwrap();

    // Rust functions can be passed into JavaScript.
    let rust_add = mv8.create_function(|inv| {
        let (a, b): (f64, f64) = inv.args.into(&inv.mv8)?;
        Ok(a + b)
    });
    // Like any other value, these functions can be bound as properties of an object.
    //
    // (Notice: Cloning values just creates a new reference to the value, similar to JavaScript's
    // own object referencing semantics.)
    object.set("add", rust_add.clone()).unwrap();

    // JavaScript functions can be passed into Rust.
    let js_add: Function = mv8.eval("(a, b) => a + b").unwrap();
    // Functions can be called from within Rust.
    let value: f64 = rust_add.call((1, 2)).unwrap();
    assert_eq!(value, 3.0);
    let value: f64 = js_add.call((1, 2)).unwrap();
    assert_eq!(value, 3.0);
}

其他功能

  • 可以将自定义用户数据绑定到 MiniV8(参见 MiniV8::set_user_data)。这对于在嵌入式 Rust 函数调用之间存储状态非常有用。
  • 可以将各种标准 Rust 类型传递到 JavaScript 环境中(数值类型、StringVecBTreeMapHashSet 等)。您还可以为自定义类型定义转换接口。有关更多信息,请参阅 ToValue/FromValuesrc/conversion.rs
  • 支持执行超时。

MiniV8 受到 MiniRacer Ruby 钩子的启发,该钩子实现了与 V8 的最小桥接。从其 README 中:“这种最小设计减少了接触面,使得升级 [V8] 更简单,并且更简单的全面测试。” 与雄心勃勃的 v8-rs 钩子形成对比,该钩子因为“维护负担过高”而不再维护。”

MiniV8 依赖于可爱的 rusty_v8 钩子(它是更大项目 Deno 的一部分)来构建并提供 V8 的 FFI。

这项工作是本人自己的 ducc 钩子的伴侣,该钩子提供了对 Duktape JavaScript 引擎的最小包装。

目的

在使用任何FFI时,选择整个源库的一个子集通常比试图一对一映射其所有结构要容易得多。这当然意味着要牺牲一些功能和性能,但可以在API设计期间提供灵活性,从而在目标语言中产生更自然的代码。

显然,我选择了MiniV8的“最小桥接”模型。如果您想充分利用V8的众多内部结构,那么MiniV8可能不适合您。如果您想从Rust中优雅地执行脚本,使用其中最快的JavaScript引擎之一,那么MiniV8可能适合您。

缺点

  • MiniV8只实现了现代ECMAScript提供的完整类型集的最小桥接。也许当前的Value桥接应该扩展以支持一些额外的特殊对象类型(Uint8Array看起来很有用)。
  • 一旦将一个Error转换为要抛出为JavaScript中的异常的Value,就无法回退。能够维护Rust-JavaScript边界之间的源Error将会很棒。这应该通过在异常值中添加一个指向Error的隐藏属性来实现(我们已经用这种技巧将Rust闭包绑定到V8函数)。
  • 不支持限制内存使用。

依赖项

~74MB
~1.5M SLoC