Skip to content

Commit

Permalink
Merge pull request #293 from DelSkayn/update-rquickjs
Browse files Browse the repository at this point in the history
Update rquickjs to the newest version of QuickJS
  • Loading branch information
DelSkayn authored Apr 25, 2024
2 parents 0f8781e + 5fdac3e commit 377f9ed
Show file tree
Hide file tree
Showing 58 changed files with 3,618 additions and 4,219 deletions.
12 changes: 3 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -164,13 +164,13 @@ jobs:
os: ubuntu-latest
rust: stable
target: i686-unknown-linux-gnu
features: exports
features: full-async
optimization: false
- task: bindings
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
features: exports
features: full-async
optimization: false
- task: bindings
os: macos-latest
Expand Down Expand Up @@ -233,12 +233,6 @@ jobs:
features: full-async
optimization: false
# Test features
- task: features
os: ubuntu-latest
rust: stable
target: x86_64-unknown-linux-gnu
features: exports
optimization: false
- task: features
os: ubuntu-latest
rust: stable
Expand Down Expand Up @@ -401,7 +395,7 @@ jobs:
RUST_LOG: bindgen=warn,bindgen::ir=error,bindgen::codegen=error
run: |
cargo clean
cargo build ${{ matrix.optimization && '--release' || '' }} --manifest-path sys/Cargo.toml --target ${{ matrix.target }} --features exports,bindgen,update-bindings,logging
cargo build ${{ matrix.optimization && '--release' || '' }} --manifest-path sys/Cargo.toml --target ${{ matrix.target }} --features bindgen,update-bindings,logging
- name: Upload bindings
if: matrix.task == 'bindings'
uses: actions/upload-artifact@v3
Expand Down
7 changes: 2 additions & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ members = [
]

[features]
default = ["exports", "classes", "properties"]
default = ["classes", "properties"]

# Almost all features excluding "parallel" and support for async runtimes
full = ["chrono", "exports", "loader", "allocator", "dyn-load", "either", "indexmap", "classes", "properties", "array-buffer", "macro", "phf"]
full = ["chrono", "loader", "allocator", "dyn-load", "either", "indexmap", "classes", "properties", "array-buffer", "macro", "phf"]

# Almost all features excluding "parallel"
full-async = ["full", "futures"]
Expand All @@ -68,9 +68,6 @@ bindgen = ["rquickjs-core/bindgen", "rquickjs-macro?/bindgen"]
# Enable support of parallel execution
parallel = ["rquickjs-core/parallel"]

# Enable support of reading module exports
exports = ["rquickjs-core/exports"]

# Enable user-defined module loader support
loader = ["rquickjs-core/loader"]

Expand Down
5 changes: 1 addition & 4 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ optional = true
default = []

# Almost all features excluding "parallel" and support for async runtimes
full = ["chrono", "exports", "loader", "allocator", "dyn-load", "either", "indexmap", "classes", "properties", "array-buffer"]
full = ["chrono", "loader", "allocator", "dyn-load", "either", "indexmap", "classes", "properties", "array-buffer"]

# Almost all features excluding "parallel"
full-async = ["full", "futures"]
Expand All @@ -59,9 +59,6 @@ bindgen = ["rquickjs-sys/bindgen"]
# Enable support of parallel execution
parallel = []

# Enable support of reading module exports
exports = ["rquickjs-sys/exports"]

# Enable user-defined module loader support
loader = ["relative-path"]

Expand Down
7 changes: 5 additions & 2 deletions core/src/class/trace.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use std::marker::PhantomData;
#[cfg(feature = "either")]
use either::{Either, Left, Right};

use crate::{markers::Invariant, qjs, Class, Ctx, Value};
use crate::{markers::Invariant, qjs, Class, Ctx, Module, Value};

use super::JsClass;

Expand Down Expand Up @@ -80,6 +80,10 @@ where
}
}

impl<'js, T> Trace<'js> for Module<'js, T> {
fn trace<'a>(&self, _tracer: Tracer<'a, 'js>) {}
}

impl<'js, T> Trace<'js> for Option<T>
where
T: Trace<'js>,
Expand Down Expand Up @@ -200,7 +204,6 @@ trace_impls! {
bool,char,
String,
crate::Atom<'js>,
crate::Module<'js>,
}

trace_impls! {
Expand Down
17 changes: 10 additions & 7 deletions core/src/context/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@ impl Context {
let ctx = NonNull::new(unsafe { qjs::JS_NewContextRaw(guard.rt.as_ptr()) })
.ok_or_else(|| Error::Allocation)?;
unsafe { I::add_intrinsic(ctx) };
// rquickjs assumes the base objects exist, so we allways need to add this.
unsafe { intrinsic::Base::add_intrinsic(ctx) };
unsafe { Self::init_raw(ctx.as_ptr()) }
let res = Inner {
ctx,
Expand Down Expand Up @@ -194,20 +196,21 @@ mod test {
});
}

#[cfg(feature = "exports")]
#[test]
fn module() {
test_with(|ctx| {
let _value: Module = ctx
.compile(
"test_mod",
r#"
Module::evaluate(
ctx,
"test_mod",
r#"
let t = "3";
let b = (a) => a + 3;
export { b, t}
"#,
)
.unwrap();
)
.unwrap()
.finish::<()>()
.unwrap();
});
}

Expand Down
114 changes: 82 additions & 32 deletions core/src/context/ctx.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use std::{
ffi::{CStr, CString},
fs, mem,
fs,
mem::{self, MaybeUninit},
path::Path,
ptr::NonNull,
};
Expand All @@ -11,18 +12,21 @@ use std::future::Future;
#[cfg(feature = "futures")]
use crate::AsyncContext;
use crate::{
markers::Invariant, qjs, runtime::raw::Opaque, Context, Error, FromJs, Function, IntoJs,
Module, Object, Result, String, Value,
atom::PredefinedAtom, markers::Invariant, qjs, runtime::raw::Opaque, Atom, Context, Error,
FromJs, Function, IntoJs, Object, Promise, Result, String, Value,
};

/// Eval options.
#[non_exhaustive]
pub struct EvalOptions {
/// Global code.
pub global: bool,
/// Force 'strict' mode.
pub strict: bool,
/// Don't include the stack frames before this eval in the Error() backtraces.
pub backtrace_barrier: bool,
/// Support top-level-await.
pub promise: bool,
}

impl EvalOptions {
Expand All @@ -41,6 +45,10 @@ impl EvalOptions {
flag |= qjs::JS_EVAL_FLAG_BACKTRACE_BARRIER;
}

if self.promise {
flag |= qjs::JS_EVAL_FLAG_ASYNC;
}

flag as i32
}
}
Expand All @@ -51,6 +59,7 @@ impl Default for EvalOptions {
global: true,
strict: true,
backtrace_barrier: false,
promise: false,
}
}
}
Expand Down Expand Up @@ -135,6 +144,20 @@ impl<'js> Ctx<'js> {
self.eval_with_options(source, Default::default())
}

/// Evaluate a script in global context with top level await support.
///
/// This function always returns a promise which resolves to the result of the evaluated
/// expression.
pub fn eval_promise<S: Into<Vec<u8>>>(&self, source: S) -> Result<Promise<'js>> {
self.eval_with_options(
source,
EvalOptions {
promise: true,
..Default::default()
},
)
}

/// Evaluate a script with the given options.
pub fn eval_with_options<V: FromJs<'js>, S: Into<Vec<u8>>>(
&self,
Expand Down Expand Up @@ -174,15 +197,6 @@ impl<'js> Ctx<'js> {
})
}

/// Compile a module for later use.
pub fn compile<N, S>(self, name: N, source: S) -> Result<Module<'js>>
where
N: Into<Vec<u8>>,
S: Into<Vec<u8>>,
{
Module::evaluate(self, name, source)
}

/// Returns the global object of this context.
pub fn globals(&self) -> Object<'js> {
unsafe {
Expand Down Expand Up @@ -344,24 +358,35 @@ impl<'js> Ctx<'js> {
}
}

// Creates promise and resolving functions.
pub fn promise(&self) -> Result<(Object<'js>, Function<'js>, Function<'js>)> {
/// Creates javascipt promise along with its reject and resolve functions.
pub fn promise(&self) -> Result<(Promise<'js>, Function<'js>, Function<'js>)> {
let mut funcs = mem::MaybeUninit::<(qjs::JSValue, qjs::JSValue)>::uninit();

Ok(unsafe {
let promise = self.handle_exception(qjs::JS_NewPromiseCapability(
self.ctx.as_ptr(),
funcs.as_mut_ptr() as _,
))?;
let (then, catch) = funcs.assume_init();
let (resolve, reject) = funcs.assume_init();
(
Object::from_js_value(self.clone(), promise),
Function::from_js_value(self.clone(), then),
Function::from_js_value(self.clone(), catch),
Promise::from_js_value(self.clone(), promise),
Function::from_js_value(self.clone(), resolve),
Function::from_js_value(self.clone(), reject),
)
})
}

/// Executes a quickjs job.
///
/// Returns wether a job was actually executed.
/// If this function returned false, no job was pending.
pub fn execute_pending_job(&self) -> bool {
let mut ptr = MaybeUninit::<*mut qjs::JSContext>::uninit();
let rt = unsafe { qjs::JS_GetRuntime(self.ctx.as_ptr()) };
let res = unsafe { qjs::JS_ExecutePendingJob(rt, ptr.as_mut_ptr()) };
res != 0
}

pub(crate) unsafe fn get_opaque(&self) -> *mut Opaque<'js> {
let rt = qjs::JS_GetRuntime(self.ctx.as_ptr());
qjs::JS_GetRuntimeOpaque(rt).cast::<Opaque>()
Expand Down Expand Up @@ -402,41 +427,54 @@ impl<'js> Ctx<'js> {
}
}

pub fn script_or_module_name(&self, stack_level: isize) -> Option<Atom<'js>> {
let stack_level = std::os::raw::c_int::try_from(stack_level).unwrap();
let atom = unsafe { qjs::JS_GetScriptOrModuleName(self.as_ptr(), stack_level) };
if PredefinedAtom::Null as u32 == atom {
unsafe { qjs::JS_FreeAtom(self.as_ptr(), atom) };
return None;
}
unsafe { Some(Atom::from_atom_val(self.clone(), atom)) }
}

/// Returns the pointer to the C library context.
pub fn as_raw(&self) -> NonNull<qjs::JSContext> {
self.ctx
}

/// Frees modules which aren't evaluated.
///
/// When a module is compiled and the compilation results in an error the module can already
/// have resolved several modules. Originally QuickJS freed all these module when compiling
/// (but not when a it was dynamically imported), this library patched that behavior out
/// because it proved to be hard to make safe. This function will free those modules.
///
/// # Safety
/// Caller must ensure that this method is not called from a module being evaluated.
/*
// Frees modules which aren't evaluated.
//
// When a module is compiled and the compilation results in an error the module can already
// have resolved several modules. Originally QuickJS freed all these module when compiling
// (but not when a it was dynamically imported), this library patched that behavior out
// because it proved to be hard to make safe. This function will free those modules.
//
// # Safety
// Caller must ensure that this method is not called from a module being evaluated.
pub unsafe fn free_unevaluated_modules(&self) {
qjs::JS_FreeUnevaluatedModules(self.ctx.as_ptr())
}
*/
}

#[cfg(test)]
mod test {

#[cfg(feature = "exports")]
#[test]
fn exports() {
use crate::{context::intrinsic, Context, Function, Runtime};
use crate::{context::intrinsic, Context, Function, Module, Promise, Runtime};

let runtime = Runtime::new().unwrap();
let ctx = Context::custom::<(intrinsic::Promise, intrinsic::Eval)>(&runtime).unwrap();
ctx.with(|ctx| {
let module = ctx
.compile("test", "export default async () => 1;")
let (module, promise) = Module::declare(ctx, "test", "export default async () => 1;")
.unwrap()
.eval()
.unwrap();
promise.finish::<()>().unwrap();
let func: Function = module.get("default").unwrap();
func.call::<(), ()>(()).unwrap();
func.call::<(), Promise>(()).unwrap();
});
}

Expand Down Expand Up @@ -464,6 +502,18 @@ mod test {
})
}

#[test]
fn eval_minimal_test() {
use crate::{Context, Runtime};

let runtime = Runtime::new().unwrap();
let ctx = Context::full(&runtime).unwrap();
ctx.with(|ctx| {
let res: i32 = ctx.eval(" 1 + 1 ").unwrap();
assert_eq!(2, res);
})
}

#[test]
#[should_panic(expected = "'foo' is not defined")]
fn eval_with_sloppy_code() {
Expand Down
10 changes: 3 additions & 7 deletions core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ mod persistent;
mod value;
pub use persistent::{Outlive, Persistent};
pub use value::{
array, atom, convert, function, module, object, Array, Atom, BigInt, Coerced, Exception,
Filter, FromAtom, FromIteratorJs, FromJs, Function, IntoAtom, IntoJs, IteratorJs, Module, Null,
Object, String, Symbol, Type, Undefined, Value,
array, atom, convert, function, module, object, promise, Array, Atom, BigInt, Coerced,
Exception, Filter, FromAtom, FromIteratorJs, FromJs, Function, IntoAtom, IntoJs, IteratorJs,
Module, Null, Object, Promise, String, Symbol, Type, Undefined, Value,
};

pub mod class;
Expand All @@ -73,10 +73,6 @@ pub use value::{ArrayBuffer, TypedArray};

pub(crate) use std::{result::Result as StdResult, string::String as StdString};

#[cfg(feature = "futures")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "futures")))]
pub mod promise;

#[cfg(feature = "allocator")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(feature = "allocator")))]
pub mod allocator;
Expand Down
Loading

0 comments on commit 377f9ed

Please sign in to comment.