深入探究 Rust、V8 和 JavaScript™️ 传奇

不幸的是,今天我几乎没有时间深入研究有趣的话题。我花了一些时间在一个业余项目上(我可能会在接下来的几天里写一下),做了我的日常工作,做了一些运动。不过,我的重点仍然是**Deno**、**Rust**和**V8引擎**,就像我之前的文章一样。

我设法回顾了 Matouš Dzivjak 的精彩文章,他在其中解释了如何使用 **Rust V8** 构建运行时。

文章中的代码有点过时,无法在当前版本的 Rust 上运行。我设法让它运行起来,尽管我实际上还不了解 Rust(所以这要么是一项简单的任务,要么我真的很幸运)。

以下是更新后的代码,其中有一些细微的调整和一些注释,有助于理解。当然,所有功劳都归于 Matouš。

快速分解

此代码演示了如何使用 Rust 和 **V8 引擎**构建**最小 JavaScript 运行时**:

  • 平台和 V8 初始化:main.rs 文件初始化 V8 引擎和平台,准备执行 JavaScript 代码。
  • 运行时设置:在运行时使用 JavaScript 公开一个全局对象(globalThis.workerHandler),从而使得 Rust 函数可以从 JavaScript 调用。
  • 脚本编译:build_worker 函数编译并评估 JavaScript 代码,结合运行时脚本和定义处理程序函数的 worker_script。
  • 全局上下文绑定:Rust 函数(例如 sayHello)通过将它们绑定到全局对象属性来公开给 V8 运行时。
  • 函数执行:run_worker 函数从 Rust 调用 JavaScript 处理程序函数,传递参数并将结果(Hello World)打印到控制台。
  • // runtime.js
    globalThis.workerHandler = (x) => { 
      return handler(x);
    }
    // main.rs
    use v8;
    
    fn main() {
        // Platform and V8 initialization
        let platform = v8::Platform::new(0, false).make_shared();
        v8::V8::initialize_platform(platform);
        v8::V8::initialize();
        // `include_str!` is a Rust macro that loads a file and converts it into a Rust string
        let runtime = include_str!("runtime.js");
    
        let worker_script = r#"
        export function handler(y) {
            return sayHello(y);
        };
        "#;
        // The runtime.js file exposes the `handler` function as a global object
        let script = format!(
            r#"
            {runtime}
            {worker_script}
            "#
        );
    
        {
            // Create a V8 isolate with default parameters
            let mut isolate = v8::Isolate::new(v8::CreateParams::default());
            let global = setup_runtime(&mut isolate);
            let worker_scope = &mut v8::HandleScope::with_context(isolate.as_mut(), global.clone());
            let handler = build_worker(script.as_str(), worker_scope, &global);
            run_worker(handler, worker_scope, &global);
        }
    
        unsafe {
            v8::V8::dispose();
        }
        v8::V8::dispose_platform();
    }
    // Set up the global runtime context
    fn setup_runtime(isolate: &mut v8::OwnedIsolate) -> v8::Global {
        // Create a handle scope for all isolate handles    
        let isolate_scope = &mut v8::HandleScope::new(isolate);
        // ObjectTemplate is used to create objects inside the isolate
        let globals = v8::ObjectTemplate::new(isolate_scope);
        // The function name to bind to the Rust implementation
        let resource_name = v8::String::new(isolate_scope, "sayHello").unwrap().into();
        // Expose the function to the global object
        globals.set(
            resource_name,
            v8::FunctionTemplate::new(isolate_scope, say_hello_binding).into()
        );
        // Create a context for isolate execution
        let context_options = v8::ContextOptions {
            global_template: Some(globals),
            ..Default::default()
        };
        let global_context = v8::Context::new(isolate_scope, context_options);
        // Create and return the global context
        v8::Global::new(isolate_scope, global_context)
    }
    // Define the Rust binding for the sayHello function
    pub fn say_hello_binding(
        scope: &mut v8::HandleScope,
        args: v8::FunctionCallbackArguments,
        mut retval: v8::ReturnValue,
    ) {
        let to = args.get(0).to_rust_string_lossy(scope);
        let hello = v8::String::new(scope, format!("Hello {}", to).as_str())
        .unwrap().into();
        retval.set(hello);
    }
    // Build the worker by compiling and instantiating the script
    fn build_worker(
        script: &str,
        worker_scope: &mut v8::HandleScope,
        global: &v8::Global,
    ) -> v8::Global {
        let code = v8::String::new(worker_scope, script).unwrap();
        let resource_name = v8::String::new(worker_scope, "script.js").unwrap().into();
        // The source map is optional and used for debugging purposes
        let source_map_url: Option> = Some(v8::String::new(worker_scope, "placeholder").unwrap().into());
        let mut source = v8::script_compiler::Source::new(
            code,
            Some(&v8::ScriptOrigin::new(
                worker_scope,
                resource_name,
                0,
                0,
                false,
                i32::from(0),
                source_map_url,
                false,
                false,
                true,
                None
            )),
        );
        // Compile and evaluate the module
        let module = v8::script_compiler::compile_module(worker_scope, &mut source).unwrap();
        let _ = module.instantiate_module(worker_scope, |_, _, _, _| None);
        let _ = module.evaluate(worker_scope);
        // open a global scope associated to the worker_scope
        let global = global.open(worker_scope);
        // create and assign the handler to the global context
        let global = global.global(worker_scope);
        let handler_key = v8::String::new(worker_scope, "workerHandler").unwrap();
        let js_handler = global.get(worker_scope, handler_key.into()).unwrap();    
        let local_handler = v8::Local::::try_from(js_handler).unwrap();
        v8::Global::new(worker_scope, local_handler)
    }
    
    // Run the worker and execute the `handler` function
    pub fn run_worker(
        worker: v8::Global,
        scope: &mut v8::HandleScope,
        global: &v8::Global,
    ) {
        let handler = worker.open(scope);
        let global = global.open(scope);
        let global = global.global(scope);
    
        let param = v8::String::new(scope, "World").unwrap().into();
        // call the handler and get the result
        match handler.call(scope, global.into(), &[param]) {
            Some(response) => {
                let result = v8::Local::::try_from(response)
                    .expect("Handler did not return a string");
                let result = result.to_string(scope).unwrap();
                println!("{}", result.to_rust_string_lossy(scope));
            }
            None => todo!(),
        };
    }

    我强烈建议您阅读 Matouš Dzivjak 的精彩文章,以深入了解代码及其概念。这只是一个总结——他的详细解释对于理解技术细节至关重要!

    JavaScript 请愿书!