diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 7f14299a..4d9c4da5 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -372,6 +372,7 @@ jobs:
run: |
cargo clean
cargo build ${{ matrix.optimization && '--release' || '' }} --manifest-path sys/Cargo.toml --target ${{ matrix.target }} --features bindgen,update-bindings,logging
+ cat sys/src/bindings/${{ matrix.target }}.rs
- name: Upload bindings
if: matrix.task == 'bindings'
uses: actions/upload-artifact@v4
@@ -381,7 +382,10 @@ jobs:
overwrite: true
- name: Build
if: ${{ !matrix.no-build }}
- run: cargo build ${{ matrix.optimization && '--release' || '' }} --target ${{ matrix.target }} --no-default-features --features ${{ matrix.features }}
+ env:
+ BUILD_TARGET: ${{ matrix.target }}
+ run: |
+ cargo build ${{ matrix.optimization && '--release' || '' }} --target ${{ matrix.target }} --no-default-features --features ${{ matrix.features }}
- name: Test
if: ${{ !matrix.no-build && !matrix.no-test }}
timeout-minutes: 12
diff --git a/.gitmodules b/.gitmodules
index 56af563a..89f7ad5c 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,3 @@
[submodule "rquickjs-sys/quickjs"]
path = sys/quickjs
- url = https://github.com/bellard/quickjs.git
+ url = https://github.com/quickjs-ng/quickjs.git
diff --git a/core/src/allocator.rs b/core/src/allocator.rs
index d4b2063a..f4d9f43d 100644
--- a/core/src/allocator.rs
+++ b/core/src/allocator.rs
@@ -1,7 +1,6 @@
//! Tools for using different allocators with QuickJS.
use crate::qjs;
-use std::ptr;
mod rust;
@@ -24,6 +23,11 @@ pub unsafe trait Allocator {
///
fn alloc(&mut self, size: usize) -> *mut u8;
+ /// Allocates memory for an array of num objects of size and initializes all bytes in the allocated storage to zero.
+ ///
+ ///
+ fn calloc(&mut self, count: usize, size: usize) -> *mut u8;
+
/// De-allocate previously allocated memory
///
/// # Safety
@@ -65,6 +69,7 @@ impl AllocatorHolder {
A: Allocator,
{
qjs::JSMallocFunctions {
+ js_calloc: Some(Self::calloc::),
js_malloc: Some(Self::malloc::),
js_free: Some(Self::free::),
js_realloc: Some(Self::realloc::),
@@ -83,47 +88,30 @@ impl AllocatorHolder {
self.0
}
- fn size_t(size: usize) -> qjs::size_t {
- size.try_into().expect(qjs::SIZE_T_ERROR)
- }
-
- unsafe extern "C" fn malloc(
- state: *mut qjs::JSMallocState,
+ unsafe extern "C" fn calloc(
+ opaque: *mut qjs::c_void,
+ count: qjs::size_t,
size: qjs::size_t,
) -> *mut qjs::c_void
where
A: Allocator,
{
- if size == 0 {
- return ptr::null_mut();
- }
-
- let state = &mut *state;
-
- if state.malloc_size + size > state.malloc_limit {
- return ptr::null_mut();
- }
-
+ let allocator = &mut *(opaque as *mut DynAllocator);
let rust_size: usize = size.try_into().expect(qjs::SIZE_T_ERROR);
- // simulate the default behavior of libc::malloc
-
- let allocator = &mut *(state.opaque as *mut DynAllocator);
-
- let res = allocator.alloc(rust_size as _);
-
- if res.is_null() {
- return ptr::null_mut();
- }
-
- let size = A::usable_size(res);
-
- state.malloc_count += 1;
- state.malloc_size += Self::size_t(size);
+ let rust_count: usize = count.try_into().expect(qjs::SIZE_T_ERROR);
+ allocator.calloc(rust_count, rust_size) as *mut qjs::c_void
+ }
- res as *mut qjs::c_void
+ unsafe extern "C" fn malloc(opaque: *mut qjs::c_void, size: qjs::size_t) -> *mut qjs::c_void
+ where
+ A: Allocator,
+ {
+ let allocator = &mut *(opaque as *mut DynAllocator);
+ let rust_size: usize = size.try_into().expect(qjs::SIZE_T_ERROR);
+ allocator.alloc(rust_size) as *mut qjs::c_void
}
- unsafe extern "C" fn free(state: *mut qjs::JSMallocState, ptr: *mut qjs::c_void)
+ unsafe extern "C" fn free(opaque: *mut qjs::c_void, ptr: *mut qjs::c_void)
where
A: Allocator,
{
@@ -133,56 +121,21 @@ impl AllocatorHolder {
return;
}
- let state = &mut *state;
- state.malloc_count -= 1;
-
- let size = A::usable_size(ptr as *mut u8);
-
- let allocator = &mut *(state.opaque as *mut DynAllocator);
+ let allocator = &mut *(opaque as *mut DynAllocator);
allocator.dealloc(ptr as _);
-
- state.malloc_size -= Self::size_t(size);
}
unsafe extern "C" fn realloc(
- state: *mut qjs::JSMallocState,
+ opaque: *mut qjs::c_void,
ptr: *mut qjs::c_void,
size: qjs::size_t,
) -> *mut qjs::c_void
where
A: Allocator,
{
- let state_ref = &mut *state;
- let allocator = &mut *(state_ref.opaque as *mut DynAllocator);
-
- // simulate the default behavior of libc::realloc
- if ptr.is_null() {
- return Self::malloc::(state, size);
- } else if size == 0 {
- Self::free::(state, ptr);
- return ptr::null_mut();
- }
-
- let old_size = Self::size_t(A::usable_size(ptr as *mut u8));
-
- let new_malloc_size = state_ref.malloc_size - old_size + size;
- if new_malloc_size > state_ref.malloc_limit {
- return ptr::null_mut();
- }
-
- let ptr = allocator.realloc(ptr as _, size.try_into().expect(qjs::SIZE_T_ERROR))
- as *mut qjs::c_void;
-
- if ptr.is_null() {
- return ptr::null_mut();
- }
-
- let actual_size = Self::size_t(A::usable_size(ptr as *mut u8));
-
- state_ref.malloc_size -= old_size;
- state_ref.malloc_size += actual_size;
-
- ptr
+ let rust_size: usize = size.try_into().expect(qjs::SIZE_T_ERROR);
+ let allocator = &mut *(opaque as *mut DynAllocator);
+ allocator.realloc(ptr as _, rust_size) as *mut qjs::c_void
}
unsafe extern "C" fn malloc_usable_size(ptr: *const qjs::c_void) -> qjs::size_t
diff --git a/core/src/allocator/rust.rs b/core/src/allocator/rust.rs
index 21967b63..7158dc78 100644
--- a/core/src/allocator/rust.rs
+++ b/core/src/allocator/rust.rs
@@ -35,6 +35,37 @@ fn round_size(size: usize) -> usize {
pub struct RustAllocator;
unsafe impl Allocator for RustAllocator {
+ fn calloc(&mut self, count: usize, size: usize) -> *mut u8 {
+ if count == 0 || size == 0 {
+ return ptr::null_mut();
+ }
+
+ let total_size = count.checked_mul(size).expect("overflow");
+
+ let total_size = round_size(total_size);
+
+ // Calculate the total allocated size including header
+ let alloc_size = HEADER_SIZE + total_size;
+
+ let layout = if let Ok(layout) = Layout::from_size_align(alloc_size, ALLOC_ALIGN) {
+ layout
+ } else {
+ return ptr::null_mut();
+ };
+
+ let ptr = unsafe { alloc::alloc_zeroed(layout) };
+
+ if ptr.is_null() {
+ return ptr::null_mut();
+ }
+
+ let header = unsafe { &mut *(ptr as *mut Header) };
+ header.size = total_size;
+
+ let ptr = unsafe { ptr.add(HEADER_SIZE) };
+ ptr
+ }
+
fn alloc(&mut self, size: usize) -> *mut u8 {
let size = round_size(size);
let alloc_size = size + HEADER_SIZE;
@@ -110,6 +141,14 @@ mod test {
}
}
+ fn calloc(&mut self, count: usize, size: usize) -> *mut u8 {
+ unsafe {
+ let res = RustAllocator.calloc(count, size);
+ ALLOC_SIZE.fetch_add(RustAllocator::usable_size(res), Ordering::AcqRel);
+ res
+ }
+ }
+
unsafe fn dealloc(&mut self, ptr: *mut u8) {
ALLOC_SIZE.fetch_sub(RustAllocator::usable_size(ptr), Ordering::AcqRel);
RustAllocator.dealloc(ptr);
diff --git a/core/src/class.rs b/core/src/class.rs
index 6e6e36e3..7165e01a 100644
--- a/core/src/class.rs
+++ b/core/src/class.rs
@@ -145,7 +145,7 @@ impl<'js, C: JsClass<'js>> Class<'js, C> {
///
/// Returns `None` if the class is not yet registered or if the class doesn't have a prototype.
pub fn prototype(ctx: &Ctx<'js>) -> Result