Skip to content

Commit d70553e

Browse files
wip
1 parent b0aa0ce commit d70553e

17 files changed

+136
-32
lines changed

.github/workflows/rust.yml

+13
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,19 @@ jobs:
6868
- name: Run tests
6969
run: cargo test --verbose
7070

71+
build-ubuntu-quickjs-ng:
72+
runs-on: ubuntu-latest
73+
steps:
74+
- uses: actions/checkout@v2
75+
- name: Prepare
76+
run: |
77+
sudo apt update
78+
sudo apt install llvm autoconf2.13 automake clang -y
79+
- name: Run tests
80+
run: cargo test --verbose --no-default-features --features quickjs-ng console setimmediate setinterval settimeout typescript
81+
with:
82+
token: ${{ secrets.GITHUB_TOKEN }}
83+
7184
build:
7285
runs-on: ubuntu-latest
7386
steps:

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# 0.14.5
2+
3+
* quickjs-ng 0.8.0
4+
15
# 0.14.4
26

37
* don't panic on utf8 error in to_str

Cargo.toml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "quickjs_runtime"
3-
version = "0.14.4"
3+
version = "0.14.5"
44
authors = ["Andries Hiemstra <info@hirofa.com>"]
55
edition = "2021"
66
description = "Wrapper API and utils for the QuickJS JavaScript engine with support for Promise, Modules, Async/await"
@@ -31,7 +31,7 @@ backtrace = "0.3.67"
3131

3232
#libquickjs-sys = {package="hirofa-quickjs-sys", git='https://github.com/HiRoFa/quickjs-sys'}
3333
#libquickjs-sys = {package="hirofa-quickjs-sys", path='../quickjs-sys', default-features=false}
34-
libquickjs-sys = {package="hirofa-quickjs-sys", version="0.6.0", default-features=false}
34+
libquickjs-sys = {package="hirofa-quickjs-sys", version="0.8.0", default-features=false}
3535
lazy_static = "1.4.0"
3636
log = "0.4"
3737
num_cpus = "1"

README.md

+12-12
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
# quickjs_runtime
22

3-
quickjs_runtime is a library for quickly getting started with embedding a javascript engine in your rust project.
3+
Quickjs_runtime is a library for quickly getting started with embedding a javascript engine in your rust project.
44

5-
**as of 2024 this lib no longer relies on [libquickjs-sys](https://github.com/theduke/quickjs-rs/tree/master/libquickjs-sys) but on our own [hirofa-quickjs-sys](https://github.com/HiRoFa/quickjs-sys) adding flexibility in used quickjs version**
5+
Relies on [hirofa-quickjs-sys](https://github.com/HiRoFa/quickjs-sys) to support quickjs-ng as well as the original quickjs
66

7-
quickjs_runtime runs all javascript action in a single thread using an EventLoop. This means you can call javascript safely from several threads by adding tasks to the EventLoop.
7+
Quickjs_runtime runs all javascript action in a single thread using an EventLoop. This means you can call javascript safely from several threads by adding tasks to the EventLoop.
88

99
# quickjs or quickjs-ng
1010

11-
quickjs_runtime supports both the original quickjs and the quickjs-ng project.
11+
Quickjs_runtime supports both the original quickjs and the quickjs-ng project.
1212

1313
You can try out quickjs-ng by adding the dep to quickjs_runtime like this (use at your own risk as I have not extensively tested it yet):
1414
```toml
@@ -19,7 +19,7 @@ quickjs_runtime = {git="https://github.com/HiRoFa/quickjs_es_runtime", features=
1919

2020
An example on how to embed a script engine in rust using this lib can be found here: [github.com/andrieshiemstra/ScriptExtensionLayerExample](https://github.com/andrieshiemstra/ScriptExtensionLayerExample). It was published in TWIR as a walkthrough.
2121

22-
quickjs_runtime focuses on making [quickjs](https://bellard.org/quickjs/) easy to use and does not add any additional features, that's where these projects come in:
22+
Wuickjs_runtime focuses on making [quickjs](https://bellard.org/quickjs/) easy to use and does not add any additional features, that's where these projects come in:
2323
* A more feature-rich (e.g. fetch api support, http based module loader and much more) runtime: [GreenCopperRuntime](https://github.com/HiRoFa/GreenCopperRuntime).
2424
* The commandline client: [GreenCopperCmd](https://github.com/HiRoFa/GreenCopperCmd).
2525

@@ -37,23 +37,23 @@ Please see the [DOCS](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtim
3737
* Start at the QuickjsRuntimeFacade, it provides an EventQueue which has a thread_local QuickJsRuntimeAdapter
3838
* All values are copied or abstracted in a JsValueFacades
3939
* So no need to worry about Garbage collection
40-
* evaluate script and invoke functions while waiting for results blocking or with async/await
40+
* Evaluate script and invoke functions while waiting for results blocking or with async/await
4141
* Get Promise result blocking or with async/await
4242

4343
# What works?
4444

4545
## Script and Modules
4646

4747
* Typescript (via SWC)
48-
* console (.log/info/debug/trace/error) ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/features/console/index.html))
48+
* Console (.log/info/debug/trace/error) ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/features/console/index.html))
4949
* Eval script ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/facades/struct.QuickJsRuntimeFacade.html#method.eval))
5050
* Create promises in JavaScript which execute async
5151
* Eval modules ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/facades/struct.QuickJsRuntimeFacade.html#method.eval_module))
5252
* Load modules (dynamic and static) ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/builder/struct.QuickJsRuntimeBuilder.html#method.script_module_loader))
53-
* fetch api (impl in [GreenCopperRuntime](https://github.com/HiRoFa/GreenCopperRuntime))
53+
* Fetch api (impl in [GreenCopperRuntime](https://github.com/HiRoFa/GreenCopperRuntime))
5454
* setImmediate
5555
* setTimeout/Interval (and clear)
56-
* script preprocessing (impls for ifdef/macro's/typescript can be found in [GreenCopperRuntime](https://github.com/HiRoFa/GreenCopperRuntime))
56+
* Script preprocessing (impls for ifdef/macro's/typescript can be found in [GreenCopperRuntime](https://github.com/HiRoFa/GreenCopperRuntime))
5757

5858
## Rust-Script interoperability
5959

@@ -62,8 +62,8 @@ Please see the [DOCS](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtim
6262
* Invoke JS functions from rust ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/facades/struct.QuickJsRuntimeFacade.html#method.invoke_function))
6363
* Pass primitives, objects and arrays from and to rust ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/quickjs_utils/primitives/index.html))
6464
* Create Classes from rust ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/reflection/struct.Proxy.html))
65-
* async/await support on eval/call_function/promise resolution ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/values/struct.CachedJsPromiseRef.html#method.get_promise_result))
66-
* import native Modules (e.g. dynamic loading of rust functions or Proxy classes) ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/builder/struct.QuickJsRuntimeBuilder.html#method.native_module_loader))
65+
* Async/await support on eval/call_function/promise resolution ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/values/struct.CachedJsPromiseRef.html#method.get_promise_result))
66+
* Import native Modules (e.g. dynamic loading of rust functions or Proxy classes) ([docs](https://hirofa.github.io/quickjs_es_runtime/quickjs_runtime/builder/struct.QuickJsRuntimeBuilder.html#method.native_module_loader))
6767

6868
# Goals
6969

@@ -87,7 +87,7 @@ The fun stuff about QuickJS:
8787
Cargo.toml
8888
```toml
8989
[dependencies]
90-
quickjs_runtime = "0.13.1"
90+
quickjs_runtime = "0.14"
9191
```
9292

9393
Here are some quickstarts:

src/quickjs_utils/arrays.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -220,17 +220,26 @@ pub mod tests {
220220
rt.exe_rt_task_in_event_loop(|q_js_rt| {
221221
let q_ctx = q_js_rt.get_main_realm();
222222
let arr = create_array_q(q_ctx).ok().unwrap();
223+
224+
#[cfg(feature = "bellard")]
223225
assert_eq!(arr.get_ref_count(), 1);
224226

225227
let a = objects::create_object_q(q_ctx).ok().unwrap();
228+
229+
#[cfg(feature = "bellard")]
226230
assert_eq!(1, a.get_ref_count());
227231

228232
set_element_q(q_ctx, &arr, 0, &a).ok().unwrap();
233+
234+
#[cfg(feature = "bellard")]
229235
assert_eq!(2, a.get_ref_count());
230236

231-
let a2 = get_element_q(q_ctx, &arr, 0).ok().unwrap();
237+
let _a2 = get_element_q(q_ctx, &arr, 0).ok().unwrap();
238+
239+
#[cfg(feature = "bellard")]
232240
assert_eq!(3, a.get_ref_count());
233-
assert_eq!(3, a2.get_ref_count());
241+
#[cfg(feature = "bellard")]
242+
assert_eq!(3, _a2.get_ref_count());
234243
});
235244
}
236245
}

src/quickjs_utils/bigints.rs

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ pub unsafe fn new_bigint_i64(
2828
) -> Result<QuickJsValueAdapter, JsError> {
2929
let res_val = q::JS_NewBigInt64(context, int);
3030
let ret = QuickJsValueAdapter::new(context, res_val, false, true, "new_bigint_i64");
31+
32+
#[cfg(feature = "bellard")]
3133
assert_eq!(ret.get_ref_count(), 1);
3234
Ok(ret)
3335
}
@@ -41,6 +43,8 @@ pub unsafe fn new_bigint_u64(
4143
) -> Result<QuickJsValueAdapter, JsError> {
4244
let res_val = q::JS_NewBigUint64(context, int);
4345
let ret = QuickJsValueAdapter::new(context, res_val, false, true, "new_bigint_u64");
46+
47+
#[cfg(feature = "bellard")]
4448
assert_eq!(ret.get_ref_count(), 1);
4549
Ok(ret)
4650
}
@@ -63,6 +67,8 @@ pub unsafe fn new_bigint_str(
6367
let str_ref = primitives::from_string(context, input_str)?;
6468
let bigint_ref = functions::invoke_member_function(context, &global_ref, "BigInt", &[str_ref])?;
6569
let ret = bigint_ref;
70+
71+
#[cfg(feature = "bellard")]
6672
assert_eq!(ret.get_ref_count(), 1);
6773
Ok(ret)
6874
}

src/quickjs_utils/compile.rs

+6
Original file line numberDiff line numberDiff line change
@@ -295,31 +295,37 @@ pub mod tests {
295295
Script::new("test_func_runfail.es", "let abcdef = 1;"),
296296
);
297297
let func = func_res.expect("func compile failed");
298+
#[cfg(feature = "bellard")]
298299
assert_eq!(1, func.get_ref_count());
299300

300301
let bytecode: Vec<u8> = to_bytecode(q_ctx.context, &func);
301302

303+
#[cfg(feature = "bellard")]
302304
assert_eq!(1, func.get_ref_count());
303305

304306
drop(func);
305307

308+
#[cfg(feature = "bellard")]
306309
assert!(!bytecode.is_empty());
307310

308311
let func2_res = from_bytecode(q_ctx.context, &bytecode);
309312
let func2 = func2_res.expect("could not read bytecode");
310313
//should fail the second time you run this because abcdef is already defined
311314

315+
#[cfg(feature = "bellard")]
312316
assert_eq!(1, func2.get_ref_count());
313317

314318
let run_res1 =
315319
run_compiled_function(q_ctx.context, &func2).expect("run 1 failed unexpectedly");
316320
drop(run_res1);
317321

322+
#[cfg(feature = "bellard")]
318323
assert_eq!(1, func2.get_ref_count());
319324

320325
let _run_res2 = run_compiled_function(q_ctx.context, &func2)
321326
.expect_err("run 2 succeeded unexpectedly");
322327

328+
#[cfg(feature = "bellard")]
323329
assert_eq!(1, func2.get_ref_count());
324330
});
325331
}

src/quickjs_utils/functions.rs

+17-5
Original file line numberDiff line numberDiff line change
@@ -721,32 +721,41 @@ pub mod tests {
721721
"this.test = {q: {}}; let global = this; (function(a, b){global.test.a = a; return {a: 1};});",
722722
))
723723
.expect("aa");
724+
#[cfg(feature = "bellard")]
724725
assert_eq!(func_ref.get_ref_count(), 1);
725726

726727
let a = objects::create_object_q(q_ctx).ok().unwrap();
727728
let b = objects::create_object_q(q_ctx).ok().unwrap();
728729

730+
#[cfg(feature = "bellard")]
729731
assert_eq!(1, a.get_ref_count());
732+
#[cfg(feature = "bellard")]
730733
assert_eq!(1, b.get_ref_count());
731734

732735
let i_res = call_function_q(q_ctx, &func_ref, &[a.clone(), b.clone()], None)
733736
.expect("a");
737+
734738
assert!(i_res.is_object());
739+
#[cfg(feature = "bellard")]
735740
assert_eq!(i_res.get_ref_count(), 1);
736-
741+
#[cfg(feature = "bellard")]
737742
assert_eq!(2, a.get_ref_count());
743+
#[cfg(feature = "bellard")]
738744
assert_eq!(1, b.get_ref_count());
739745

740746
let q_ref = q_ctx.eval(Script::new("test_ret_refcount2.es", "test.q;")).expect("get q failed");
747+
#[cfg(feature = "bellard")]
741748
assert_eq!(2, q_ref.get_ref_count());
742749
let _ = call_function_q(q_ctx, &func_ref, &[primitives::from_i32(123), q_ref], None)
743750
.expect("b");
744751
let q_ref = q_ctx.eval(Script::new("test_ret_refcount2.es", "test.q;")).expect("get q failed");
752+
#[cfg(feature = "bellard")]
745753
assert_eq!(2, q_ref.get_ref_count());
746754
let _ = call_function_q(q_ctx, &func_ref, &[q_ref, primitives::from_i32(123)], None)
747755
.expect("b");
748-
let q_ref = q_ctx.eval(Script::new("test_ret_refcount2.es", "test.q;")).expect("get q failed");
749-
assert_eq!(3, q_ref.get_ref_count());
756+
let _q_ref = q_ctx.eval(Script::new("test_ret_refcount2.es", "test.q;")).expect("get q failed");
757+
#[cfg(feature = "bellard")]
758+
assert_eq!(3, _q_ref.get_ref_count());
750759

751760
// cleanup
752761
q_ctx.eval(Script::new("cleanup.es", "this.test = null;")).ok().unwrap();
@@ -835,6 +844,7 @@ pub mod tests {
835844
.ok()
836845
.expect("could not create function");
837846

847+
#[cfg(feature = "bellard")]
838848
assert_eq!(1, cb_ref.get_ref_count());
839849

840850
cb_ref.label("cb_ref at test_callback");
@@ -844,6 +854,7 @@ pub mod tests {
844854
.ok()
845855
.expect("could not get function");
846856

857+
#[cfg(feature = "bellard")]
847858
assert_eq!(2, func_ref.get_ref_count());
848859

849860
let res = call_function_q(q_ctx, &func_ref, &[cb_ref], None);
@@ -876,9 +887,10 @@ pub mod tests {
876887
let cb_ref = new_function_q(
877888
q_ctx,
878889
"cb",
879-
|_q_ctx, _this_ref, args| {
890+
|_q_ctx, _this_ref, _args| {
880891
log::trace!("native callback invoked");
881-
assert_eq!(args[0].get_ref_count(), 3);
892+
#[cfg(feature = "bellard")]
893+
assert_eq!(_args[0].get_ref_count(), 3);
882894

883895
Ok(primitives::from_i32(983))
884896
},

src/quickjs_utils/json.rs

+2
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,8 @@ pub mod tests {
156156
.ok()
157157
.unwrap();
158158
let str_res = json::stringify_q(q_ctx, &obj, None).ok().unwrap();
159+
160+
#[cfg(feature = "bellard")]
159161
assert_eq!(str_res.get_ref_count(), 1);
160162
let json = str_res.to_string().ok().unwrap();
161163
assert_eq!(json, "{\"a\":532,\"b\":true,\"c\":\"abcdË\"}");

src/quickjs_utils/mod.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -173,10 +173,12 @@ pub mod tests {
173173
rt.exe_rt_task_in_event_loop(|q_js_rt| {
174174
let q_ctx = q_js_rt.get_main_realm();
175175

176+
#[cfg(feature = "bellard")]
176177
let ct = get_global_q(q_ctx).get_ref_count();
177178
for _ in 0..5 {
178-
let global = get_global_q(q_ctx);
179-
assert_eq!(global.get_ref_count(), ct);
179+
let _global = get_global_q(q_ctx);
180+
#[cfg(feature = "bellard")]
181+
assert_eq!(_global.get_ref_count(), ct);
180182
}
181183
});
182184
}

src/quickjs_utils/modules.rs

+30-4
Original file line numberDiff line numberDiff line change
@@ -78,8 +78,33 @@ pub fn set_module_loader(q_js_rt: &QuickJsRuntimeAdapter) {
7878

7979
/// detect if a script is module (contains import or export statements)
8080
pub fn detect_module(source: &str) -> bool {
81-
let cstr = CString::new(source).expect("could not create CString due to null term in source");
82-
unsafe { q::JS_DetectModule(cstr.as_ptr(), source.len() as _) != 0 }
81+
// own impl since detectmodule in quickjs-ng is broken since 0.7.0
82+
// https://github.com/quickjs-ng/quickjs/issues/767
83+
84+
// Check for static `import` statements
85+
86+
#[cfg(feature = "quickjs-ng")]
87+
{
88+
for line in source.lines() {
89+
let trimmed = line.trim();
90+
if trimmed.starts_with("import ") && !trimmed.contains("(") {
91+
return true;
92+
}
93+
if trimmed.starts_with("export ") {
94+
return true;
95+
}
96+
}
97+
false
98+
}
99+
100+
#[cfg(feature = "bellard")]
101+
{
102+
let cstr =
103+
CString::new(source).expect("could not create CString due to null term in source");
104+
let res = unsafe { q::JS_DetectModule(cstr.as_ptr(), source.len() as _) };
105+
//println!("res for {} = {}", source, res);
106+
res != 0
107+
}
83108
}
84109

85110
/// create new Module (JSModuleDef struct) which can be populated with exports after (and from) the init_func
@@ -274,10 +299,11 @@ pub mod tests {
274299

275300
#[test]
276301
fn test_detect() {
277-
assert!(detect_module("import {} from 'foo.es';"));
302+
assert!(detect_module("import {} from 'foo.js';"));
278303
assert!(detect_module("export function a(){};"));
279-
assert!(!detect_module("import('foo.es').then((a) = {});"));
304+
assert!(!detect_module("//hi"));
280305
assert!(!detect_module("let a = 1;"));
306+
assert!(!detect_module("import('foo.js').then((a) = {});"));
281307
}
282308

283309
#[test]

0 commit comments

Comments
 (0)