quickjs_runtime

quickjs_runtime is a library for quickly getting started with embedding a javascript engine in your rust project.

DISCLAIMER: This project is not yet what I would call "Battle Tested", use at your own risk.

An example on how to embed a script engine in rust using this lib can be found here: https://github.com/andrieshiemstra/ScriptExtensionLayerExample

quickjsruntime focuses purely on making quickjs easy to use and does not add any additional features, that's where these projects come in: * TypeScript support can be added by using typescriptutils * A more feature-rich (e.g. fetch api support, http based module loader and much more) runtime: GreenCopperRuntime. * The commandline client: GreenCopperCmd.

This project is inspired by the quickjs wrapper at theduke/quickjs-rs and still uses its low level bindings libquickjs-sys.

The big difference to quickjs-rs is that quickjs_runtime executes all quickjs related code in a dedicated single-threaded EventLoop.

Please see the DOCS for all inner workings

This lib serves two main goals:

1. Provide simple utils for working with quickjs (these are located in the quickjs_utils mod)

2. Wrap quickjs for use as a ready to go JavaScript Runtime

What works?

Script and Modules

Rust-Script interoperability

Future / Todo

goals

Same goals as https://github.com/HiRoFa/es_runtime but with using quickjs

so * slower js

but

For some of my projects those are a big plus!

examples

Here are some quickstarts:

Cargo.toml

toml [dependencies] hirofa_utils = "0.4" quickjs_runtime = "0.7" log = "0.4" simple-logging = "2.0"

```rust use crate::builder::QuickJsRuntimeBuilder; use crate::facades::QuickJsRuntimeFacade; use crate::quickjsrealmadapter::QuickJsRealmAdapter; use futures::executor::blockon; use hirofautils::jsutils::adapters::proxies::JsProxy; use hirofautils::jsutils::adapters::JsRealmAdapter; use hirofautils::jsutils::facades::values::{JsValueConvertable, JsValueFacade}; use hirofautils::jsutils::facades::{JsRuntimeBuilder, JsRuntimeFacade}; use hirofautils::js_utils::{JsError, Script}; use log::LevelFilter; use std::time::Duration;

#[test]
fn test_examples() {
    let rt = QuickJsRuntimeBuilder::new().js_build();
    let outcome = block_on(run_examples(&rt));
    if outcome.is_err() {
        log::error!("an error occured: {}", outcome.err().unwrap());
    }
    log::info!("done");
}

async fn take_long() -> i32 {
    std::thread::sleep(Duration::from_millis(500));
    537
}

async fn run_examples(rt: &QuickJsRuntimeFacade) -> Result<(), JsError> {
    // ensure console.log calls get outputted
    simple_logging::log_to_stderr(LevelFilter::Info);

    // do a simple eval on the main realm
    let eval_res = rt
        .js_eval(None, Script::new("simple_eval.js", "2*7;"))
        .await?;
    log::info!("simple eval:{}", eval_res.get_i32());

    // invoke a JS method from rust

    let meth_res = rt
        .js_function_invoke(None, &["Math"], "round", vec![12.321.to_js_value_facade()])
        .await?;
    log::info!("Math.round(12.321) = {}", meth_res.get_i32());

    // add a rust function to js as a callback

    let cb = JsValueFacade::new_callback(|args| {
        let a = args[0].get_i32();
        let b = args[1].get_i32();
        log::info!("rust cb was called with a:{} and b:{}", a, b);
        Ok(JsValueFacade::Null)
    });
    rt.js_function_invoke(
        None,
        &[],
        "setTimeout",
        vec![
            cb,
            10.to_js_value_facade(),
            12.to_js_value_facade(),
            13.to_js_value_facade(),
        ],
    )
    .await?;
    std::thread::sleep(Duration::from_millis(20));
    log::info!("rust cb should have been called by now");

    // create simple proxy class with an async function
    rt.js_loop_realm_sync(None, |_rt_adapter, realm_adapter| {
        let proxy = JsProxy::new(&["com", "mystuff"], "MyProxy").add_static_method(
            "doSomething",
            |_rt_adapter, realm_adapter: &QuickJsRealmAdapter, _args| {
                realm_adapter.js_promise_create_resolving_async(
                    async { Ok(take_long().await) },
                    |realm_adapter, producer_result| {
                        realm_adapter.js_i32_create(producer_result)
                    },
                )
            },
        );
        realm_adapter
            .js_proxy_install(proxy, true)
            .ok()
            .expect("could not install proxy");
    });

    rt.js_eval(
        None,
        Script::new(
            "testMyProxy.js",
            "async function a() {\
                        console.log('a called at %s ms', new Date().getTime());\
                        let res = await com.mystuff.MyProxy.doSomething();\
                        console.log('a got result %s at %s ms', res, new Date().getTime());\
                       }; a();",
        ),
    )
    .await?;
    std::thread::sleep(Duration::from_millis(600));
    log::info!("a should have been called by now");

    Ok(())
}

```