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!
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 :)