Skip to content

Allow rquickjs-core to build with no_std #455

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

Merged
merged 10 commits into from
Jun 26, 2025
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 10 additions & 7 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -23,10 +23,10 @@ members = [
]

[workspace.dependencies]
rquickjs-core = { version = "0.9.0", path = "core" }
rquickjs-macro = { version = "0.9.0", path = "macro" }
rquickjs-sys = { version = "0.9.0", path = "sys" }
rquickjs = { version = "0.9.0", path = "./" }
rquickjs-core = { version = "0.9.0", path = "core", default-features = false }
rquickjs-macro = { version = "0.9.0", path = "macro", default-features = false }
rquickjs-sys = { version = "0.9.0", path = "sys", default-features = false }
rquickjs = { version = "0.9.0", path = "./", default-features = false }

[dependencies]
indexmap-rs = { package = "indexmap", version = "2", optional = true }
@@ -36,20 +36,23 @@ rquickjs-macro = { workspace = true, optional = true }


[features]
default = []
default = ["std"]

# Almost all features excluding "parallel" and support for async runtimes
full = ["chrono", "loader", "dyn-load", "either", "indexmap", "macro", "phf"]
full = ["std", "chrono", "loader", "dyn-load", "either", "indexmap", "macro", "phf"]

# A version of full designed for wasm32-wasip1 and wasm32-wasip2 (simply excludes dyn-load)
full-wasi = ["chrono", "loader", "either", "indexmap", "macro", "phf"]
full-wasi = ["std", "chrono", "loader", "either", "indexmap", "macro", "phf"]

# Almost all features excluding "parallel"
full-async = ["full", "futures"]

# A version of full-async designed for wasm32-wasip1 and wasm32-wasip2
full-async-wasi = ["full-wasi", "futures"]

# Enable use of the rust standard library
std = ["rquickjs-core/std"]

# Chrono support.
chrono = ["rquickjs-core/chrono"]

17 changes: 11 additions & 6 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
@@ -15,14 +15,14 @@ features = ["full-async", "doc-cfg"]

[dependencies]
rquickjs-sys = { workspace = true }
hashbrown = { version = "0.15" }
phf = { version = "0.11", optional = true }
indexmap = { version = "2", optional = true }
either = { version = "1", optional = true }
async-lock = { version = "3", optional = true }
async-lock = { version = "3", optional = true, default-features = false }
chrono = { version = "0.4", optional = true }
dlopen = { version = "0.1", optional = true }
relative-path = { version = "1.9", optional = true }

relative-path = { version = "2.0", optional = true, default-features = false, features = ["alloc"] }

[dev-dependencies]
futures-rs = { package = "futures", version = "0.3" }
@@ -37,20 +37,25 @@ approx = "0.5"
trybuild = "1.0.23"

[features]
default = []
default = ["std"]

std = ["relative-path?/std"]

# Almost all features excluding "parallel" and support for async runtimes
full = ["chrono", "loader", "dyn-load", "either", "indexmap"]
full = ["std", "chrono", "loader", "dyn-load", "either", "indexmap"]

# Almost all features excluding "parallel"
full-async = ["full", "futures"]

# Enable conversion of chrono types to/from JS
chrono = ["dep:chrono"]

# Use bindgen to generate bindings at compile-type
# otherwise bundled bindings will be used
bindgen = ["rquickjs-sys/bindgen"]

# Enable support of parallel execution
parallel = ["tokio/rt-multi-thread"]
parallel = ["std", "tokio/rt-multi-thread"]

# Enable user-defined module loader support
loader = ["relative-path"]
1 change: 1 addition & 0 deletions core/src/allocator.rs
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@ use crate::qjs;

mod rust;

use alloc::boxed::Box;
pub use rust::RustAllocator;

/// The allocator interface
6 changes: 2 additions & 4 deletions core/src/allocator/rust.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
use std::{
alloc::{self, Layout},
mem, ptr,
};
use alloc::alloc;
use core::{alloc::Layout, mem, ptr};

use super::Allocator;

9 changes: 5 additions & 4 deletions core/src/class.rs
Original file line number Diff line number Diff line change
@@ -6,7 +6,8 @@ use crate::{
value::Constructor,
Ctx, Error, FromJs, IntoJs, JsLifetime, Object, Result, Value,
};
use std::{hash::Hash, marker::PhantomData, mem, ops::Deref, ptr::NonNull};
use alloc::boxed::Box;
use core::{hash::Hash, marker::PhantomData, mem, ops::Deref, ptr::NonNull};

mod cell;
mod trace;
@@ -69,7 +70,7 @@ impl<'js, C: JsClass<'js>> PartialEq for Class<'js, C> {
impl<'js, C: JsClass<'js>> Eq for Class<'js, C> {}

impl<'js, C: JsClass<'js>> Hash for Class<'js, C> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
fn hash<H: core::hash::Hasher>(&self, state: &mut H) {
self.0.hash(state)
}
}
@@ -301,15 +302,15 @@ impl<'js> Object<'js> {
// duplicated, which is possible when compilation with multiple code-gen units.
//
// Doing check avoids a lookup and an dynamic function call in some cases.
if std::ptr::eq(v_table, VTable::get::<C>()) {
if core::ptr::eq(v_table, VTable::get::<C>()) {
return true;
}

v_table.is_of_class::<C>()
}

/// Turn the object into the class if it is an instance of that class.
pub fn into_class<C: JsClass<'js>>(&self) -> std::result::Result<Class<'js, C>, &Self> {
pub fn into_class<C: JsClass<'js>>(&self) -> core::result::Result<Class<'js, C>, &Self> {
if self.instance_of::<C>() {
Ok(Class(self.clone(), PhantomData))
} else {
6 changes: 3 additions & 3 deletions core/src/class/cell.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::{Class, JsClass};
use crate::{result::BorrowError, Ctx, Error, FromJs, IntoJs, Value};
use std::{
use core::{
cell::{Cell, UnsafeCell},
marker::PhantomData,
mem::ManuallyDrop,
@@ -318,7 +318,7 @@ impl<'js, T: JsClass<'js> + 'js> OwnedBorrow<'js, T> {
pub fn into_inner(mut self) -> Class<'js, T> {
unsafe { <T::Mutable as Mutability>::unborrow(&self.0.get_cell().cell) };
let res = unsafe { ManuallyDrop::take(&mut self.0) };
std::mem::forget(self);
core::mem::forget(self);
res
}
}
@@ -376,7 +376,7 @@ impl<'js, T: JsClass<'js> + 'js> OwnedBorrowMut<'js, T> {
pub fn into_inner(mut self) -> Class<'js, T> {
unsafe { <T::Mutable as Mutability>::unborrow_mut(&self.0.get_cell().cell) };
let res = unsafe { ManuallyDrop::take(&mut self.0) };
std::mem::forget(self);
core::mem::forget(self);
res
}
}
3 changes: 2 additions & 1 deletion core/src/class/ffi.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use super::{JsClass, Tracer};
use crate::{class::JsCell, function::Params, qjs, runtime::opaque::Opaque, Value};
use std::{any::TypeId, panic::AssertUnwindSafe, ptr::NonNull};
use alloc::boxed::Box;
use core::{any::TypeId, panic::AssertUnwindSafe, ptr::NonNull};

/// FFI finalizer, destroying the object once it is delete by the Gc.
pub(crate) unsafe extern "C" fn class_finalizer(rt: *mut qjs::JSRuntime, val: qjs::JSValue) {
2 changes: 1 addition & 1 deletion core/src/class/impl_.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
//! Helper classes and functions for use inside the macros.

use crate::{value::Constructor, Ctx, Object, Result};
use std::marker::PhantomData;
use core::marker::PhantomData;

/// Trait used for borrow specialization for implementing methods without access to the class.
pub trait MethodImplementor<T>: Sized {
22 changes: 12 additions & 10 deletions core/src/class/trace.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use super::JsClass;
use crate::{markers::Invariant, qjs, Class, Ctx, Module, Value};
use std::marker::PhantomData;
use core::marker::PhantomData;

#[cfg(feature = "either")]
use either::{Either, Left, Right};
@@ -209,7 +209,7 @@ trace_impls! {
i8,i16,i32,i64,isize,
f32,f64,
bool,char,
String,
alloc::string::String,
crate::Atom<'js>,
}

@@ -226,9 +226,9 @@ trace_impls! {

trace_impls! {
ref:
Box,
std::rc::Rc,
std::sync::Arc,
alloc::boxed::Box,
alloc::rc::Rc,
alloc::sync::Arc,
}

trace_impls! {
@@ -254,20 +254,22 @@ trace_impls! {

trace_impls! {
list:
Vec,
std::collections::VecDeque,
std::collections::LinkedList,
alloc::vec::Vec,
alloc::collections::VecDeque,
alloc::collections::LinkedList,
#[cfg(feature = "std")]
std::collections::HashSet {S},
std::collections::BTreeSet,
alloc::collections::BTreeSet,
#[cfg(feature = "indexmap")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(all(feature = "indexmap"))))]
indexmap::IndexSet {S},
}

trace_impls! {
map:
#[cfg(feature = "std")]
std::collections::HashMap {S},
std::collections::BTreeMap,
alloc::collections::BTreeMap,
#[cfg(feature = "indexmap")]
#[cfg_attr(feature = "doc-cfg", doc(cfg(all(feature = "indexmap"))))]
indexmap::IndexMap {S},
10 changes: 6 additions & 4 deletions core/src/context/async.rs
Original file line number Diff line number Diff line change
@@ -4,7 +4,8 @@ use super::{
ContextBuilder, Intrinsic,
};
use crate::{markers::ParallelSend, qjs, runtime::AsyncRuntime, Ctx, Error, Result};
use std::{future::Future, mem, pin::Pin, ptr::NonNull};
use alloc::boxed::Box;
use core::{future::Future, mem, pin::Pin, ptr::NonNull};

mod future;

@@ -66,7 +67,7 @@ use future::WithFuture;
macro_rules! async_with{
($context:expr => |$ctx:ident| { $($t:tt)* }) => {
$crate::AsyncContext::async_with(&$context,|$ctx| {
let fut = Box::pin(async move {
let fut = $crate::alloc::boxed::Box::pin(async move {
$($t)*
});
/// SAFETY: While rquickjs objects have a 'js lifetime attached to them,
@@ -80,8 +81,8 @@ macro_rules! async_with{
/// rquickjs objects are send so the future will never be send.
/// Since we acquire a lock before running the future and nothing can escape the closure
/// and future it is safe to recast the future as send.
unsafe fn uplift<'a,'b,R>(f: std::pin::Pin<Box<dyn std::future::Future<Output = R> + 'a>>) -> std::pin::Pin<Box<dyn std::future::Future<Output = R> + 'b + Send>>{
std::mem::transmute(f)
unsafe fn uplift<'a,'b,R>(f: core::pin::Pin<$crate::alloc::boxed::Box<dyn core::future::Future<Output = R> + 'a>>) -> core::pin::Pin<$crate::alloc::boxed::Box<dyn core::future::Future<Output = R> + 'b + Send>>{
core::mem::transmute(f)
}
unsafe{ uplift(fut) }
})
@@ -103,6 +104,7 @@ impl DropContext for AsyncRuntime {
// We should still free the context.
// TODO see if there is a way to recover from a panic which could cause the
// following assertion to trigger
#[cfg(feature = "std")]
assert!(std::thread::panicking());
}
unsafe { qjs::JS_FreeContext(ctx.as_ptr()) }
3 changes: 2 additions & 1 deletion core/src/context/async/future.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use std::{
use alloc::boxed::Box;
use core::{
future::Future,
mem::{self, ManuallyDrop},
pin::Pin,
3 changes: 2 additions & 1 deletion core/src/context/base.rs
Original file line number Diff line number Diff line change
@@ -5,7 +5,7 @@ use super::{
ContextBuilder, Intrinsic,
};
use crate::{qjs, Ctx, Error, Result, Runtime};
use std::{mem, ptr::NonNull};
use core::{mem, ptr::NonNull};

impl DropContext for Runtime {
unsafe fn drop_context(&self, ctx: NonNull<qjs::JSContext>) {
@@ -19,6 +19,7 @@ impl DropContext for Runtime {
// We should still free the context.
// TODO see if there is a way to recover from a panic which could cause the
// following assertion to trigger
#[cfg(feature = "std")]
assert!(std::thread::panicking());
}
unsafe { qjs::JS_FreeContext(ctx.as_ptr()) }
2 changes: 1 addition & 1 deletion core/src/context/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::{marker::PhantomData, ptr::NonNull};
use core::{marker::PhantomData, ptr::NonNull};

#[cfg(feature = "futures")]
use crate::{context::AsyncContext, runtime::AsyncRuntime};
17 changes: 11 additions & 6 deletions core/src/context/ctx.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
#[cfg(feature = "futures")]
use std::future::Future;
use std::{
use core::future::Future;

use alloc::{boxed::Box, ffi::CString, vec::Vec};
use core::{
any::Any,
ffi::{CStr, CString},
fs,
ffi::CStr,
mem::{self, MaybeUninit},
path::Path,
ptr::NonNull,
result::Result as StdResult,
};

#[cfg(feature = "std")]
use std::{fs, path::Path};

#[cfg(feature = "futures")]
use crate::AsyncContext;
use crate::{
@@ -182,11 +185,13 @@ impl<'js> Ctx<'js> {
})
}

#[cfg(feature = "std")]
/// Evaluate a script directly from a file.
pub fn eval_file<V: FromJs<'js>, P: AsRef<Path>>(&self, path: P) -> Result<V> {
self.eval_file_with_options(path, Default::default())
}

#[cfg(feature = "std")]
pub fn eval_file_with_options<V: FromJs<'js>, P: AsRef<Path>>(
&self,
path: P,
@@ -424,7 +429,7 @@ impl<'js> Ctx<'js> {
/// name.
/// Otherwise it will return none.
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 stack_level = core::ffi::c_int::try_from(stack_level).unwrap();
let atom = unsafe { qjs::JS_GetScriptOrModuleName(self.as_ptr(), stack_level) };
#[allow(clippy::useless_conversion)] //needed for multi platform binding support
if qjs::__JS_ATOM_NULL == atom.try_into().unwrap() {
2 changes: 1 addition & 1 deletion core/src/context/multi_with_impl.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(clippy::many_single_char_names)]
use super::{Context, Ctx, MultiWith};
use std::mem;
use core::mem;

macro_rules! list {
({$($r:ident,)+} => $e:ty) => {
4 changes: 2 additions & 2 deletions core/src/context/owner.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::ptr::NonNull;
use core::ptr::NonNull;

use crate::qjs;

@@ -20,7 +20,7 @@ pub(crate) struct ContextOwner<R: DropContext> {
ctx: NonNull<qjs::JSContext>,
#[cfg(feature = "parallel")]
pub(crate) ctx: Arc<NonNull<qjs::JSContext>>,
rt: R,
pub(crate) rt: R,
}

impl<R: DropContext> ContextOwner<R> {
Loading