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.
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
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!
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(())
}
```