Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Access the exported methods from a script #396

Closed
Angelmmiguel opened this issue Jun 14, 2023 · 9 comments
Closed

Access the exported methods from a script #396

Angelmmiguel opened this issue Jun 14, 2023 · 9 comments
Labels
enhancement New feature or request

Comments

@Angelmmiguel
Copy link

What is the idea?

Currently, Javy supports ECMAScript modules (ESM) via the compile_module JSContext method. It interprets and run any code that includes keywords from the ESM specification like import / export.

However, we cannot access to the these exported methods and values after it finishes. For example, given the following JavaScript code, I would like to access to the fetch in the exported object:

export default {
  async fetch(request) {
    const body = `Hello world!`;
    return new Response(body);
  },
};

The goal is to expose these methods at the quickjs-wasm-rs level, so we can build custom JavaScript runtimes on top of the javy crate. For now, I'm not thinking on fetching these exports and setting them as "exported functions" in the Wasm module from the javy-cli.

What problem does it solve?

ESM modules are more and more common in the JavaScript ecosystem. For example, different projects from the WinterCG started using ESM modules as the way to define workers / functions in their platforms:

// From
addEventListener("fetch", event => { 
  return event.respondWith(new Response("Hello World")); 
}); 

// To
export default {
  async fetch(_request) {
    return new Response("Hello world");
  },
};

For example:

We want to offer the same option in Wasm Workers Server, that relies on Javy for the JavaScript runtime. We need access to those exported methods so we can call those functions from the Rust side.

Here you can find the related issue (vmware-labs/wasm-workers-server/issues/149).

@Angelmmiguel Angelmmiguel added the enhancement New feature or request label Jun 14, 2023
@jeffcharles
Copy link
Collaborator

I am also actively investigating how to do this for one of our use cases too 😄

@Angelmmiguel
Copy link
Author

I am also actively investigating how to do this for one of our use cases too 😄

That's great @jeffcharles! If there's any branch / fork related to this effort, I would be really happy to check it and try to contribute. Any guidance on the required changes here would be helpful to start playing with it 😄

@gc-victor
Copy link

I use a simple replacement. It is not a fancy solution, but it does its work.

https://github.com/gc-victor/js-wasm-workers-runtime/blob/main/crates/runtime/src/lib.rs#L31

@Angelmmiguel
Copy link
Author

I use a simple replacement. It is not a fancy solution, but it does its work.

https://github.com/gc-victor/js-wasm-workers-runtime/blob/main/crates/runtime/src/lib.rs#L31

Yes, I'm using a similar replacement for some tests now hehe.

@jeffcharles
Copy link
Collaborator

I've got an approach that seems to work though it's a bit messy in https://github.com/bytecodealliance/javy/compare/jc.exported-func-invoke-example?expand=1.

I couldn't find a C API that enables us to retrieve the function directly unfortunately. There are C APIs for working with the exports list of a module (for example, find_export_entry) but they're internal to QuickJS. But you can import the module and its functions by evaluating some JavaScript with the appropriate eval type flag set. There is a ~10% performance decrease compared to calling a function assigned to globalThis in Rust.

@Angelmmiguel
Copy link
Author

I've got an approach that seems to work though it's a bit messy in https://github.com/bytecodealliance/javy/compare/jc.exported-func-invoke-example?expand=1.

I couldn't find a C API that enables us to retrieve the function directly unfortunately. There are C APIs for working with the exports list of a module (for example, find_export_entry) but they're internal to QuickJS. But you can import the module and its functions by evaluating some JavaScript with the appropriate eval type flag set. There is a ~10% performance decrease compared to calling a function assigned to globalThis in Rust.

Thank you @jeffcharles! I think this may work. In my case, I'm using the global object to get the result of the script. I will need to define custom methods to extract the information, so I don't rely on a specific field in the globalThis object.

@AndreLion
Copy link

So, before the exported methods is officially supported, how can I execute the wasm built by Javy with browser API WebAssembly.instantiate(), in other words, how can I send stdin into wasm in browser?

Thanks and btw really great work @jeffcharles

@jeffcharles
Copy link
Collaborator

how can I execute the wasm built by Javy with browser API WebAssembly.instantiate(), in other words, how can I send stdin into wasm in browser?

You'll need to use a browser implementation of WASI. Using https://github.com/bjorn3/browser_wasi_shim may work. I don't have a browser example unfortunately.

@jeffcharles
Copy link
Collaborator

A while back, I merged #412 which uses

pub fn invoke_function(runtime: &Runtime, fn_module: &str, fn_name: &str) {
let context = runtime.context();
let js = if fn_name == "default" {
format!("import {{ default as defaultFn }} from '{fn_module}'; defaultFn();")
} else {
format!("import {{ {fn_name} }} from '{fn_module}'; {fn_name}();")
};
context
.eval_module("runtime.mjs", &js)
.and_then(|_| process_event_loop(context))
.unwrap_or_else(handle_error);
}
to invoke exported functions. Feel free to re-use that logic in your implementations!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

4 participants