百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 编程字典 > 正文

用 Rust 创建高性能 JavaScript API

toyiye 2024-08-22 02:38 7 浏览 0 评论

WasmEdge 将 Rust 的性能和 JavaScript 的易用性结合在一起

在我之前的文章中,我讨论了如何将 JavaScript 代码嵌入到 Rust 程序。 然而,对于 JavaScript 开发者来说,需求往往相反——是将 Rust 函数合并到 JavaScript API 中。 这使开发者能够使用“纯 JavaScript”编写程序,同时仍然可以利用 Rust 函数的绝佳性能。 使用 WasmEdge Runtime ,你可以做到这一点。


接下来,让我们看几个示例。查看 wasmedge-quickjs Github repo 并打开 examples/embed_js 文件夹。

$ git clone https://github.com/second-state/wasmedge-quickjs
$ cd examples/embed_js

要先安装 Rust 和 WasmEdge 才能构建和运行本文中的示例。

embed_js demo 展示了几个关于如何在 Rust 中嵌入 JavaScript 的不同示例。 你可以按如下方式构建和运行所有示例。

$ cargo build --target wasm32-wasi --release
$ wasmedge --dir .:. target/wasm32-wasi/release/embed_js.wasm

创建一个 JavaScript 函数 API

以下代码片段定义了一个 Rust 函数,该函数可以作为 API 合并到 JavaScript 解释器中。

fn run_rust_function(ctx: &mut Context) {

    struct HelloFn;
    impl JsFn for HelloFn {
        fn call(_ctx: &mut Context, _this_val: JsValue, argv: &[JsValue]) -> JsValue {
            println!("hello from rust");
            println!("argv={:?}", argv);
            JsValue::UnDefined
        }
    }
    
    ...
}

下面的代码片段展示了如何将这个 Rust 函数添加到 JavaScript 解释器中,命名为 hi() 作为它的 JavaScript API,然后从 JavaScript 代码中调用它。

fn run_rust_function(ctx: &mut Context) {
    ...
    
    let f = ctx.new_function::<HelloFn>("hello");
    ctx.get_global().set("hi", f.into());
    let code = r#"hi(1,2,3)"#;
    let r = ctx.eval_global_str(code);
    println!("return value:{:?}", r);
}


执行结果如下:

hello from rust
argv=[Int(1), Int(2), Int(3)]
return value:UnDefined

使用这种方法,可以创建一个带有自定义 API 函数的 JavaScript 解释器。 解释器在 WasmEdge 内部运行,可以从 CLI 或网络执行调用此类 API 函数的 JavaScript 代码。

创建 JavaScript 对象 API

在 JavaScript API 设计中,我们有时需要提供一个同时封装数据和函数的对象。 在以下示例中,我们为 JavaScript API 定义了一个 Rust 函数。

fn rust_new_object_and_js_call(ctx: &mut Context) {

    struct ObjectFn;
    impl JsFn for ObjectFn {
        fn call(_ctx: &mut Context, this_val: JsValue, argv: &[JsValue]) -> JsValue {
            println!("hello from rust");
            println!("argv={:?}", argv);
            if let JsValue::Object(obj) = this_val {
                let obj_map = obj.to_map();
                println!("this={:#?}", obj_map);
            }
            JsValue::UnDefined
        }
    }
    
    

然后我们在 Rust 端创建一个“对象”,设置它的数据字段,然后将 Rust 函数注册为与对象关联的 JavaScript 函数。

    let mut obj = ctx.new_object();
    obj.set("a", 1.into());
    obj.set("b", ctx.new_string("abc").into());
    
    let f = ctx.new_function::<ObjectFn>("anything");
    obj.set("f", f.into());
    

接下来,我们使 Rust “对象”作为 JavaScript 对象 test_obj在 JavaScript 解释器中可用。

ctx.get_global().set("test_obj", obj.into());

在 JavaScript 代码中,你现在可以直接使用 test_obj 作为 API 的一部分。

let code = r#"
      print('test_obj keys=',Object.keys(test_obj))
      print('test_obj.a=',test_obj.a)
      print('test_obj.b=',test_obj.b)
      test_obj.f(1,2,3,"hi")
    "#;

    ctx.eval_global_str(code);
}

执行结果如下。

test_obj keys= a,b,f
test_obj.a= 1
test_obj.b= abc
hello from rust
argv=[Int(1), Int(2), Int(3), String(JsString(hi))]
this=Ok(
    {
        "a": Int(
            1,
        ),
        "b": String(
            JsString(
                abc,
            ),
        ),
        "f": Function(
            JsFunction(
                function anything() {
                    [native code]
                },
            ),
        ),
    },
)

一个完整的 JavaScript 对象 API

在前面的示例中,我们演示了从 Rust 创建 JavaScript API 的简单示例。 在这个例子中,我们将创建一个完整的 Rust 模块并将其作为 JavaScript 对象 API 提供。 该项目位于 examples/embed_rust_module 文件夹中。 你可以在 WasmEdge 中将其作为标准 Rust 应用程序构建和运行。

$ cargo build --target wasm32-wasi --release
$ wasmedge --dir .:. target/wasm32-wasi/release/embed_rust_module.wasm

该对象的 Rust 实现是一个模块,如下所示。 它具有数据字段、构造函数、getter 和 setter 以及函数。

mod point {
    use wasmedge_quickjs::*;

    #[derive(Debug)]
    struct Point(i32, i32);

    struct PointDef;

    impl JsClassDef<Point> for PointDef {
        const CLASS_NAME: &'static str = "Point\0";
        const CONSTRUCTOR_ARGC: u8 = 2;

        fn constructor(_: &mut Context, argv: &[JsValue]) -> Option<Point> {
            println!("rust-> new Point {:?}", argv);
            let x = argv.get(0);
            let y = argv.get(1);
            if let ((Some(JsValue::Int(ref x)), Some(JsValue::Int(ref y)))) = (x, y) {
                Some(Point(*x, *y))
            } else {
                None
            }
        }

        fn proto_init(p: &mut JsClassProto<Point, PointDef>) {
            struct X;
            impl JsClassGetterSetter<Point> for X {
                const NAME: &'static str = "x\0";

                fn getter(_: &mut Context, this_val: &mut Point) -> JsValue {
                    println!("rust-> get x");
                    this_val.0.into()
                }

                fn setter(_: &mut Context, this_val: &mut Point, val: JsValue) {
                    println!("rust-> set x:{:?}", val);
                    if let JsValue::Int(x) = val {
                        this_val.0 = x
                    }
                }
            }

            struct Y;
            impl JsClassGetterSetter<Point> for Y {
                const NAME: &'static str = "y\0";

                fn getter(_: &mut Context, this_val: &mut Point) -> JsValue {
                    println!("rust-> get y");
                    this_val.1.into()
                }

                fn setter(_: &mut Context, this_val: &mut Point, val: JsValue) {
                    println!("rust-> set y:{:?}", val);
                    if let JsValue::Int(y) = val {
                        this_val.1 = y
                    }
                }
            }

            struct FnPrint;
            impl JsMethod<Point> for FnPrint {
                const NAME: &'static str = "pprint\0";
                const LEN: u8 = 0;

                fn call(_: &mut Context, this_val: &mut Point, _argv: &[JsValue]) -> JsValue {
                    println!("rust-> pprint: {:?}", this_val);
                    JsValue::Int(1)
                }
            }

            p.add_getter_setter(X);
            p.add_getter_setter(Y);
            p.add_function(FnPrint);
        }
    }

    struct PointModule;
    impl ModuleInit for PointModule {
        fn init_module(ctx: &mut Context, m: &mut JsModuleDef) {
            m.add_export("Point\0", PointDef::class_value(ctx));
        }
    }

    pub fn init_point_module(ctx: &mut Context) {
        ctx.register_class(PointDef);
        ctx.register_module("point\0", PointModule, &["Point\0"]);
    }
}

在解释器的实现中,首先调用 point::init_point_module 将 Rust 模块注册到 JavaScript 上下文中,然后我们就可以运行一个简单地使用 point 对象的 JavaScript 程序。

use wasmedge_quickjs::*;
fn main() {
    let mut ctx = Context::new();
    point::init_point_module(&mut ctx);

    let code = r#"
      import('point').then((point)=>{
        let p0 = new point.Point(1,2)
        print("js->",p0.x,p0.y)
        p0.pprint()
        try{
            let p = new point.Point()
            print("js-> p:",p)
            print("js->",p.x,p.y)
            p.x=2
            p.pprint()
        } catch(e) {
            print("An error has been caught");
            print(e)
        }    
      })
    "#;

    ctx.eval_global_str(code);
    ctx.promise_loop_poll();
}

上述应用程序的执行结果如下。

rust-> new Point [Int(1), Int(2)]
rust-> get x
rust-> get y
js-> 1 2
rust-> pprint: Point(1, 2)
rust-> new Point []
js-> p: undefined
An error has been caught
TypeError: cannot read property 'x' of undefined

接下来

使用 Rust 函数和模块来实现 JavaScript API 是一个强大的想法。 这允许 WasmEdge Runtime 显著提高 JavaScript 应用程序的性能。 然而,Rust 函数仍然需要编译成 WebAssembly 字节码。 对于一些函数,比如 AI 推理,直接从 WasmEdge 调用原生 C 库函数效率更高。 在下一篇文章中,我将讨论如何运用 WasmEdge 来支持 C 原生函数,然后将这些函数公开为 Rust 和 JavaScript API。

云原生 WebAssembly 中的 JavaScript 是下一代云和边缘计算基础设施中的新兴领域。 我们也是刚开始涉足。如果对此感兴趣,请加入 WasmEdge 项目或通过提出 feature request issue 告诉我们,你需要的是什么。

相关推荐

# Python 3 # Python 3字典Dictionary(1)

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值(key=>value)对用冒号(:)分割,每个对之间用逗号(,)分割,整个字典包括在花括号({})中,格式如...

Python第八课:数据类型中的字典及其函数与方法

Python3字典字典是另一种可变容器模型,且可存储任意类型对象。字典的每个键值...

Python中字典详解(python 中字典)

字典是Python中使用键进行索引的重要数据结构。它们是无序的项序列(键值对),这意味着顺序不被保留。键是不可变的。与列表一样,字典的值可以保存异构数据,即整数、浮点、字符串、NaN、布尔值、列表、数...

Python3.9又更新了:dict内置新功能,正式版十月见面

机器之心报道参与:一鸣、JaminPython3.8的热乎劲还没过去,Python就又双叒叕要更新了。近日,3.9版本的第四个alpha版已经开源。从文档中,我们可以看到官方透露的对dic...

Python3 基本数据类型详解(python三种基本数据类型)

文章来源:加米谷大数据Python中的变量不需要声明。每个变量在使用前都必须赋值,变量赋值以后该变量才会被创建。在Python中,变量就是变量,它没有类型,我们所说的"类型"是变...

一文掌握Python的字典(python字典用法大全)

字典是Python中最强大、最灵活的内置数据结构之一。它们允许存储键值对,从而实现高效的数据检索、操作和组织。本文深入探讨了字典,涵盖了它们的创建、操作和高级用法,以帮助中级Python开发...

超级完整|Python字典详解(python字典的方法或操作)

一、字典概述01字典的格式Python字典是一种可变容器模型,且可存储任意类型对象,如字符串、数字、元组等其他容器模型。字典的每个键值key=>value对用冒号:分割,每个对之间用逗号,...

Python3.9版本新特性:字典合并操作的详细解读

处于测试阶段的Python3.9版本中有一个新特性:我们在使用Python字典时,将能够编写出更可读、更紧凑的代码啦!Python版本你现在使用哪种版本的Python?3.7分?3.5分?还是2.7...

python 自学,字典3(一些例子)(python字典有哪些基本操作)

例子11;如何批量复制字典里的内容2;如何批量修改字典的内容3;如何批量修改字典里某些指定的内容...

Python3.9中的字典合并和更新,几乎影响了所有Python程序员

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

Python3大字典:《Python3自学速查手册.pdf》限时下载中

最近有人会想了,2022了,想学Python晚不晚,学习python有前途吗?IT行业行业薪资高,发展前景好,是很多求职群里严重的香饽饽,而要进入这个高薪行业,也不是那么轻而易举的,拿信工专业的大学生...

python学习——字典(python字典基本操作)

字典Python的字典数据类型是基于hash散列算法实现的,采用键值对(key:value)的形式,根据key的值计算value的地址,具有非常快的查取和插入速度。但它是无序的,包含的元素个数不限,值...

324页清华教授撰写【Python 3 菜鸟查询手册】火了,小白入门字典

如何入门学习python...

Python3.9中的字典合并和更新,了解一下

全文共2837字,预计学习时长9分钟Python3.9正在积极开发,并计划于今年10月发布。2月26日,开发团队发布了alpha4版本。该版本引入了新的合并(|)和更新(|=)运算符,这个新特性几乎...

python3基础之字典(python中字典的基本操作)

字典和列表一样,也是python内置的一种数据结构。字典的结构如下图:列表用中括号[]把元素包起来,而字典是用大括号{}把元素包起来,只不过字典的每一个元素都包含键和值两部分。键和值是一一对应的...

取消回复欢迎 发表评论:

请填写验证码