javascriptrustv8denoembedded-v8

How to export JavaScript module members to Rust and call them using v8 or deno_core?


Very much simplified, I would like to write a javascript module which exports members like export function sum(a, b) {return a + b} and then use v8 or deno_core in Rust to compile the module and call the sum method when needed. Could someone tell me how to handle this when using modules instead of traditional scripts?

I already made this work when not using modules but scripts that return a namespace with methods. This way, however, I cannot import/require which I need to keep. Also it is very straight forward to call Rust from Javascript but I want to do it the other way around: Call Javascript functions from Rust. Getting Isolates and the JsRuntime to work is not the issue.

EDIT This is what I have tried last:

fn main() -> Result<(), Error> {
    let mut js_runtime = JsRuntime::new(RuntimeOptions {
        module_loader: Some(Rc::new(FsModuleLoader)),
        ..Default::default()
    });

    let runtime = tokio::runtime::Builder::new_current_thread()
        .enable_all()
        .build()?;

    let main_module = deno_core::resolve_path(
        "./module.js",
        &std::env::current_dir().context("Unable to get CWD")?,
    )?;

    let future = async move {
        let mod_id = js_runtime.load_main_module(&main_module, None).await?;
        let result = js_runtime.mod_evaluate(mod_id);
        js_runtime.run_event_loop(false).await?;

        let context = js_runtime.global_context();
        let scope = &mut js_runtime.handle_scope();
        let global = context.open(scope).global(scope);

        let func_key = v8::String::new(scope, "sum").unwrap();
        let func = global.get(scope, func_key.into()).unwrap();

        if func.is_function() {
            println!("func is a function");
        } else {
            println!("func is not a function but {:?}", func.type_of(scope));
        }

        result.await?
    };
    runtime.block_on(future)
}

Resulting in func is not a function but Local(0x141017a60, PhantomData)

I appreciate any help!


Solution

  • I managed to make it work using deno_core with the JsRuntime::get_module_namespace

    module.js:

    export function sum(a, b) {
        return a + b;
    }
    

    main.rs:

    fn main() -> Result<(), Error> {
        let mut js_runtime = JsRuntime::new(RuntimeOptions {
            module_loader: Some(Rc::new(FsModuleLoader)),
            ..Default::default()
        });
    
        let runtime = tokio::runtime::Builder::new_current_thread()
            .enable_all()
            .build()?;
    
        let main_module = deno_core::resolve_path(
            "./module.js",
            &std::env::current_dir().context("Unable to get CWD")?,
        )?;
    
        let future = async move {
            let mod_id = js_runtime.load_main_module(&main_module, None).await?;
            let result = js_runtime.mod_evaluate(mod_id);
            js_runtime.run_event_loop(false).await?;
    
            let global = js_runtime.get_module_namespace(mod_id).unwrap();
            let scope = &mut js_runtime.handle_scope();
    
            let func_key = v8::String::new(scope, "sum").unwrap();
            let func = global.get(scope, func_key.into()).unwrap();
            let func = v8::Local::<v8::Function>::try_from(func).unwrap();
    
            let a = v8::Integer::new(scope, 5).into();
            let b = v8::Integer::new(scope, 2).into();
            let func_res = func.call(scope, global.into(), &[a, b]).unwrap();
            let func_res = func_res
                .to_string(scope)
                .unwrap()
                .to_rust_string_lossy(scope);
            println!("Function returned: {}", func_res);
    
            result.await?
        };
        runtime.block_on(future)
    }
    

    Printing Function returned: 7

    The same way I can also export classes (v8::Object) and access its methods. This makes total sense and i am 100% sure I tried it like this before, but apparently I did not :)